Declaration based compilation units.

This commit is contained in:
2023-09-02 20:22:08 -07:00
parent 04c78c35e0
commit b1f9e02253
26 changed files with 254 additions and 305 deletions

View File

@ -6,7 +6,7 @@ Very WIP. Like VERY.
```pork ```pork
/* fibonacci sequence */ /* fibonacci sequence */
fib = { n in fn fib(n) {
if n == 0 if n == 0
then 0 then 0
else if n == 1 else if n == 1
@ -14,8 +14,10 @@ fib = { n in
else fib(n - 1) + fib(n - 2) else fib(n - 1) + fib(n - 2)
} }
result = fib(20) fn main() {
println(result) result = fib(20)
println(result)
}
``` ```
## Usage ## Usage

View File

@ -1,17 +0,0 @@
/* fibonacci sequence */
/**
* fib(n): calculate the fibonacci sequence.
* @input n the number to calculate fibonacci for
* @result the value of the fibonacci sequence for the number
*/
fib = { n in
if n == 0 // if n is zero, return zero
then 0
else if n == 1 // if n is one, return one
then 1
else fib(n - 1) + fib(n - 2)
}
// result of fib(20)
result = fib(20)
println(result)

View File

@ -1,5 +1,5 @@
/* fibonacci sequence */ /* fibonacci sequence */
fib = { n in fn fib(n) {
if n == 0 if n == 0
then 0 then 0
else if n == 1 else if n == 1
@ -7,5 +7,7 @@ fib = { n in
else fib(n - 1) + fib(n - 2) else fib(n - 1) + fib(n - 2)
} }
result = fib(20) fn main() {
println(result) result = fib(20)
println(result)
}

View File

@ -1,40 +1,41 @@
three = 3 fn main() {
two = 2 three = 3
two = 2
calculateSimple = { in calculateSimple = { in
(50 + three) * two (50 + three) * two
} }
calculateComplex = { in calculateComplex = { in
three + two + 50 three + two + 50
} }
multiply = { a, b in multiply = { a, b in
a * b a * b
} }
// calculates the result // calculates the result
calculateSimpleResult = calculateSimple() calculateSimpleResult = calculateSimple()
calculateComplexResult = calculateComplex() calculateComplexResult = calculateComplex()
multiplyResult = multiply(50, 50) multiplyResult = multiply(50, 50)
list = [10, 20, 30] list = [10, 20, 30]
trueValue = true trueValue = true
falseValue = false falseValue = false
invert = { value in invert = { value in
!value !value
} }
notEqual = { a, b in notEqual = { a, b in
a != b a != b
} }
equal = { a, b in equal = { a, b in
a == b a == b
} }
results = [ results = [
calculateSimpleResult, calculateSimpleResult,
calculateComplexResult, calculateComplexResult,
multiplyResult, multiplyResult,
@ -47,7 +48,8 @@ results = [
equal(5, 6), equal(5, 6),
notEqual(5, 5), notEqual(5, 5),
notEqual(5, 6) notEqual(5, 6)
] ]
println("results:") println("results:")
println(results) println(results)
}

View File

@ -16,7 +16,10 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
override fun visitPrefixOperation(node: PrefixOperation): Unit = handle(node) override fun visitPrefixOperation(node: PrefixOperation): Unit = handle(node)
override fun visitIf(node: If): Unit = handle(node) override fun visitIf(node: If): Unit = handle(node)
override fun visitInfixOperation(node: InfixOperation): Unit = handle(node) override fun visitInfixOperation(node: InfixOperation): Unit = handle(node)
override fun visitProgram(node: Program): Unit = handle(node) override fun visitFunctionDeclaration(node: FunctionDeclaration): Unit = handle(node)
override fun visitBlock(node: Block): Unit = handle(node)
override fun visitCompilationUnit(node: CompilationUnit): Unit = handle(node)
private fun handle(node: Node) { private fun handle(node: Node) {
handler(node) handler(node)

View File

@ -4,7 +4,9 @@ enum class NodeType(val parent: NodeType? = null) {
Node, Node,
Symbol(Node), Symbol(Node),
Expression(Node), Expression(Node),
Program(Node), Declaration(Node),
Block(Node),
CompilationUnit(Node),
IntLiteral(Expression), IntLiteral(Expression),
BooleanLiteral(Expression), BooleanLiteral(Expression),
ListLiteral(Expression), ListLiteral(Expression),
@ -16,7 +18,8 @@ enum class NodeType(val parent: NodeType? = null) {
InfixOperation(Expression), InfixOperation(Expression),
SymbolReference(Expression), SymbolReference(Expression),
FunctionCall(Expression), FunctionCall(Expression),
If(Expression); If(Expression),
FunctionDeclaration(Declaration);
val parents: Set<NodeType> val parents: Set<NodeType>

View File

@ -16,7 +16,10 @@ interface NodeVisitor<T> {
fun visitPrefixOperation(node: PrefixOperation): T fun visitPrefixOperation(node: PrefixOperation): T
fun visitIf(node: If): T fun visitIf(node: If): T
fun visitInfixOperation(node: InfixOperation): T fun visitInfixOperation(node: InfixOperation): T
fun visitProgram(node: Program): T fun visitFunctionDeclaration(node: FunctionDeclaration): T
fun visitBlock(node: Block): T
fun visitCompilationUnit(node: CompilationUnit): T
fun visitExpression(node: Expression): T = when (node) { fun visitExpression(node: Expression): T = when (node) {
is IntLiteral -> visitIntLiteral(node) is IntLiteral -> visitIntLiteral(node)
@ -33,10 +36,16 @@ interface NodeVisitor<T> {
is InfixOperation -> visitInfixOperation(node) is InfixOperation -> visitInfixOperation(node)
} }
fun visitDeclaration(node: Declaration): T = when (node) {
is FunctionDeclaration -> visitFunctionDeclaration(node)
}
fun visit(node: Node): T = when (node) { fun visit(node: Node): T = when (node) {
is Symbol -> visitSymbol(node) is Symbol -> visitSymbol(node)
is Expression -> visitExpression(node) is Expression -> visitExpression(node)
is Program -> visitProgram(node) is CompilationUnit -> visitCompilationUnit(node)
is Block -> visitBlock(node)
is Declaration -> visitDeclaration(node)
} }
fun visitNodes(vararg nodes: Node?): List<T> = fun visitNodes(vararg nodes: Node?): List<T> =

View File

@ -82,7 +82,6 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.symbol) visit(node.symbol)
} }
override fun visitLambda(node: Lambda) { override fun visitLambda(node: Lambda) {
append("{") append("{")
if (node.arguments.isNotEmpty()) { if (node.arguments.isNotEmpty()) {
@ -148,10 +147,37 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.right) visit(node.right)
} }
override fun visitProgram(node: Program) { override fun visitFunctionDeclaration(node: FunctionDeclaration) {
append("fn ")
visit(node.symbol)
append("(")
for ((index, argument) in node.arguments.withIndex()) {
visit(argument)
if (index + 1 != node.arguments.size) {
append(", ")
}
}
append(") ")
visit(node.block)
}
override fun visitBlock(node: Block) {
append("{")
if (node.expressions.isNotEmpty()) {
out.increaseIndent()
for (expression in node.expressions) { for (expression in node.expressions) {
out.emitIndent() appendLine()
visit(expression) visit(expression)
}
out.decreaseIndent()
appendLine()
}
append("}")
}
override fun visitCompilationUnit(node: CompilationUnit) {
for (declaration in node.declarations) {
visit(declaration)
appendLine() appendLine()
} }
} }

View File

@ -6,15 +6,15 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@SerialName("program") @SerialName("block")
class Program(val expressions: List<Expression>) : Node() { class Block(val expressions: List<Expression>) : Node() {
override val type: NodeType = NodeType.Program override val type: NodeType = NodeType.Block
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> = override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(expressions) visitor.visitAll(expressions)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is Program) return false if (other !is Block) return false
return other.expressions == expressions return other.expressions == expressions
} }

View File

@ -0,0 +1,26 @@
package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("compilationUnit")
class CompilationUnit(val declarations: List<Declaration>) : Node() {
override val type: NodeType = NodeType.CompilationUnit
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(declarations)
override fun equals(other: Any?): Boolean {
if (other !is CompilationUnit) return false
return other.declarations == declarations
}
override fun hashCode(): Int {
var result = declarations.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.ast.nodes
import kotlinx.serialization.Serializable
@Serializable
sealed class Declaration : Node()

View File

@ -0,0 +1,28 @@
package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("functionDeclaration")
class FunctionDeclaration(val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Declaration() {
override val type: NodeType = NodeType.FunctionDeclaration
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol)
override fun equals(other: Any?): Boolean {
if (other !is FunctionDeclaration) return false
return other.symbol == symbol && other.arguments == arguments && other.block == block
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + symbol.hashCode()
result = 31 * result + arguments.hashCode()
result = 31 * result + block.hashCode()
return result
}
}

View File

@ -13,7 +13,7 @@ class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute"
override fun run() { override fun run() {
val frontend = FileFrontend(path) val frontend = FileFrontend(path)
val attribution = TokenNodeAttribution() val attribution = TokenNodeAttribution()
val program = frontend.parse(attribution) val compilationUnit = frontend.parse(attribution)
val coalescer = NodeCoalescer { node -> val coalescer = NodeCoalescer { node ->
val tokens = attribution.assembleTokens(node) val tokens = attribution.assembleTokens(node)
@ -22,6 +22,6 @@ class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute"
println("token $token") println("token $token")
} }
} }
coalescer.visit(program) coalescer.visit(compilationUnit)
} }
} }

View File

@ -1,16 +0,0 @@
package gay.pizza.pork.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.compiler.DartCompiler
import gay.pizza.pork.frontend.FileFrontend
class GenerateDartCommand : CliktCommand(help = "Generate Dart Code", name = "generate-dart") {
val path by argument("file").path(mustExist = true, canBeDir = false)
override fun run() {
val frontend = FileFrontend(path)
println(frontend.visit(DartCompiler()))
}
}

View File

@ -1,16 +0,0 @@
package gay.pizza.pork.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.compiler.KotlinCompiler
import gay.pizza.pork.frontend.FileFrontend
class GenerateKotlinCommand : CliktCommand(help = "Generate Kotlin Code", name = "generate-kotlin") {
val path by argument("file").path(mustExist = true, canBeDir = false)
override fun run() {
val frontend = FileFrontend(path)
println(frontend.visit(KotlinCompiler()))
}
}

View File

@ -5,7 +5,7 @@ import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.frontend.FileFrontend import gay.pizza.pork.frontend.FileFrontend
class ReprintCommand : CliktCommand(help = "Reprint Parsed Program", name = "reprint") { class ReprintCommand : CliktCommand(help = "Reprint Parsed Compilation Unit", name = "reprint") {
val path by argument("file").path(mustExist = true, canBeDir = false) val path by argument("file").path(mustExist = true, canBeDir = false)
override fun run() { override fun run() {

View File

@ -14,9 +14,7 @@ class RootCommand : CliktCommand(
TokenizeCommand(), TokenizeCommand(),
ReprintCommand(), ReprintCommand(),
AstCommand(), AstCommand(),
AttributeCommand(), AttributeCommand()
GenerateKotlinCommand(),
GenerateDartCommand()
) )
} }

View File

@ -3,6 +3,7 @@ package gay.pizza.pork.cli
import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.eval.Arguments
import gay.pizza.pork.eval.CallableFunction import gay.pizza.pork.eval.CallableFunction
import gay.pizza.pork.eval.Scope import gay.pizza.pork.eval.Scope
import gay.pizza.pork.frontend.FileFrontend import gay.pizza.pork.frontend.FileFrontend
@ -19,5 +20,6 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
} }
}) })
frontend.evaluate(scope) frontend.evaluate(scope)
scope.call("main", Arguments(emptyList()))
} }
} }

