language: var, reassign, comparison operators

This commit is contained in:
Alex Zenla 2023-09-10 04:34:50 -04:00
parent f433ba2776
commit 71999032ac
Signed by: alex
GPG Key ID: C0780728420EBFE5
20 changed files with 262 additions and 26 deletions

View File

@ -41,6 +41,20 @@ types:
type: Symbol type: Symbol
- name: value - name: value
type: Expression type: Expression
VarAssignment:
parent: Expression
values:
- name: symbol
type: Symbol
- name: value
type: Expression
SetAssignment:
parent: Expression
values:
- name: symbol
type: Symbol
- name: value
type: Expression
InfixOperator: InfixOperator:
values: values:
- name: token - name: token
@ -70,6 +84,18 @@ types:
- name: Remainder - name: Remainder
values: values:
token: "rem" token: "rem"
- name: Lesser
values:
token: "<"
- name: Greater
values:
token: ">"
- name: GreaterEqual
values:
token: ">="
- name: LesserEqual
values:
token: "<="
InfixOperation: InfixOperation:
parent: Expression parent: Expression
values: values:

View File

@ -8,6 +8,8 @@ digraph A {
type_Block [shape=box,label="Block"] type_Block [shape=box,label="Block"]
type_CompilationUnit [shape=box,label="CompilationUnit"] type_CompilationUnit [shape=box,label="CompilationUnit"]
type_LetAssignment [shape=box,label="LetAssignment"] type_LetAssignment [shape=box,label="LetAssignment"]
type_VarAssignment [shape=box,label="VarAssignment"]
type_SetAssignment [shape=box,label="SetAssignment"]
type_InfixOperator [shape=box,label="InfixOperator"] type_InfixOperator [shape=box,label="InfixOperator"]
type_InfixOperation [shape=box,label="InfixOperation"] type_InfixOperation [shape=box,label="InfixOperation"]
type_BooleanLiteral [shape=box,label="BooleanLiteral"] type_BooleanLiteral [shape=box,label="BooleanLiteral"]
@ -35,6 +37,8 @@ digraph A {
type_Node -> type_CompilationUnit type_Node -> type_CompilationUnit
type_Node -> type_Native type_Node -> type_Native
type_Expression -> type_LetAssignment type_Expression -> type_LetAssignment
type_Expression -> type_VarAssignment
type_Expression -> type_SetAssignment
type_Expression -> type_InfixOperation type_Expression -> type_InfixOperation
type_Expression -> type_BooleanLiteral type_Expression -> type_BooleanLiteral
type_Expression -> type_FunctionCall type_Expression -> type_FunctionCall
@ -58,6 +62,10 @@ digraph A {
type_CompilationUnit -> type_Definition [style=dotted] type_CompilationUnit -> type_Definition [style=dotted]
type_LetAssignment -> type_Symbol [style=dotted] type_LetAssignment -> type_Symbol [style=dotted]
type_LetAssignment -> type_Expression [style=dotted] type_LetAssignment -> type_Expression [style=dotted]
type_VarAssignment -> type_Symbol [style=dotted]
type_VarAssignment -> type_Expression [style=dotted]
type_SetAssignment -> type_Symbol [style=dotted]
type_SetAssignment -> type_Expression [style=dotted]
type_InfixOperation -> type_Expression [style=dotted] type_InfixOperation -> type_Expression [style=dotted]
type_InfixOperation -> type_InfixOperator [style=dotted] type_InfixOperation -> type_InfixOperator [style=dotted]
type_FunctionCall -> type_Symbol [style=dotted] type_FunctionCall -> type_Symbol [style=dotted]

View File

@ -14,5 +14,9 @@ enum class InfixOperator(val token: String) {
Equals("=="), Equals("=="),
NotEquals("!="), NotEquals("!="),
EuclideanModulo("mod"), EuclideanModulo("mod"),
Remainder("rem") Remainder("rem"),
Lesser("<"),
Greater(">"),
GreaterEqual(">="),
LesserEqual("<=")
} }

View File

@ -53,6 +53,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
override fun visitPrefixOperation(node: PrefixOperation): Unit = override fun visitPrefixOperation(node: PrefixOperation): Unit =
handle(node) handle(node)
override fun visitSetAssignment(node: SetAssignment): Unit =
handle(node)
override fun visitStringLiteral(node: StringLiteral): Unit = override fun visitStringLiteral(node: StringLiteral): Unit =
handle(node) handle(node)
@ -62,6 +65,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
override fun visitSymbolReference(node: SymbolReference): Unit = override fun visitSymbolReference(node: SymbolReference): Unit =
handle(node) handle(node)
override fun visitVarAssignment(node: VarAssignment): Unit =
handle(node)
override fun visitWhile(node: While): Unit = override fun visitWhile(node: While): Unit =
handle(node) handle(node)

View File

@ -23,8 +23,10 @@ enum class NodeType(val parent: NodeType? = null) {
Native(Node), Native(Node),
Parentheses(Expression), Parentheses(Expression),
PrefixOperation(Expression), PrefixOperation(Expression),
SetAssignment(Expression),
StringLiteral(Expression), StringLiteral(Expression),
Symbol(Node), Symbol(Node),
SymbolReference(Expression), SymbolReference(Expression),
VarAssignment(Expression),
While(Expression) While(Expression)
} }

View File

@ -36,11 +36,15 @@ interface NodeVisitor<T> {
fun visitPrefixOperation(node: PrefixOperation): T fun visitPrefixOperation(node: PrefixOperation): T
fun visitSetAssignment(node: SetAssignment): T
fun visitStringLiteral(node: StringLiteral): T fun visitStringLiteral(node: StringLiteral): T
fun visitSymbol(node: Symbol): T fun visitSymbol(node: Symbol): T
fun visitSymbolReference(node: SymbolReference): T fun visitSymbolReference(node: SymbolReference): T
fun visitVarAssignment(node: VarAssignment): T
fun visitWhile(node: While): T fun visitWhile(node: While): T
} }

