From a08526c92c54268bbba0e39e0f0761f6243c01db Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 17 Sep 2023 08:38:11 -0700 Subject: [PATCH] language: implement list indexing --- ast/src/main/ast/pork.yml | 7 +++++ ast/src/main/graph/types.dot | 3 ++ .../kotlin/gay/pizza/pork/ast/IndexedBy.kt | 29 +++++++++++++++++++ .../gay/pizza/pork/ast/NodeCoalescer.kt | 3 ++ .../kotlin/gay/pizza/pork/ast/NodeParser.kt | 2 ++ .../pizza/pork/ast/NodeParserExtensions.kt | 1 + .../kotlin/gay/pizza/pork/ast/NodeType.kt | 1 + .../kotlin/gay/pizza/pork/ast/NodeVisitor.kt | 2 ++ .../pizza/pork/ast/NodeVisitorExtensions.kt | 1 + .../pizza/pork/evaluator/EvaluationVisitor.kt | 15 ++++++++++ .../kotlin/gay/pizza/pork/parser/Parser.kt | 12 ++++++++ .../kotlin/gay/pizza/pork/parser/Printer.kt | 7 +++++ .../pork/idea/psi/gen/IndexedByElement.kt | 6 ++++ .../pork/idea/psi/gen/PorkElementFactory.kt | 1 + 14 files changed, 90 insertions(+) create mode 100644 ast/src/main/kotlin/gay/pizza/pork/ast/IndexedBy.kt create mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/IndexedByElement.kt diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index 58a8e7b..74c6bcc 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -292,3 +292,10 @@ types: type: Symbol - name: definition type: StringLiteral + IndexedBy: + parent: Expression + values: + - name: expression + type: Expression + - name: index + type: Expression diff --git a/ast/src/main/graph/types.dot b/ast/src/main/graph/types.dot index 5687cb7..ee65a08 100644 --- a/ast/src/main/graph/types.dot +++ b/ast/src/main/graph/types.dot @@ -36,6 +36,7 @@ digraph A { type_Continue [shape=box,label="Continue"] type_NoneLiteral [shape=box,label="NoneLiteral"] type_Native [shape=box,label="Native"] + type_IndexedBy [shape=box,label="IndexedBy"] type_Node -> type_Expression type_Node -> type_Symbol type_Node -> type_Declaration @@ -64,6 +65,7 @@ digraph A { type_Expression -> type_Break type_Expression -> type_Continue type_Expression -> type_NoneLiteral + type_Expression -> type_IndexedBy type_Definition -> type_FunctionDefinition type_Definition -> type_LetDefinition type_Declaration -> type_ImportDeclaration @@ -108,4 +110,5 @@ digraph A { type_ForIn -> type_Block [style=dotted] type_Native -> type_Symbol [style=dotted] type_Native -> type_StringLiteral [style=dotted] + type_IndexedBy -> type_Expression [style=dotted] } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/IndexedBy.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/IndexedBy.kt new file mode 100644 index 0000000..3867fcd --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/IndexedBy.kt @@ -0,0 +1,29 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.ast + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("indexedBy") +class IndexedBy(val expression: Expression, val index: Expression) : Expression() { + override val type: NodeType = NodeType.IndexedBy + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitNodes(expression, index) + + override fun visit(visitor: NodeVisitor): T = + visitor.visitIndexedBy(this) + + override fun equals(other: Any?): Boolean { + if (other !is IndexedBy) return false + return other.expression == expression && other.index == index + } + + override fun hashCode(): Int { + var result = expression.hashCode() + result = 31 * result + index.hashCode() + result = 31 * result + type.hashCode() + return result + } +} diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt index 7c0d9aa..925f032 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -35,6 +35,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { override fun visitImportDeclaration(node: ImportDeclaration): Unit = handle(node) + override fun visitIndexedBy(node: IndexedBy): Unit = + handle(node) + override fun visitInfixOperation(node: InfixOperation): Unit = handle(node) diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt index b6816a6..555ba29 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt @@ -30,6 +30,8 @@ interface NodeParser { fun parseImportDeclaration(): ImportDeclaration + fun parseIndexedBy(): IndexedBy + fun parseInfixOperation(): InfixOperation fun parseIntegerLiteral(): IntegerLiteral diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt index e2e5eb1..a159120 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt @@ -34,5 +34,6 @@ fun NodeParser.parse(type: NodeType): Node = NodeType.Continue -> parseContinue() NodeType.NoneLiteral -> parseNoneLiteral() NodeType.Native -> parseNative() + NodeType.IndexedBy -> parseIndexedBy() else -> throw RuntimeException("Unable to automatically parse type: ${type.name}") } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt index ba5497f..43442ea 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -17,6 +17,7 @@ enum class NodeType(val parent: NodeType? = null) { FunctionDefinition(Definition), If(Expression), ImportDeclaration(Declaration), + IndexedBy(Expression), InfixOperation(Expression), IntegerLiteral(Expression), LetAssignment(Expression), diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt index 0298337..25a4cbb 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -24,6 +24,8 @@ interface NodeVisitor { fun visitImportDeclaration(node: ImportDeclaration): T + fun visitIndexedBy(node: IndexedBy): T + fun visitInfixOperation(node: InfixOperation): T fun visitIntegerLiteral(node: IntegerLiteral): T diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt index 295f2cd..976ede6 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt @@ -31,6 +31,7 @@ fun NodeVisitor.visit(node: Node): T = is Continue -> visitContinue(node) is NoneLiteral -> visitNoneLiteral(node) is Native -> visitNative(node) + is IndexedBy -> visitIndexedBy(node) } fun NodeVisitor.visitNodes(vararg nodes: Node?): List = diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index 466f8db..f93c2c4 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -366,6 +366,21 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { topLevelUsedError("ImportDeclaration", "CompilationUnitContext") } + override fun visitIndexedBy(node: IndexedBy): Any { + val value = node.expression.visit(this) + val index = node.index.visit(this) + + if (value is List<*> && index is Number) { + return value[index.toInt()] ?: None + } + + if (value is Array<*> && index is Number) { + return value[index.toInt()] ?: None + } + + throw RuntimeException("Failed to index '${value}' by '${index}': Unsupported types used.") + } + override fun visitCompilationUnit(node: CompilationUnit): Any { topLevelUsedError("CompilationUnit", "CompilationUnitContext") } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index f943f42..ce0cd6f 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -78,6 +78,10 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : val infixOperator = ParserHelpers.convertInfixOperator(infixToken) InfixOperation(expression, infixOperator, parseExpression()) } + } else if (next(TokenType.LeftBracket)) { + val index = parseExpression() + expect(TokenType.RightBracket) + IndexedBy(expression, index) } else expression } @@ -237,6 +241,14 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : ImportDeclaration(form, components) } + override fun parseIndexedBy(): IndexedBy = guarded(NodeType.IndexedBy) { + val expression = parseExpression() + expect(TokenType.LeftBracket) + val index = parseExpression() + expect(TokenType.RightBracket) + IndexedBy(expression, index) + } + override fun parseInfixOperation(): InfixOperation = guarded(NodeType.InfixOperation) { val infixToken = next() val infixOperator = ParserHelpers.convertInfixOperator(infixToken) diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt index 400f7b3..5b78985 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt @@ -233,6 +233,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor { } } + override fun visitIndexedBy(node: IndexedBy) { + visit(node.expression) + append("[") + visit(node.index) + append("]") + } + override fun visitCompilationUnit(node: CompilationUnit) { for (declaration in node.declarations) { visit(declaration) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/IndexedByElement.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/IndexedByElement.kt new file mode 100644 index 0000000..043e537 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/IndexedByElement.kt @@ -0,0 +1,6 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.idea.psi.gen + +import com.intellij.lang.ASTNode + +class IndexedByElement(node: ASTNode) : PorkElement(node) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt index 6340085..ce088e8 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt @@ -38,6 +38,7 @@ object PorkElementFactory { NodeType.Continue -> ContinueElement(node) NodeType.NoneLiteral -> NoneLiteralElement(node) NodeType.Native -> NativeElement(node) + NodeType.IndexedBy -> IndexedByElement(node) else -> ASTWrapperPsiElement(node) } }