View File

@ -5,7 +5,7 @@ import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.frontend.FileFrontend import gay.pizza.pork.frontend.FileFrontend
class TokenizeCommand : CliktCommand(help = "Tokenize Program", name = "tokenize") { class TokenizeCommand : CliktCommand(help = "Tokenize Compilation Unit", name = "tokenize") {
val path by argument("file").path(mustExist = true, canBeDir = false) val path by argument("file").path(mustExist = true, canBeDir = false)
override fun run() { override fun run() {

View File

@ -1,90 +0,0 @@
package gay.pizza.pork.compiler
import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.nodes.*
import gay.pizza.pork.util.StringEscape
class DartCompiler : NodeVisitor<String> {
override fun visitIntLiteral(node: IntLiteral): String =
node.value.toString()
override fun visitStringLiteral(node: StringLiteral): String =
"\"" + StringEscape.escape(node.text) + "\""
override fun visitBooleanLiteral(node: BooleanLiteral): String =
node.value.toString()
override fun visitListLiteral(node: ListLiteral): String = buildString {
append("[")
for ((index, item) in node.items.withIndex()) {
appendLine()
append(visit(item))
if (index + 1 != node.items.size) {
append(",")
}
}
append("]")
}
override fun visitSymbol(node: Symbol): String =
node.id
override fun visitFunctionCall(node: FunctionCall): String =
"${visit(node.symbol)}(${node.arguments.joinToString(", ") { visit(it) }})"
override fun visitDefine(node: Define): String =
"final ${visit(node.symbol)} = ${visit(node.value)};"
override fun visitSymbolReference(node: SymbolReference): String =
visit(node.symbol)
override fun visitLambda(node: Lambda): String = buildString {
append("(${node.arguments.joinToString(", ") { visit(it) }}) {")
appendLine()
for ((index, expression) in node.expressions.withIndex()) {
val code = visit(expression)
if (index == node.expressions.size - 1) {
append("return ");
}
append(code)
append(";")
}
appendLine()
append("}")
}
override fun visitParentheses(node: Parentheses): String =
"(${visit(node.expression)})"
override fun visitPrefixOperation(node: PrefixOperation): String =
"${node.op.token}${visit(node.expression)}"
override fun visitIf(node: If): String = buildString {
append("if (")
append(visit(node.condition))
append(") {")
append(visit(node.thenExpression))
append("}")
if (node.elseExpression != null) {
append(" else {")
append(visit(node.elseExpression))
append("}")
}
}
override fun visitInfixOperation(node: InfixOperation): String =
"${visit(node.left)} ${node.op.token} ${visit(node.right)}"
override fun visitProgram(node: Program): String = buildString {
appendLine("void main() {")
for (item in node.expressions) {
append(visit(item))
if (!endsWith(";")) {
append(";")
}
append(";")
appendLine()
}
appendLine("}")
}
}

View File

@ -1,78 +0,0 @@
package gay.pizza.pork.compiler
import gay.pizza.pork.ast.*
import gay.pizza.pork.ast.nodes.*
import gay.pizza.pork.util.StringEscape
class KotlinCompiler : NodeVisitor<String> {
override fun visitIntLiteral(node: IntLiteral): String =
node.value.toString()
override fun visitStringLiteral(node: StringLiteral): String =
"\"" + StringEscape.escape(node.text) + "\""
override fun visitBooleanLiteral(node: BooleanLiteral): String =
node.value.toString()
override fun visitListLiteral(node: ListLiteral): String = buildString {
append("listOf(")
for ((index, item) in node.items.withIndex()) {
appendLine()
append(visit(item))
if (index + 1 != node.items.size) {
append(",")
}
}
append(")")
}
override fun visitSymbol(node: Symbol): String =
node.id
override fun visitFunctionCall(node: FunctionCall): String =
"${visit(node.symbol)}(${node.arguments.joinToString(", ") { visit(it) }})"
override fun visitDefine(node: Define): String =
"val ${visit(node.symbol)} = ${visit(node.value)}"
override fun visitSymbolReference(node: SymbolReference): String =
visit(node.symbol)
override fun visitLambda(node: Lambda): String = buildString {
append("{ ${node.arguments.joinToString(", ") { visit(it) }} ->")
appendLine()
append(visitAll(node.expressions).joinToString("\n"))
appendLine()
append("}")
}
override fun visitParentheses(node: Parentheses): String =
"(${visit(node.expression)})"
override fun visitPrefixOperation(node: PrefixOperation): String =
"${node.op.token}${visit(node.expression)}"
override fun visitIf(node: If): String = buildString {
append("if (")
append(visit(node.condition))
append(") {")
append(visit(node.thenExpression))
append("}")
if (node.elseExpression != null) {
append(" else {")
append(visit(node.elseExpression))
append("}")
}
}
override fun visitInfixOperation(node: InfixOperation): String =
"${visit(node.left)} ${node.op.token} ${visit(node.right)}"
override fun visitProgram(node: Program): String = buildString {
appendLine("fun main() {")
for (item in node.expressions) {
appendLine(visit(item))
}
appendLine("}")
}
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.eval
fun interface BlockFunction {
fun call(): Any
}

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.eval package gay.pizza.pork.eval
import gay.pizza.pork.ast.* import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.nodes.* import gay.pizza.pork.ast.nodes.*
class Evaluator(root: Scope) : NodeVisitor<Any> { class Evaluator(root: Scope) : NodeVisitor<Any> {
@ -102,11 +102,35 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
} }
} }
override fun visitProgram(node: Program): Any { override fun visitFunctionDeclaration(node: FunctionDeclaration): Any {
val blockFunction = visitBlock(node.block) as BlockFunction
val function = CallableFunction { arguments ->
currentScope = currentScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}
try {
return@CallableFunction blockFunction.call()
} finally {
currentScope = currentScope.leave()
}
}
currentScope.define(node.symbol.id, function)
return None
}
override fun visitBlock(node: Block): Any = BlockFunction {
var value: Any? = null var value: Any? = null
for (expression in node.expressions) { for (expression in node.expressions) {
value = visit(expression) value = visit(expression)
} }
return value ?: None value ?: None
}
override fun visitCompilationUnit(node: CompilationUnit): Any {
for (declaration in node.declarations) {
visit(declaration)
}
return None
} }
} }