View File

@ -7,6 +7,8 @@ fun <T> NodeVisitor<T>.visit(node: Node): T =
is Block -> visitBlock(node) is Block -> visitBlock(node)
is CompilationUnit -> visitCompilationUnit(node) is CompilationUnit -> visitCompilationUnit(node)
is LetAssignment -> visitLetAssignment(node) is LetAssignment -> visitLetAssignment(node)
is VarAssignment -> visitVarAssignment(node)
is SetAssignment -> visitSetAssignment(node)
is InfixOperation -> visitInfixOperation(node) is InfixOperation -> visitInfixOperation(node)
is BooleanLiteral -> visitBooleanLiteral(node) is BooleanLiteral -> visitBooleanLiteral(node)
is FunctionCall -> visitFunctionCall(node) is FunctionCall -> visitFunctionCall(node)

View File

@ -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("setAssignment")
class SetAssignment(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.SetAssignment
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol, value)
override fun <T> visit(visitor: NodeVisitor<T>): T =
visitor.visitSetAssignment(this)
override fun equals(other: Any?): Boolean {
if (other !is SetAssignment) return false
return other.symbol == symbol && other.value == value
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + value.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -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("varAssignment")
class VarAssignment(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.VarAssignment
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol, value)
override fun <T> visit(visitor: NodeVisitor<T>): T =
visitor.visitVarAssignment(this)
override fun equals(other: Any?): Boolean {
if (other !is VarAssignment) return false
return other.symbol == symbol && other.value == value
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + value.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -26,13 +26,19 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitLetAssignment(node: LetAssignment): Any { override fun visitLetAssignment(node: LetAssignment): Any {
val value = node.value.visit(this) val value = node.value.visit(this)
currentScope.define(node.symbol.id, value) currentScope.define(node.symbol.id, value, ValueStoreType.Let)
return value return value
} }
override fun visitSymbolReference(node: SymbolReference): Any = override fun visitSymbolReference(node: SymbolReference): Any =
currentScope.value(node.symbol.id) currentScope.value(node.symbol.id)
override fun visitVarAssignment(node: VarAssignment): Any {
val value = node.value.visit(this)
currentScope.define(node.symbol.id, value, type = ValueStoreType.Var)
return value
}
override fun visitWhile(node: While): Any { override fun visitWhile(node: While): Any {
val blockFunction = node.block.visit(this) as BlockFunction val blockFunction = node.block.visit(this) as BlockFunction
var result: Any? = null var result: Any? = null
@ -68,6 +74,12 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
} }
} }
override fun visitSetAssignment(node: SetAssignment): Any {
val value = node.value.visit(this)
currentScope.set(node.symbol.id, value)
return value
}
override fun visitIf(node: If): Any { override fun visitIf(node: If): Any {
val condition = node.condition.visit(this) val condition = node.condition.visit(this)
return if (condition == true) { return if (condition == true) {
@ -108,7 +120,11 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
multiply = { a, b -> a * b }, multiply = { a, b -> a * b },
divide = { a, b -> a / b }, divide = { a, b -> a / b },
euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") }, euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") },
remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") } remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
greaterEqual = { a, b -> a >= b }
) )
} }
@ -123,7 +139,11 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
multiply = { a, b -> a * b }, multiply = { a, b -> a * b },
divide = { a, b -> a / b }, divide = { a, b -> a / b },
euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") }, euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") },
remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") } remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
greaterEqual = { a, b -> a >= b }
) )
} }
@ -138,7 +158,11 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
multiply = { a, b -> a * b }, multiply = { a, b -> a * b },
divide = { a, b -> a / b }, divide = { a, b -> a / b },
euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } }, euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } },
remainder = { x, d -> x % d } remainder = { x, d -> x % d },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
greaterEqual = { a, b -> a >= b }
) )
} }
@ -153,7 +177,11 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
multiply = { a, b -> a * b }, multiply = { a, b -> a * b },
divide = { a, b -> a / b }, divide = { a, b -> a / b },
euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } }, euclideanModulo = { x, d -> (x % d).let { q -> if (q < 0) q + abs(d) else q } },
remainder = { x, d -> x % d } remainder = { x, d -> x % d },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
greaterEqual = { a, b -> a >= b }
) )
} }
@ -170,8 +198,12 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
multiply: (T, T) -> T, multiply: (T, T) -> T,
divide: (T, T) -> T, divide: (T, T) -> T,
euclideanModulo: (T, T) -> T, euclideanModulo: (T, T) -> T,
remainder: (T, T) -> T remainder: (T, T) -> T,
): T { lesser: (T, T) -> Boolean,
greater: (T, T) -> Boolean,
lesserEqual: (T, T) -> Boolean,
greaterEqual: (T, T) -> Boolean
): Any {
return when (op) { return when (op) {
InfixOperator.Plus -> add(convert(left), convert(right)) InfixOperator.Plus -> add(convert(left), convert(right))
InfixOperator.Minus -> subtract(convert(left), convert(right)) InfixOperator.Minus -> subtract(convert(left), convert(right))
@ -179,6 +211,10 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
InfixOperator.Divide -> divide(convert(left), convert(right)) InfixOperator.Divide -> divide(convert(left), convert(right))
InfixOperator.EuclideanModulo -> euclideanModulo(convert(left), convert(right)) InfixOperator.EuclideanModulo -> euclideanModulo(convert(left), convert(right))
InfixOperator.Remainder -> remainder(convert(left), convert(right)) InfixOperator.Remainder -> remainder(convert(left), convert(right))
InfixOperator.Lesser -> lesser(convert(left), convert(right))
InfixOperator.Greater -> greater(convert(left), convert(right))
InfixOperator.LesserEqual -> lesserEqual(convert(left), convert(right))
InfixOperator.GreaterEqual -> greaterEqual(convert(left), convert(right))
else -> throw RuntimeException("Unable to handle operation $op") else -> throw RuntimeException("Unable to handle operation $op")
} }
} }

