mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
language: add unary plus & minus, post increment & decrement operators, non-newline print builtin. fix block comments
This commit is contained in:
parent
f6d2fc5165
commit
36b574bf5b
@ -181,6 +181,12 @@ types:
|
||||
- name: Negate
|
||||
values:
|
||||
token: "!"
|
||||
- name: UnaryPlus
|
||||
values:
|
||||
token: "+"
|
||||
- name: UnaryMinus
|
||||
values:
|
||||
token: "-"
|
||||
PrefixOperation:
|
||||
parent: Expression
|
||||
values:
|
||||
@ -188,6 +194,24 @@ types:
|
||||
type: PrefixOperator
|
||||
- name: expression
|
||||
type: Expression
|
||||
SuffixOperator:
|
||||
values:
|
||||
- name: token
|
||||
type: String
|
||||
enums:
|
||||
- name: Increment
|
||||
values:
|
||||
token: "++"
|
||||
- name: Decrement
|
||||
values:
|
||||
token: "--"
|
||||
SuffixOperation:
|
||||
parent: Expression
|
||||
values:
|
||||
- name: op
|
||||
type: SuffixOperator
|
||||
- name: reference
|
||||
type: SymbolReference
|
||||
StringLiteral:
|
||||
parent: Expression
|
||||
values:
|
||||
|
@ -24,6 +24,8 @@ digraph A {
|
||||
type_Parentheses [shape=box,label="Parentheses"]
|
||||
type_PrefixOperator [shape=box,label="PrefixOperator"]
|
||||
type_PrefixOperation [shape=box,label="PrefixOperation"]
|
||||
type_SuffixOperator [shape=box,label="SuffixOperator"]
|
||||
type_SuffixOperation [shape=box,label="SuffixOperation"]
|
||||
type_StringLiteral [shape=box,label="StringLiteral"]
|
||||
type_SymbolReference [shape=box,label="SymbolReference"]
|
||||
type_While [shape=box,label="While"]
|
||||
@ -49,6 +51,7 @@ digraph A {
|
||||
type_Expression -> type_ListLiteral
|
||||
type_Expression -> type_Parentheses
|
||||
type_Expression -> type_PrefixOperation
|
||||
type_Expression -> type_SuffixOperation
|
||||
type_Expression -> type_StringLiteral
|
||||
type_Expression -> type_SymbolReference
|
||||
type_Expression -> type_While
|
||||
@ -84,6 +87,8 @@ digraph A {
|
||||
type_Parentheses -> type_Expression [style=dotted]
|
||||
type_PrefixOperation -> type_PrefixOperator [style=dotted]
|
||||
type_PrefixOperation -> type_Expression [style=dotted]
|
||||
type_SuffixOperation -> type_SuffixOperator [style=dotted]
|
||||
type_SuffixOperation -> type_SymbolReference [style=dotted]
|
||||
type_SymbolReference -> type_Symbol [style=dotted]
|
||||
type_While -> type_Expression [style=dotted]
|
||||
type_While -> type_Block [style=dotted]
|
||||
|
@ -59,6 +59,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
override fun visitStringLiteral(node: StringLiteral): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitSymbol(node: Symbol): Unit =
|
||||
handle(node)
|
||||
|
||||
|
@ -25,6 +25,7 @@ enum class NodeType(val parent: NodeType? = null) {
|
||||
PrefixOperation(Expression),
|
||||
SetAssignment(Expression),
|
||||
StringLiteral(Expression),
|
||||
SuffixOperation(Expression),
|
||||
Symbol(Node),
|
||||
SymbolReference(Expression),
|
||||
VarAssignment(Expression),
|
||||
|
@ -40,6 +40,8 @@ interface NodeVisitor<T> {
|
||||
|
||||
fun visitStringLiteral(node: StringLiteral): T
|
||||
|
||||
fun visitSuffixOperation(node: SuffixOperation): T
|
||||
|
||||
fun visitSymbol(node: Symbol): T
|
||||
|
||||
fun visitSymbolReference(node: SymbolReference): T
|
||||
|
@ -20,6 +20,7 @@ fun <T> NodeVisitor<T>.visit(node: Node): T =
|
||||
is ListLiteral -> visitListLiteral(node)
|
||||
is Parentheses -> visitParentheses(node)
|
||||
is PrefixOperation -> visitPrefixOperation(node)
|
||||
is SuffixOperation -> visitSuffixOperation(node)
|
||||
is StringLiteral -> visitStringLiteral(node)
|
||||
is SymbolReference -> visitSymbolReference(node)
|
||||
is While -> visitWhile(node)
|
||||
|
@ -7,5 +7,7 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
@SerialName("prefixOperator")
|
||||
enum class PrefixOperator(val token: String) {
|
||||
Negate("!")
|
||||
Negate("!"),
|
||||
UnaryPlus("+"),
|
||||
UnaryMinus("-")
|
||||
}
|
||||
|
29
ast/src/main/kotlin/gay/pizza/pork/ast/SuffixOperation.kt
Normal file
29
ast/src/main/kotlin/gay/pizza/pork/ast/SuffixOperation.kt
Normal 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("suffixOperation")
|
||||
class SuffixOperation(val op: SuffixOperator, val reference: SymbolReference) : Expression() {
|
||||
override val type: NodeType = NodeType.SuffixOperation
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(reference)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitSuffixOperation(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is SuffixOperation) return false
|
||||
return other.op == op && other.reference == reference
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = op.hashCode()
|
||||
result = 31 * result + reference.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
12
ast/src/main/kotlin/gay/pizza/pork/ast/SuffixOperator.kt
Normal file
12
ast/src/main/kotlin/gay/pizza/pork/ast/SuffixOperator.kt
Normal file
@ -0,0 +1,12 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("suffixOperator")
|
||||
enum class SuffixOperator(val token: String) {
|
||||
Increment("++"),
|
||||
Decrement("--")
|
||||
}
|
@ -71,9 +71,65 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
}
|
||||
!value
|
||||
}
|
||||
PrefixOperator.UnaryPlus, PrefixOperator.UnaryMinus -> {
|
||||
if (value !is Number) {
|
||||
throw RuntimeException("Numeric unary '${node.op.token}' illegal on non-numeric type '${value.javaClass.simpleName}'")
|
||||
}
|
||||
unaryNumericOperation(node, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unaryNumericOperation(node: PrefixOperation, value: Number) = when (value) {
|
||||
is Double -> {
|
||||
unaryNumericOperation(
|
||||
node.op,
|
||||
value,
|
||||
convert = { it.toDouble() },
|
||||
plus = { +it },
|
||||
minus = { -it }
|
||||
)
|
||||
}
|
||||
is Float -> {
|
||||
unaryNumericOperation(
|
||||
node.op,
|
||||
value,
|
||||
convert = { it.toFloat() },
|
||||
plus = { +it },
|
||||
minus = { -it }
|
||||
)
|
||||
}
|
||||
is Long -> {
|
||||
unaryNumericOperation(
|
||||
node.op,
|
||||
value,
|
||||
convert = { it.toLong() },
|
||||
plus = { +it },
|
||||
minus = { -it }
|
||||
)
|
||||
}
|
||||
is Int -> {
|
||||
unaryNumericOperation(
|
||||
node.op,
|
||||
value,
|
||||
convert = { it.toInt() },
|
||||
plus = { +it },
|
||||
minus = { -it }
|
||||
)
|
||||
}
|
||||
else -> throw RuntimeException("Unknown numeric type: ${value.javaClass.name}")
|
||||
}
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation): Any {
|
||||
val previousValue = currentScope.value(node.reference.symbol.id)
|
||||
val infix = visitInfixOperation(InfixOperation(node.reference, when (node.op) {
|
||||
SuffixOperator.Increment -> InfixOperator.Plus
|
||||
SuffixOperator.Decrement -> InfixOperator.Minus
|
||||
}, IntegerLiteral(1)))
|
||||
currentScope.set(node.reference.symbol.id, infix)
|
||||
return previousValue
|
||||
}
|
||||
|
||||
override fun visitSetAssignment(node: SetAssignment): Any {
|
||||
val value = node.value.visit(this)
|
||||
currentScope.set(node.symbol.id, value)
|
||||
@ -219,6 +275,20 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T: Number> unaryNumericOperation(
|
||||
op: PrefixOperator,
|
||||
value: Number,
|
||||
convert: (Number) -> T,
|
||||
plus: (T) -> T,
|
||||
minus: (T) -> T
|
||||
): Any {
|
||||
return when (op) {
|
||||
PrefixOperator.UnaryPlus -> plus(convert(value))
|
||||
PrefixOperator.UnaryMinus -> minus(convert(value))
|
||||
else -> throw RuntimeException("Unable to handle operation $op")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBlock(node: Block): BlockFunction = BlockFunction {
|
||||
var value: Any? = null
|
||||
for (expression in node.expressions) {
|
||||
|
@ -2,6 +2,7 @@ package gay.pizza.pork.evaluator
|
||||
|
||||
class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
|
||||
private val functions = mutableMapOf(
|
||||
"print" to CallableFunction(::printValues),
|
||||
"println" to CallableFunction(::printLine)
|
||||
)
|
||||
|
||||
@ -9,15 +10,15 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
|
||||
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
|
||||
}
|
||||
|
||||
private fun printValues(arguments: Arguments): Any {
|
||||
if (quiet || arguments.values.isEmpty()) return None
|
||||
print(arguments.values.joinToString(" "))
|
||||
return None
|
||||
}
|
||||
|
||||
private fun printLine(arguments: Arguments): Any {
|
||||
if (quiet) {
|
||||
return None
|
||||
}
|
||||
when (arguments.values.count()) {
|
||||
0 -> println()
|
||||
1 -> println(arguments.values[0])
|
||||
else -> println(arguments.values.joinToString(" "))
|
||||
}
|
||||
if (quiet) return None
|
||||
println(arguments.values.joinToString(" "))
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
20
examples/suffix.pork
Normal file
20
examples/suffix.pork
Normal file
@ -0,0 +1,20 @@
|
||||
/* Barber shop sign generator! */
|
||||
func generatePole(width, height) {
|
||||
var i = 0
|
||||
while i < height {
|
||||
let index = i++ mod width
|
||||
var j = 0
|
||||
while j < width {
|
||||
if j == index { print("#") } else { print("_") }
|
||||
j++
|
||||
}
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
export func main() {
|
||||
println("nobody:")
|
||||
println()
|
||||
println("barber pole:")
|
||||
generatePole(6, 18)
|
||||
}
|
6
examples/unary.pork
Normal file
6
examples/unary.pork
Normal file
@ -0,0 +1,6 @@
|
||||
export func main() {
|
||||
let constant = -42
|
||||
println("constant set by unary minus: ", constant)
|
||||
println("constant modified by unary minus:", -constant)
|
||||
println("constant modified by unary plus: ", +constant)
|
||||
}
|
@ -66,7 +66,12 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
expect(TokenType.RightParentheses)
|
||||
FunctionCall(symbol, arguments)
|
||||
} else {
|
||||
SymbolReference(symbol)
|
||||
val reference = SymbolReference(symbol)
|
||||
if (peek(TokenType.PlusPlus, TokenType.MinusMinus)) {
|
||||
expect(TokenType.PlusPlus, TokenType.MinusMinus) {
|
||||
SuffixOperation(convertSuffixOperator(it), reference)
|
||||
}
|
||||
} else reference
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,8 +83,8 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
}
|
||||
|
||||
private fun readPrefixOperation(): PrefixOperation = within {
|
||||
expect(TokenType.Negation) {
|
||||
PrefixOperation(PrefixOperator.Negate, readExpression())
|
||||
expect(TokenType.Negation, TokenType.Plus, TokenType.Minus) {
|
||||
PrefixOperation(convertPrefixOperator(it), readExpression())
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +148,7 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
readParentheses()
|
||||
}
|
||||
|
||||
TokenType.Negation -> {
|
||||
TokenType.Negation, TokenType.Plus, TokenType.Minus -> {
|
||||
readPrefixOperation()
|
||||
}
|
||||
|
||||
@ -288,22 +293,33 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertInfixOperator(token: Token): InfixOperator =
|
||||
when (token.type) {
|
||||
TokenType.Plus -> InfixOperator.Plus
|
||||
TokenType.Minus -> InfixOperator.Minus
|
||||
TokenType.Multiply -> InfixOperator.Multiply
|
||||
TokenType.Divide -> InfixOperator.Divide
|
||||
TokenType.Equality -> InfixOperator.Equals
|
||||
TokenType.Inequality -> InfixOperator.NotEquals
|
||||
TokenType.Mod -> InfixOperator.EuclideanModulo
|
||||
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")
|
||||
}
|
||||
private fun convertInfixOperator(token: Token): InfixOperator = when (token.type) {
|
||||
TokenType.Plus -> InfixOperator.Plus
|
||||
TokenType.Minus -> InfixOperator.Minus
|
||||
TokenType.Multiply -> InfixOperator.Multiply
|
||||
TokenType.Divide -> InfixOperator.Divide
|
||||
TokenType.Equality -> InfixOperator.Equals
|
||||
TokenType.Inequality -> InfixOperator.NotEquals
|
||||
TokenType.Mod -> InfixOperator.EuclideanModulo
|
||||
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")
|
||||
}
|
||||
|
||||
private fun convertPrefixOperator(token: Token): PrefixOperator = when (token.type) {
|
||||
TokenType.Plus -> PrefixOperator.UnaryPlus
|
||||
TokenType.Minus -> PrefixOperator.UnaryMinus
|
||||
else -> throw RuntimeException("Unknown Prefix Operator")
|
||||
}
|
||||
|
||||
private fun convertSuffixOperator(token: Token): SuffixOperator = when (token.type) {
|
||||
TokenType.PlusPlus -> SuffixOperator.Increment
|
||||
TokenType.MinusMinus -> SuffixOperator.Decrement
|
||||
else -> throw RuntimeException("Unknown Suffix Operator")
|
||||
}
|
||||
|
||||
fun readCompilationUnit(): CompilationUnit = within {
|
||||
val declarations = mutableListOf<Declaration>()
|
||||
|
@ -120,6 +120,11 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
visit(node.expression)
|
||||
}
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation) {
|
||||
visit(node.reference)
|
||||
append(node.op.token)
|
||||
}
|
||||
|
||||
override fun visitSetAssignment(node: SetAssignment) {
|
||||
visit(node.symbol)
|
||||
append(" = ")
|
||||
|
@ -15,8 +15,10 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
|
||||
Equality(OperatorFamily),
|
||||
Inequality(OperatorFamily),
|
||||
Equals(SingleChar('='), Promotion('=', Equality)),
|
||||
Plus(SingleChar('+'), OperatorFamily),
|
||||
Minus(SingleChar('-'), OperatorFamily),
|
||||
PlusPlus(ManyChars("++"), OperatorFamily),
|
||||
MinusMinus(ManyChars("--"), OperatorFamily),
|
||||
Plus(SingleChar('+'), OperatorFamily, Promotion('+', PlusPlus)),
|
||||
Minus(SingleChar('-'), OperatorFamily, Promotion('-', MinusMinus)),
|
||||
Multiply(SingleChar('*'), OperatorFamily),
|
||||
Divide(SingleChar('/'), OperatorFamily),
|
||||
LesserEqual(OperatorFamily),
|
||||
|
@ -9,6 +9,7 @@ class Tokenizer(val source: CharSource) {
|
||||
var endOfComment = false
|
||||
while (true) {
|
||||
val char = source.next()
|
||||
if (char == CharSource.NullChar) throw RuntimeException("Unterminated block comment")
|
||||
append(char)
|
||||
|
||||
if (endOfComment) {
|
||||
|
@ -1,2 +1,5 @@
|
||||
export func println(messages...)
|
||||
export func print(values...)
|
||||
native internal "print"
|
||||
|
||||
export func println(values...)
|
||||
native internal "println"
|
||||
|
Loading…
Reference in New Issue
Block a user