View File

@ -2,7 +2,7 @@ package gay.pizza.pork.frontend
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.Printer import gay.pizza.pork.ast.Printer
import gay.pizza.pork.ast.nodes.Program import gay.pizza.pork.ast.nodes.CompilationUnit
import gay.pizza.pork.eval.Evaluator import gay.pizza.pork.eval.Evaluator
import gay.pizza.pork.eval.Scope import gay.pizza.pork.eval.Scope
import gay.pizza.pork.parse.* import gay.pizza.pork.parse.*
@ -13,8 +13,8 @@ abstract class Frontend {
fun tokenize(): TokenStream = fun tokenize(): TokenStream =
Tokenizer(createCharSource()).tokenize() Tokenizer(createCharSource()).tokenize()
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): Program = fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit =
Parser(TokenStreamSource(tokenize()), attribution).readProgram() Parser(TokenStreamSource(tokenize()), attribution).readCompilationUnit()
fun highlight(scheme: HighlightScheme): List<Highlight> = fun highlight(scheme: HighlightScheme): List<Highlight> =
Highlighter(scheme).highlight(tokenize()) Highlighter(scheme).highlight(tokenize())

View File

@ -160,6 +160,35 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
} else expression } else expression
} }
private fun readBlock(): Block = within {
expect(TokenType.LeftCurly)
val items = collect(TokenType.RightCurly) {
readExpression()
}
expect(TokenType.RightCurly)
Block(items)
}
private fun readFunctionDeclaration(): FunctionDeclaration = within {
expect(TokenType.Fn)
val name = readSymbolRaw()
expect(TokenType.LeftParentheses)
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { readSymbolRaw() }
expect(TokenType.RightParentheses)
FunctionDeclaration(name, arguments, readBlock())
}
fun readDeclaration(): Declaration {
val token = peek()
return when (token.type) {
TokenType.Fn -> readFunctionDeclaration()
else -> throw RuntimeException(
"Failed to parse token: ${token.type} '${token.text}' as" +
" declaration (index ${unsanitizedSource.currentIndex})"
)
}
}
private fun convertInfixOperator(token: Token): InfixOperator = private fun convertInfixOperator(token: Token): InfixOperator =
when (token.type) { when (token.type) {
TokenType.Plus -> InfixOperator.Plus TokenType.Plus -> InfixOperator.Plus
@ -171,10 +200,10 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
else -> throw RuntimeException("Unknown Infix Operator") else -> throw RuntimeException("Unknown Infix Operator")
} }
fun readProgram(): Program = within { fun readCompilationUnit(): CompilationUnit = within {
val items = collect(TokenType.EndOfFile) { readExpression() } val declarations = collect(TokenType.EndOfFile) { readDeclaration() }
expect(TokenType.EndOfFile) expect(TokenType.EndOfFile)
Program(items) CompilationUnit(declarations)
} }
private fun <T> collect( private fun <T> collect(
@ -209,7 +238,7 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
val token = next() val token = next()
if (!types.contains(token.type)) { if (!types.contains(token.type)) {
throw RuntimeException( throw RuntimeException(
"Expected one of ${types.joinToString(", ")} " + "Expected one of ${types.joinToString(", ")}" +
" but got type ${token.type} '${token.text}'" " but got type ${token.type} '${token.text}'"
) )
} }

View File

@ -28,6 +28,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
If(Keyword("if"), KeywordFamily), If(Keyword("if"), KeywordFamily),
Then(Keyword("then"), KeywordFamily), Then(Keyword("then"), KeywordFamily),
Else(Keyword("else"), KeywordFamily), Else(Keyword("else"), KeywordFamily),
Fn(Keyword("fn"), 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),