View File

@ -6,43 +6,55 @@ class Scope(
val name: String? = null val name: String? = null
) { ) {
private val inherited = inherits.toMutableList() private val inherited = inherits.toMutableList()
private val variables = mutableMapOf<String, Any>() private val variables = mutableMapOf<String, ValueStore>()
fun define(name: String, value: Any) { fun define(name: String, value: Any, type: ValueStoreType = ValueStoreType.Let) {
val previous = variables.put(name, value) val previous = variables.put(name, ValueStore(value, type))
if (previous != null) { if (previous != null) {
variables[name] = previous variables[name] = previous
throw RuntimeException("Variable '${name}' is already defined") throw RuntimeException("Variable '${name}' is already defined")
} }
} }
fun value(name: String): Any { fun set(name: String, value: Any) {
val value = valueOrNotFound(name) val holder = valueHolderOrNotFound(name)
if (value === NotFound) { if (holder.type == ValueStoreType.Let) {
throw RuntimeException("Variable '${name}' is already defined")
}
if (holder === NotFound.Holder) {
throw RuntimeException("Variable '${name}' not defined") throw RuntimeException("Variable '${name}' not defined")
} }
return value holder.value = value
} }
private fun valueOrNotFound(name: String): Any { fun value(name: String): Any {
val value = variables[name] val holder = valueHolderOrNotFound(name)
if (value == null) { if (holder === NotFound.Holder) {
throw RuntimeException("Variable '${name}' not defined")
}
return holder.value
}
private fun valueHolderOrNotFound(name: String): ValueStore {
val holder = variables[name]
if (holder == null) {
if (parent != null) { if (parent != null) {
val parentMaybeFound = parent.valueOrNotFound(name) val parentMaybeFound = parent.valueHolderOrNotFound(name)
if (parentMaybeFound !== NotFound) { if (parentMaybeFound !== NotFound.Holder) {
return parentMaybeFound return parentMaybeFound
} }
} }
for (inherit in inherited) { for (inherit in inherited) {
val inheritMaybeFound = inherit.valueOrNotFound(name) val inheritMaybeFound = inherit.valueHolderOrNotFound(name)
if (inheritMaybeFound !== NotFound) { if (inheritMaybeFound !== NotFound.Holder) {
return inheritMaybeFound return inheritMaybeFound
} }
} }
return NotFound return NotFound.Holder
} }
return value return holder
} }
fun fork(name: String? = null): Scope = fun fork(name: String? = null): Scope =
@ -80,5 +92,7 @@ class Scope(
} }
} }
private object NotFound private object NotFound {
val Holder = ValueStore(NotFound, ValueStoreType.Let)
}
} }

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.evaluator
class ValueStore(var value: Any, val type: ValueStoreType) {
override fun toString(): String = "${type.name}: $value"
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.evaluator
enum class ValueStoreType {
Let,
Var
}

7
examples/count.pork Normal file
View File

@ -0,0 +1,7 @@
export func main() {
var x = 1
while x <= 5 {
println(x)
x = x + 1
}
}

View File

@ -5,5 +5,6 @@ import gay.pizza.pork.ast.Node
object DiscardNodeAttribution : NodeAttribution { object DiscardNodeAttribution : NodeAttribution {
override fun enter() {} override fun enter() {}
override fun push(token: Token) {} override fun push(token: Token) {}
override fun <T : Node> adopt(node: T) {}
override fun <T : Node> exit(node: T): T = node override fun <T : Node> exit(node: T): T = node
} }

View File

@ -5,5 +5,6 @@ import gay.pizza.pork.ast.Node
interface NodeAttribution { interface NodeAttribution {
fun enter() fun enter()
fun push(token: Token) fun push(token: Token)
fun <T: Node> adopt(node: T)
fun <T: Node> exit(node: T): T fun <T: Node> exit(node: T): T
} }

View File

@ -45,6 +45,14 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
LetAssignment(symbol, value) LetAssignment(symbol, value)
} }
private fun readVarAssignment(): VarAssignment = within {
expect(TokenType.Var)
val symbol = readSymbolRaw()
expect(TokenType.Equals)
val value = readExpression()
VarAssignment(symbol, value)
}
private fun readSymbolRaw(): Symbol = within { private fun readSymbolRaw(): Symbol = within {
expect(TokenType.Symbol) { Symbol(it.text) } expect(TokenType.Symbol) { Symbol(it.text) }
} }
@ -123,6 +131,10 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
readLetAssignment() readLetAssignment()
} }
TokenType.Var -> {
readVarAssignment()
}
TokenType.Symbol -> { TokenType.Symbol -> {
readSymbolCases() readSymbolCases()
} }
@ -161,6 +173,15 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
} }
} }
if (expression is SymbolReference && peek(TokenType.Equals)) {
return within {
attribution.adopt(expression)
expect(TokenType.Equals)
val value = readExpression()
SetAssignment(expression.symbol, value)
}
}
return if (peek( return if (peek(
TokenType.Plus, TokenType.Plus,
TokenType.Minus, TokenType.Minus,
@ -169,7 +190,11 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
TokenType.Equality, TokenType.Equality,
TokenType.Inequality, TokenType.Inequality,
TokenType.Mod, TokenType.Mod,
TokenType.Rem TokenType.Rem,
TokenType.Lesser,
TokenType.Greater,
TokenType.LesserEqual,
TokenType.GreaterEqual
) )
) { ) {
within { within {
@ -268,6 +293,10 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
TokenType.Inequality -> InfixOperator.NotEquals TokenType.Inequality -> InfixOperator.NotEquals
TokenType.Mod -> InfixOperator.EuclideanModulo TokenType.Mod -> InfixOperator.EuclideanModulo
TokenType.Rem -> InfixOperator.Remainder TokenType.Rem -> InfixOperator.Remainder
TokenType.Lesser -> InfixOperator.Lesser
TokenType.Greater -> InfixOperator.Greater
TokenType.LesserEqual -> InfixOperator.LesserEqual
TokenType.GreaterEqual -> InfixOperator.GreaterEqual
else -> throw RuntimeException("Unknown Infix Operator") else -> throw RuntimeException("Unknown Infix Operator")
} }

View File

@ -95,6 +95,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.symbol) visit(node.symbol)
} }
override fun visitVarAssignment(node: VarAssignment) {
append("var ")
visit(node.symbol)
append(" = ")
visit(node.value)
}
override fun visitWhile(node: While) { override fun visitWhile(node: While) {
append("while ") append("while ")
visit(node.condition) visit(node.condition)
@ -113,6 +120,12 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.expression) visit(node.expression)
} }
override fun visitSetAssignment(node: SetAssignment) {
visit(node.symbol)
append(" = ")
visit(node.value)
}
override fun visitIf(node: If) { override fun visitIf(node: If) {
append("if ") append("if ")
visit(node.condition) visit(node.condition)

View File

@ -22,6 +22,15 @@ class TokenNodeAttribution : NodeAttribution {
store.add(token) store.add(token)
} }
override fun <T : Node> adopt(node: T) {
val tokens = nodes.remove(node)
if (tokens != null) {
for (token in tokens) {
push(token)
}
}
}
override fun <T: Node> exit(node: T): T { override fun <T: Node> exit(node: T): T {
val store = stack.removeLast() val store = stack.removeLast()
nodes[node] = store nodes[node] = store

View File

@ -19,6 +19,10 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
Minus(SingleChar('-'), OperatorFamily), Minus(SingleChar('-'), OperatorFamily),
Multiply(SingleChar('*'), OperatorFamily), Multiply(SingleChar('*'), OperatorFamily),
Divide(SingleChar('/'), OperatorFamily), Divide(SingleChar('/'), OperatorFamily),
LesserEqual(OperatorFamily),
GreaterEqual(OperatorFamily),
Lesser(SingleChar('<'), OperatorFamily, Promotion('=', LesserEqual)),
Greater(SingleChar('>'), OperatorFamily, Promotion('=', GreaterEqual)),
LeftCurly(SingleChar('{')), LeftCurly(SingleChar('{')),
RightCurly(SingleChar('}')), RightCurly(SingleChar('}')),
LeftBracket(SingleChar('[')), LeftBracket(SingleChar('[')),
@ -42,6 +46,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
Func(Keyword("func"), KeywordFamily), Func(Keyword("func"), KeywordFamily),
Native(Keyword("native"), KeywordFamily), Native(Keyword("native"), KeywordFamily),
Let(Keyword("let"), KeywordFamily), Let(Keyword("let"), KeywordFamily),
Var(Keyword("var"), KeywordFamily),
Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }), Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }),
BlockComment(CommentFamily), BlockComment(CommentFamily),
LineComment(CommentFamily), LineComment(CommentFamily),