Compiler refactored: CompilerContext now is class member

This commit is contained in:
Sergey Chernov 2025-07-03 20:00:01 +03:00
parent 950e8301c3
commit 6cf99fbd13
4 changed files with 156 additions and 164 deletions

View File

@ -118,7 +118,7 @@ suspend fun executeFile(fileName: String) {
text = text.substring(pos + 1) text = text.substring(pos + 1)
} }
processErrors { processErrors {
Compiler().compile(Source(fileName, text)).execute(baseContext) Compiler.compile(Source(fileName, text)).execute(baseContext)
} }
} }

View File

@ -4,44 +4,39 @@ package net.sergeych.lyng
* The LYNG compiler. * The LYNG compiler.
*/ */
class Compiler( class Compiler(
val cc: CompilerContext,
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
settings: Settings = Settings() settings: Settings = Settings()
) { ) {
class Settings class Settings
fun compile(source: Source): Script { private fun parseScript(): Script {
return parseScript(
source.startPos,
CompilerContext(parseLyng(source))
)
}
private fun parseScript(start: Pos, cc: CompilerContext): Script {
val statements = mutableListOf<Statement>() val statements = mutableListOf<Statement>()
val start = cc.currentPos()
// val returnScope = cc.startReturnScope() // val returnScope = cc.startReturnScope()
while (parseStatement(cc, braceMeansLambda = true)?.also { while (parseStatement( braceMeansLambda = true)?.also {
statements += it statements += it
} != null) {/**/ } != null) {/**/
} }
return Script(start, statements)//returnScope.needCatch) return Script(start, statements)//returnScope.needCatch)
} }
private fun parseStatement(cc: CompilerContext, braceMeansLambda: Boolean = false): Statement? { private fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
while (true) { while (true) {
val t = cc.next() val t = cc.next()
return when (t.type) { return when (t.type) {
Token.Type.ID -> { Token.Type.ID -> {
parseKeywordStatement(t, cc) parseKeywordStatement(t)
?: run { ?: run {
cc.previous() cc.previous()
parseExpression(cc) parseExpression()
} }
} }
Token.Type.PLUS2, Token.Type.MINUS2 -> { Token.Type.PLUS2, Token.Type.MINUS2 -> {
cc.previous() cc.previous()
parseExpression(cc) parseExpression()
} }
Token.Type.LABEL -> continue Token.Type.LABEL -> continue
@ -54,9 +49,9 @@ class Compiler(
Token.Type.LBRACE -> { Token.Type.LBRACE -> {
cc.previous() cc.previous()
if (braceMeansLambda) if (braceMeansLambda)
parseExpression(cc) parseExpression()
else else
parseBlock(cc) parseBlock()
} }
Token.Type.RBRACE, Token.Type.RBRACKET -> { Token.Type.RBRACE, Token.Type.RBRACKET -> {
@ -69,32 +64,32 @@ class Compiler(
else -> { else -> {
// could be expression // could be expression
cc.previous() cc.previous()
parseExpression(cc) parseExpression()
} }
} }
} }
} }
private fun parseExpression(tokens: CompilerContext): Statement? { private fun parseExpression(): Statement? {
val pos = tokens.currentPos() val pos = cc.currentPos()
return parseExpressionLevel(tokens)?.let { a -> statement(pos) { a.getter(it).value } } return parseExpressionLevel()?.let { a -> statement(pos) { a.getter(it).value } }
} }
private fun parseExpressionLevel(tokens: CompilerContext, level: Int = 0): Accessor? { private fun parseExpressionLevel(level: Int = 0): Accessor? {
if (level == lastLevel) if (level == lastLevel)
return parseTerm(tokens) return parseTerm()
var lvalue: Accessor? = parseExpressionLevel(tokens, level + 1) ?: return null var lvalue: Accessor? = parseExpressionLevel( level + 1) ?: return null
while (true) { while (true) {
val opToken = tokens.next() val opToken = cc.next()
val op = byLevel[level][opToken.type] val op = byLevel[level][opToken.type]
if (op == null) { if (op == null) {
tokens.previous() cc.previous()
break break
} }
val rvalue = parseExpressionLevel(tokens, level + 1) val rvalue = parseExpressionLevel( level + 1)
?: throw ScriptError(opToken.pos, "Expecting expression") ?: throw ScriptError(opToken.pos, "Expecting expression")
lvalue = op.generate(opToken.pos, lvalue!!, rvalue) lvalue = op.generate(opToken.pos, lvalue!!, rvalue)
@ -102,7 +97,7 @@ class Compiler(
return lvalue return lvalue
} }
private fun parseTerm(cc: CompilerContext): Accessor? { private fun parseTerm(): Accessor? {
var operand: Accessor? = null var operand: Accessor? = null
while (true) { while (true) {
@ -116,7 +111,7 @@ class Compiler(
Token.Type.NOT -> { Token.Type.NOT -> {
if (operand != null) throw ScriptError(t.pos, "unexpected operator not '!'") if (operand != null) throw ScriptError(t.pos, "unexpected operator not '!'")
val op = parseTerm(cc) ?: throw ScriptError(t.pos, "Expecting expression") val op = parseTerm() ?: throw ScriptError(t.pos, "Expecting expression")
operand = Accessor { op.getter(it).value.logicalNot(it).asReadonly } operand = Accessor { op.getter(it).value.logicalNot(it).asReadonly }
} }
@ -133,7 +128,7 @@ class Compiler(
Token.Type.LPAREN -> { Token.Type.LPAREN -> {
cc.next() cc.next()
// instance method call // instance method call
val args = parseArgs(cc).first val args = parseArgs().first
isCall = true isCall = true
operand = Accessor { context -> operand = Accessor { context ->
context.pos = next.pos context.pos = next.pos
@ -157,7 +152,7 @@ class Compiler(
cc.next() cc.next()
isCall = true isCall = true
val lambda = val lambda =
parseLambdaExpression(cc) parseLambdaExpression()
operand = Accessor { context -> operand = Accessor { context ->
context.pos = next.pos context.pos = next.pos
val v = left.getter(context).value val v = left.getter(context).value
@ -182,8 +177,8 @@ class Compiler(
val x = left.getter(context).value val x = left.getter(context).value
if (x == ObjNull && isOptional) ObjNull.asReadonly if (x == ObjNull && isOptional) ObjNull.asReadonly
else x.readField(context, next.value) else x.readField(context, next.value)
}) { cc, newValue -> }) { cxt, newValue ->
left.getter(cc).value.writeField(cc, next.value, newValue) left.getter(cxt).value.writeField(cxt, next.value, newValue)
} }
} }
} }
@ -192,21 +187,20 @@ class Compiler(
} }
Token.Type.COLONCOLON -> { Token.Type.COLONCOLON -> {
operand = parseScopeOperator(operand, cc) operand = parseScopeOperator(operand)
} }
Token.Type.LPAREN, Token.Type.NULL_COALESCE_INVOKE -> { Token.Type.LPAREN, Token.Type.NULL_COALESCE_INVOKE -> {
operand?.let { left -> operand?.let { left ->
// this is function call from <left> // this is function call from <left>
operand = parseFunctionCall( operand = parseFunctionCall(
cc,
left, left,
false, false,
t.type == Token.Type.NULL_COALESCE_INVOKE t.type == Token.Type.NULL_COALESCE_INVOKE
) )
} ?: run { } ?: run {
// Expression in parentheses // Expression in parentheses
val statement = parseStatement(cc) ?: throw ScriptError(t.pos, "Expecting expression") val statement = parseStatement() ?: throw ScriptError(t.pos, "Expecting expression")
operand = Accessor { operand = Accessor {
statement.execute(it).asReadonly statement.execute(it).asReadonly
} }
@ -219,7 +213,7 @@ class Compiler(
operand?.let { left -> operand?.let { left ->
// array access // array access
val isOptional = t.type == Token.Type.NULL_COALESCE_INDEX val isOptional = t.type == Token.Type.NULL_COALESCE_INDEX
val index = parseStatement(cc) ?: throw ScriptError(t.pos, "Expecting index expression") val index = parseStatement() ?: throw ScriptError(t.pos, "Expecting index expression")
cc.skipTokenOfType(Token.Type.RBRACKET, "missing ']' at the end of the list literal") cc.skipTokenOfType(Token.Type.RBRACKET, "missing ']' at the end of the list literal")
operand = Accessor({ cxt -> operand = Accessor({ cxt ->
val i = index.execute(cxt) val i = index.execute(cxt)
@ -233,7 +227,7 @@ class Compiler(
} }
} ?: run { } ?: run {
// array literal // array literal
val entries = parseArrayLiteral(cc) val entries = parseArrayLiteral()
// if it didn't throw, ot parsed ot and consumed it all // if it didn't throw, ot parsed ot and consumed it all
operand = Accessor { cxt -> operand = Accessor { cxt ->
val list = mutableListOf<Obj>() val list = mutableListOf<Obj>()
@ -263,7 +257,7 @@ class Compiler(
in stopKeywords -> { in stopKeywords -> {
if (operand != null) throw ScriptError(t.pos, "unexpected keyword") if (operand != null) throw ScriptError(t.pos, "unexpected keyword")
cc.previous() cc.previous()
val s = parseStatement(cc) ?: throw ScriptError(t.pos, "Expecting valid statement") val s = parseStatement() ?: throw ScriptError(t.pos, "Expecting valid statement")
operand = Accessor { s.execute(it).asReadonly } operand = Accessor { s.execute(it).asReadonly }
} }
@ -287,7 +281,7 @@ class Compiler(
} ?: run { } ?: run {
// variable to read or like // variable to read or like
cc.previous() cc.previous()
operand = parseAccessor(cc) operand = parseAccessor()
} }
} }
} }
@ -305,7 +299,7 @@ class Compiler(
} }
} ?: run { } ?: run {
// no lvalue means pre-increment, expression to increment follows // no lvalue means pre-increment, expression to increment follows
val next = parseAccessor(cc) ?: throw ScriptError(t.pos, "Expecting expression") val next = parseAccessor() ?: throw ScriptError(t.pos, "Expecting expression")
operand = Accessor { ctx -> operand = Accessor { ctx ->
next.getter(ctx).also { next.getter(ctx).also {
if (!it.isMutable) ctx.raiseError("Cannot increment immutable value") if (!it.isMutable) ctx.raiseError("Cannot increment immutable value")
@ -326,7 +320,7 @@ class Compiler(
} }
} ?: run { } ?: run {
// no lvalue means pre-decrement, expression to decrement follows // no lvalue means pre-decrement, expression to decrement follows
val next = parseAccessor(cc) ?: throw ScriptError(t.pos, "Expecting expression") val next = parseAccessor() ?: throw ScriptError(t.pos, "Expecting expression")
operand = Accessor { ctx -> operand = Accessor { ctx ->
next.getter(ctx).also { next.getter(ctx).also {
if (!it.isMutable) ctx.raiseError("Cannot decrement immutable value") if (!it.isMutable) ctx.raiseError("Cannot decrement immutable value")
@ -339,7 +333,7 @@ class Compiler(
// range operator // range operator
val isEndInclusive = t.type == Token.Type.DOTDOT val isEndInclusive = t.type == Token.Type.DOTDOT
val left = operand val left = operand
val right = parseExpression(cc) val right = parseExpression()
operand = Accessor { operand = Accessor {
ObjRange( ObjRange(
left?.getter?.invoke(it)?.value ?: ObjNull, left?.getter?.invoke(it)?.value ?: ObjNull,
@ -353,12 +347,11 @@ class Compiler(
operand = operand?.let { left -> operand = operand?.let { left ->
cc.previous() cc.previous()
parseFunctionCall( parseFunctionCall(
cc,
left, left,
blockArgument = true, blockArgument = true,
t.type == Token.Type.NULL_COALESCE_BLOCKINVOKE t.type == Token.Type.NULL_COALESCE_BLOCKINVOKE
) )
} ?: parseLambdaExpression(cc) } ?: parseLambdaExpression()
} }
Token.Type.RBRACKET, Token.Type.RPAREN -> { Token.Type.RBRACKET, Token.Type.RPAREN -> {
@ -369,7 +362,7 @@ class Compiler(
else -> { else -> {
cc.previous() cc.previous()
operand?.let { return it } operand?.let { return it }
operand = parseAccessor(cc) ?: return null //throw ScriptError(t.pos, "Expecting expression") operand = parseAccessor() ?: return null //throw ScriptError(t.pos, "Expecting expression")
} }
} }
} }
@ -378,14 +371,14 @@ class Compiler(
/** /**
* Parse lambda expression, leading '{' is already consumed * Parse lambda expression, leading '{' is already consumed
*/ */
private fun parseLambdaExpression(cc: CompilerContext): Accessor { private fun parseLambdaExpression(): Accessor {
// lambda args are different: // lambda args are different:
val startPos = cc.currentPos() val startPos = cc.currentPos()
val argsDeclaration = parseArgsDeclaration(cc) val argsDeclaration = parseArgsDeclaration()
if (argsDeclaration != null && argsDeclaration.endTokenType != Token.Type.ARROW) if (argsDeclaration != null && argsDeclaration.endTokenType != Token.Type.ARROW)
throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments") throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
val body = parseBlock(cc, skipLeadingBrace = true) val body = parseBlock(skipLeadingBrace = true)
var closure: Context? = null var closure: Context? = null
@ -417,7 +410,7 @@ class Compiler(
} }
} }
private fun parseArrayLiteral(cc: CompilerContext): List<ListEntry> { private fun parseArrayLiteral(): List<ListEntry> {
// it should be called after Token.Type.LBRACKET is consumed // it should be called after Token.Type.LBRACKET is consumed
val entries = mutableListOf<ListEntry>() val entries = mutableListOf<ListEntry>()
while (true) { while (true) {
@ -429,19 +422,19 @@ class Compiler(
Token.Type.RBRACKET -> return entries Token.Type.RBRACKET -> return entries
Token.Type.ELLIPSIS -> { Token.Type.ELLIPSIS -> {
parseExpressionLevel(cc)?.let { entries += ListEntry.Spread(it) } parseExpressionLevel()?.let { entries += ListEntry.Spread(it) }
} }
else -> { else -> {
cc.previous() cc.previous()
parseExpressionLevel(cc)?.let { entries += ListEntry.Element(it) } parseExpressionLevel()?.let { entries += ListEntry.Element(it) }
?: throw ScriptError(t.pos, "invalid list literal: expecting expression") ?: throw ScriptError(t.pos, "invalid list literal: expecting expression")
} }
} }
} }
} }
private fun parseScopeOperator(operand: Accessor?, cc: CompilerContext): Accessor { private fun parseScopeOperator(operand: Accessor?): Accessor {
// implement global scope maybe? // implement global scope maybe?
if (operand == null) throw ScriptError(cc.next().pos, "Expecting expression before ::") if (operand == null) throw ScriptError(cc.next().pos, "Expecting expression before ::")
val t = cc.next() val t = cc.next()
@ -459,7 +452,7 @@ class Compiler(
* Parse argument declaration, used in lambda (and later in fn too) * Parse argument declaration, used in lambda (and later in fn too)
* @return declaration or null if there is no valid list of arguments * @return declaration or null if there is no valid list of arguments
*/ */
private fun parseArgsDeclaration(cc: CompilerContext, isClassDeclaration: Boolean = false): ArgsDeclaration? { private fun parseArgsDeclaration(isClassDeclaration: Boolean = false): ArgsDeclaration? {
val result = mutableListOf<ArgsDeclaration.Item>() val result = mutableListOf<ArgsDeclaration.Item>()
var endTokenType: Token.Type? = null var endTokenType: Token.Type? = null
val startPos = cc.savePos() val startPos = cc.savePos()
@ -504,10 +497,10 @@ class Compiler(
var defaultValue: Statement? = null var defaultValue: Statement? = null
cc.ifNextIs(Token.Type.ASSIGN) { cc.ifNextIs(Token.Type.ASSIGN) {
defaultValue = parseExpression(cc) defaultValue = parseExpression()
} }
// type information // type information
val typeInfo = parseTypeDeclaration(cc) val typeInfo = parseTypeDeclaration()
val isEllipsis = cc.skipTokenOfType(Token.Type.ELLIPSIS, isOptional = true) val isEllipsis = cc.skipTokenOfType(Token.Type.ELLIPSIS, isOptional = true)
result += ArgsDeclaration.Item( result += ArgsDeclaration.Item(
t.value, t.value,
@ -554,7 +547,7 @@ class Compiler(
return ArgsDeclaration(result, endTokenType) return ArgsDeclaration(result, endTokenType)
} }
private fun parseTypeDeclaration(cc: CompilerContext): TypeDecl { private fun parseTypeDeclaration(): TypeDecl {
return if (cc.skipTokenOfType(Token.Type.COLON, isOptional = true)) { return if (cc.skipTokenOfType(Token.Type.COLON, isOptional = true)) {
val tt = cc.requireToken(Token.Type.ID, "type name or type expression required") val tt = cc.requireToken(Token.Type.ID, "type name or type expression required")
val isNullable = cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true) val isNullable = cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true)
@ -566,7 +559,7 @@ class Compiler(
* Parse arguments list during the call and detect last block argument * Parse arguments list during the call and detect last block argument
* _following the parenthesis_ call: `(1,2) { ... }` * _following the parenthesis_ call: `(1,2) { ... }`
*/ */
private fun parseArgs(cc: CompilerContext): Pair<List<ParsedArgument>, Boolean> { private fun parseArgs(): Pair<List<ParsedArgument>, Boolean> {
val args = mutableListOf<ParsedArgument>() val args = mutableListOf<ParsedArgument>()
do { do {
@ -577,16 +570,16 @@ class Compiler(
} }
Token.Type.ELLIPSIS -> { Token.Type.ELLIPSIS -> {
parseStatement(cc)?.let { args += ParsedArgument(it, t.pos, isSplat = true) } parseStatement()?.let { args += ParsedArgument(it, t.pos, isSplat = true) }
?: throw ScriptError(t.pos, "Expecting arguments list") ?: throw ScriptError(t.pos, "Expecting arguments list")
} }
else -> { else -> {
cc.previous() cc.previous()
parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) } parseExpression()?.let { args += ParsedArgument(it, t.pos) }
?: throw ScriptError(t.pos, "Expecting arguments list") ?: throw ScriptError(t.pos, "Expecting arguments list")
if (cc.current().type == Token.Type.COLON) if (cc.current().type == Token.Type.COLON)
parseTypeDeclaration(cc) parseTypeDeclaration()
// Here should be a valid termination: // Here should be a valid termination:
} }
} }
@ -597,7 +590,7 @@ class Compiler(
var lastBlockArgument = false var lastBlockArgument = false
if (end.type == Token.Type.LBRACE) { if (end.type == Token.Type.LBRACE) {
// last argument - callable // last argument - callable
val callableAccessor = parseLambdaExpression(cc) val callableAccessor = parseLambdaExpression()
args += ParsedArgument( args += ParsedArgument(
// transform accessor to the callable: // transform accessor to the callable:
statement { statement {
@ -613,7 +606,6 @@ class Compiler(
private fun parseFunctionCall( private fun parseFunctionCall(
cc: CompilerContext,
left: Accessor, left: Accessor,
blockArgument: Boolean, blockArgument: Boolean,
isOptional: Boolean isOptional: Boolean
@ -622,12 +614,12 @@ class Compiler(
var detectedBlockArgument = blockArgument var detectedBlockArgument = blockArgument
val args = if (blockArgument) { val args = if (blockArgument) {
val blockArg = ParsedArgument( val blockArg = ParsedArgument(
parseExpression(cc) parseExpression()
?: throw ScriptError(cc.currentPos(), "lambda body expected"), cc.currentPos() ?: throw ScriptError(cc.currentPos(), "lambda body expected"), cc.currentPos()
) )
listOf(blockArg) listOf(blockArg)
} else { } else {
val r = parseArgs(cc) val r = parseArgs()
detectedBlockArgument = r.second detectedBlockArgument = r.second
r.first r.first
} }
@ -647,13 +639,13 @@ class Compiler(
} }
} }
private fun parseAccessor(cc: CompilerContext): Accessor? { private fun parseAccessor(): Accessor? {
// could be: literal // could be: literal
val t = cc.next() val t = cc.next()
return when (t.type) { return when (t.type) {
Token.Type.INT, Token.Type.REAL, Token.Type.HEX -> { Token.Type.INT, Token.Type.REAL, Token.Type.HEX -> {
cc.previous() cc.previous()
val n = parseNumber(true, cc) val n = parseNumber(true)
Accessor { Accessor {
n.asReadonly n.asReadonly
} }
@ -664,12 +656,12 @@ class Compiler(
Token.Type.CHAR -> Accessor { ObjChar(t.value[0]).asReadonly } Token.Type.CHAR -> Accessor { ObjChar(t.value[0]).asReadonly }
Token.Type.PLUS -> { Token.Type.PLUS -> {
val n = parseNumber(true, cc) val n = parseNumber(true)
Accessor { n.asReadonly } Accessor { n.asReadonly }
} }
Token.Type.MINUS -> { Token.Type.MINUS -> {
val n = parseNumber(false, cc) val n = parseNumber(false)
Accessor { n.asReadonly } Accessor { n.asReadonly }
} }
@ -701,8 +693,8 @@ class Compiler(
} }
} }
private fun parseNumber(isPlus: Boolean, tokens: CompilerContext): Obj { private fun parseNumber(isPlus: Boolean): Obj {
val t = tokens.next() val t = cc.next()
return when (t.type) { return when (t.type) {
Token.Type.INT, Token.Type.HEX -> { Token.Type.INT, Token.Type.HEX -> {
val n = t.value.toLong(if (t.type == Token.Type.HEX) 16 else 10) val n = t.value.toLong(if (t.type == Token.Type.HEX) 16 else 10)
@ -724,36 +716,36 @@ class Compiler(
* Parse keyword-starting statement. * Parse keyword-starting statement.
* @return parsed statement or null if, for example. [id] is not among keywords * @return parsed statement or null if, for example. [id] is not among keywords
*/ */
private fun parseKeywordStatement(id: Token, cc: CompilerContext): Statement? = when (id.value) { private fun parseKeywordStatement(id: Token): Statement? = when (id.value) {
"val" -> parseVarDeclaration(false, Visibility.Public, cc) "val" -> parseVarDeclaration(false, Visibility.Public)
"var" -> parseVarDeclaration(true, Visibility.Public, cc) "var" -> parseVarDeclaration(true, Visibility.Public)
"while" -> parseWhileStatement(cc) "while" -> parseWhileStatement()
"do" -> parseDoWhileStatement(cc) "do" -> parseDoWhileStatement()
"for" -> parseForStatement(cc) "for" -> parseForStatement()
"break" -> parseBreakStatement(id.pos, cc) "break" -> parseBreakStatement(id.pos)
"continue" -> parseContinueStatement(id.pos, cc) "continue" -> parseContinueStatement(id.pos)
"if" -> parseIfStatement(cc) "if" -> parseIfStatement()
"class" -> parseClassDeclaration(cc, false) "class" -> parseClassDeclaration(false)
"try" -> parseTryStatement(cc) "try" -> parseTryStatement()
"throw" -> parseThrowStatement(cc) "throw" -> parseThrowStatement()
"when" -> parseWhenStatement(cc) "when" -> parseWhenStatement()
else -> { else -> {
// triples // triples
cc.previous() cc.previous()
val isExtern = cc.skipId("extern") val isExtern = cc.skipId("extern")
when { when {
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(cc, Visibility.Private, isExtern) cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration( Visibility.Private, isExtern)
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(cc, Visibility.Private, isExtern) cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration( Visibility.Private, isExtern)
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(cc, isOpen = true, isExtern = isExtern) cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration( isOpen = true, isExtern = isExtern)
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(cc, isOpen = true, isExtern = isExtern) cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration( isOpen = true, isExtern = isExtern)
cc.matchQualifiers("fun") -> parseFunctionDeclaration(cc, isOpen = false, isExtern = isExtern) cc.matchQualifiers("fun") -> parseFunctionDeclaration( isOpen = false, isExtern = isExtern)
cc.matchQualifiers("fn") -> parseFunctionDeclaration(cc, isOpen = false, isExtern = isExtern) cc.matchQualifiers("fn") -> parseFunctionDeclaration( isOpen = false, isExtern = isExtern)
cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private, cc) cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private)
cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private, cc) cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private)
cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, cc, true) cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, true)
cc.matchQualifiers("var", "open") -> parseVarDeclaration(true, Visibility.Private, cc, true) cc.matchQualifiers("var", "open") -> parseVarDeclaration(true, Visibility.Private, true)
else -> { else -> {
cc.next() cc.next()
null null
@ -764,12 +756,12 @@ class Compiler(
data class WhenCase(val condition: Statement, val block: Statement) data class WhenCase(val condition: Statement, val block: Statement)
private fun parseWhenStatement(cc: CompilerContext): Statement { private fun parseWhenStatement(): Statement {
// has a value, when(value) ? // has a value, when(value) ?
var t = cc.skipWsTokens() var t = cc.skipWsTokens()
return if (t.type == Token.Type.LPAREN) { return if (t.type == Token.Type.LPAREN) {
// when(value) // when(value)
val value = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when(value) expected") val value = parseStatement() ?: throw ScriptError(cc.currentPos(), "when(value) expected")
cc.skipTokenOfType(Token.Type.RPAREN) cc.skipTokenOfType(Token.Type.RPAREN)
t = cc.next() t = cc.next()
if (t.type != Token.Type.LBRACE) throw ScriptError(t.pos, "when { ... } expected") if (t.type != Token.Type.LBRACE) throw ScriptError(t.pos, "when { ... } expected")
@ -796,7 +788,7 @@ class Compiler(
Token.Type.NOTIN -> { Token.Type.NOTIN -> {
// we need a copy in the closure: // we need a copy in the closure:
val isIn = t.type == Token.Type.IN val isIn = t.type == Token.Type.IN
val container = parseExpression(cc) ?: throw ScriptError(cc.currentPos(), "type expected") val container = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
currentCondition += statement { currentCondition += statement {
val r = container.execute(this).contains(this, whenValue) val r = container.execute(this).contains(this, whenValue)
ObjBool(if (isIn) r else !r) ObjBool(if (isIn) r else !r)
@ -806,7 +798,7 @@ class Compiler(
Token.Type.IS, Token.Type.NOTIS -> { Token.Type.IS, Token.Type.NOTIS -> {
// we need a copy in the closure: // we need a copy in the closure:
val isIn = t.type == Token.Type.IS val isIn = t.type == Token.Type.IS
val caseType = parseExpression(cc) ?: throw ScriptError(cc.currentPos(), "type expected") val caseType = parseExpression() ?: throw ScriptError(cc.currentPos(), "type expected")
currentCondition += statement { currentCondition += statement {
val r = whenValue.isInstanceOf(caseType.execute(this)) val r = whenValue.isInstanceOf(caseType.execute(this))
ObjBool(if (isIn) r else !r) ObjBool(if (isIn) r else !r)
@ -830,11 +822,11 @@ class Compiler(
"when else block already defined" "when else block already defined"
) )
elseCase = elseCase =
parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when else block expected") parseStatement() ?: throw ScriptError(cc.currentPos(), "when else block expected")
skipParseBody = true skipParseBody = true
} else { } else {
cc.previous() cc.previous()
val x = parseExpression(cc) val x = parseExpression()
?: throw ScriptError(cc.currentPos(), "when case condition expected") ?: throw ScriptError(cc.currentPos(), "when case condition expected")
currentCondition += statement { currentCondition += statement {
ObjBool(x.execute(this).compareTo(this, whenValue) == 0) ObjBool(x.execute(this).compareTo(this, whenValue) == 0)
@ -845,7 +837,7 @@ class Compiler(
} }
// parsed conditions? // parsed conditions?
if (!skipParseBody) { if (!skipParseBody) {
val block = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when case block expected") val block = parseStatement() ?: throw ScriptError(cc.currentPos(), "when case block expected")
for (c in currentCondition) cases += WhenCase(c, block) for (c in currentCondition) cases += WhenCase(c, block)
} }
} }
@ -869,8 +861,8 @@ class Compiler(
} }
} }
private fun parseThrowStatement(cc: CompilerContext): Statement { private fun parseThrowStatement(): Statement {
val throwStatement = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "throw object expected") val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
return statement { return statement {
var errorObject = throwStatement.execute(this) var errorObject = throwStatement.execute(this)
if (errorObject is ObjString) if (errorObject is ObjString)
@ -887,8 +879,8 @@ class Compiler(
val block: Statement val block: Statement
) )
private fun parseTryStatement(cc: CompilerContext): Statement { private fun parseTryStatement(): Statement {
val body = parseBlock(cc) val body = parseBlock()
val catches = mutableListOf<CatchBlockData>() val catches = mutableListOf<CatchBlockData>()
cc.skipTokens(Token.Type.NEWLINE) cc.skipTokens(Token.Type.NEWLINE)
var t = cc.next() var t = cc.next()
@ -925,7 +917,7 @@ class Compiler(
exClassNames += "Exception" exClassNames += "Exception"
cc.skipTokenOfType(Token.Type.RPAREN) cc.skipTokenOfType(Token.Type.RPAREN)
} }
val block = parseBlock(cc) val block = parseBlock()
catches += CatchBlockData(catchVar, exClassNames, block) catches += CatchBlockData(catchVar, exClassNames, block)
cc.skipTokens(Token.Type.NEWLINE) cc.skipTokens(Token.Type.NEWLINE)
t = cc.next() t = cc.next()
@ -934,13 +926,13 @@ class Compiler(
cc.skipTokenOfType(Token.Type.LBRACE, "expected catch(...) or catch { ... } here") cc.skipTokenOfType(Token.Type.LBRACE, "expected catch(...) or catch { ... } here")
catches += CatchBlockData( catches += CatchBlockData(
Token("it", cc.currentPos(), Token.Type.ID), listOf("Exception"), Token("it", cc.currentPos(), Token.Type.ID), listOf("Exception"),
parseBlock(cc, true) parseBlock(true)
) )
t = cc.next() t = cc.next()
} }
} }
val finallyClause = if (t.value == "finally") { val finallyClause = if (t.value == "finally") {
parseBlock(cc) parseBlock()
} else { } else {
cc.previous() cc.previous()
null null
@ -991,11 +983,11 @@ class Compiler(
} }
} }
private fun parseClassDeclaration(cc: CompilerContext, isStruct: Boolean): Statement { private fun parseClassDeclaration(isStruct: Boolean): Statement {
val nameToken = cc.requireToken(Token.Type.ID) val nameToken = cc.requireToken(Token.Type.ID)
val constructorArgsDeclaration = val constructorArgsDeclaration =
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true)) if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
parseArgsDeclaration(cc, isClassDeclaration = true) parseArgsDeclaration( isClassDeclaration = true)
else null else null
if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN) if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN)
@ -1009,7 +1001,7 @@ class Compiler(
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) { val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
// parse body // parse body
parseScript(t.pos, cc).also { parseScript().also {
cc.skipTokens(Token.Type.RBRACE) cc.skipTokens(Token.Type.RBRACE)
} }
} else { } else {
@ -1052,7 +1044,7 @@ class Compiler(
} }
private fun getLabel(cc: CompilerContext, maxDepth: Int = 2): String? { private fun getLabel(maxDepth: Int = 2): String? {
var cnt = 0 var cnt = 0
var found: String? = null var found: String? = null
while (cc.hasPrevious() && cnt < maxDepth) { while (cc.hasPrevious() && cnt < maxDepth) {
@ -1067,9 +1059,9 @@ class Compiler(
return found return found
} }
private fun parseForStatement(cc: CompilerContext): Statement { private fun parseForStatement(): Statement {
val label = getLabel(cc)?.also { cc.labels += it } val label = getLabel()?.also { cc.labels += it }
val start = ensureLparen(cc) val start = ensureLparen()
val tVar = cc.next() val tVar = cc.next()
if (tVar.type != Token.Type.ID) if (tVar.type != Token.Type.ID)
@ -1077,16 +1069,16 @@ class Compiler(
val tOp = cc.next() val tOp = cc.next()
if (tOp.value == "in") { if (tOp.value == "in") {
// in loop // in loop
val source = parseStatement(cc) ?: throw ScriptError(start, "Bad for statement: expected expression") val source = parseStatement() ?: throw ScriptError(start, "Bad for statement: expected expression")
ensureRparen(cc) ensureRparen()
val (canBreak, body) = cc.parseLoop { val (canBreak, body) = cc.parseLoop {
parseStatement(cc) ?: throw ScriptError(start, "Bad for statement: expected loop body") parseStatement() ?: throw ScriptError(start, "Bad for statement: expected loop body")
} }
// possible else clause // possible else clause
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) { val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) {
parseStatement(cc) parseStatement()
} else { } else {
cc.previous() cc.previous()
null null
@ -1218,10 +1210,10 @@ class Compiler(
} }
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
private fun parseDoWhileStatement(cc: CompilerContext): Statement { private fun parseDoWhileStatement(): Statement {
val label = getLabel(cc)?.also { cc.labels += it } val label = getLabel()?.also { cc.labels += it }
val (breakFound, body) = cc.parseLoop { val (breakFound, body) = cc.parseLoop {
parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "Bad while statement: expected statement") parseStatement() ?: throw ScriptError(cc.currentPos(), "Bad while statement: expected statement")
} }
label?.also { cc.labels -= it } label?.also { cc.labels -= it }
@ -1231,14 +1223,14 @@ class Compiler(
if (t.type != Token.Type.ID && t.value != "while") if (t.type != Token.Type.ID && t.value != "while")
cc.skipTokenOfType(Token.Type.LPAREN, "expected '(' here") cc.skipTokenOfType(Token.Type.LPAREN, "expected '(' here")
val conditionStart = ensureLparen(cc) val conditionStart = ensureLparen()
val condition = val condition =
parseExpression(cc) ?: throw ScriptError(conditionStart, "Bad while statement: expected expression") parseExpression() ?: throw ScriptError(conditionStart, "Bad while statement: expected expression")
ensureRparen(cc) ensureRparen()
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) { val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) {
parseStatement(cc) parseStatement()
} else { } else {
cc.previous() cc.previous()
null null
@ -1270,19 +1262,19 @@ class Compiler(
} }
} }
private fun parseWhileStatement(cc: CompilerContext): Statement { private fun parseWhileStatement(): Statement {
val label = getLabel(cc)?.also { cc.labels += it } val label = getLabel()?.also { cc.labels += it }
val start = ensureLparen(cc) val start = ensureLparen()
val condition = val condition =
parseExpression(cc) ?: throw ScriptError(start, "Bad while statement: expected expression") parseExpression() ?: throw ScriptError(start, "Bad while statement: expected expression")
ensureRparen(cc) ensureRparen()
val body = parseStatement(cc) ?: throw ScriptError(start, "Bad while statement: expected statement") val body = parseStatement() ?: throw ScriptError(start, "Bad while statement: expected statement")
label?.also { cc.labels -= it } label?.also { cc.labels -= it }
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) { val elseStatement = if (cc.next().let { it.type == Token.Type.ID && it.value == "else" }) {
parseStatement(cc) parseStatement()
} else { } else {
cc.previous() cc.previous()
null null
@ -1312,7 +1304,7 @@ class Compiler(
} }
} }
private fun parseBreakStatement(start: Pos, cc: CompilerContext): Statement { private fun parseBreakStatement(start: Pos): Statement {
var t = cc.next() var t = cc.next()
val label = if (t.pos.line != start.line || t.type != Token.Type.ATLABEL) { val label = if (t.pos.line != start.line || t.type != Token.Type.ATLABEL) {
@ -1333,7 +1325,7 @@ class Compiler(
t.type != Token.Type.NEWLINE) t.type != Token.Type.NEWLINE)
) { ) {
// we have something on this line, could be expression // we have something on this line, could be expression
parseStatement(cc) parseStatement()
} else null } else null
cc.addBreak() cc.addBreak()
@ -1348,7 +1340,7 @@ class Compiler(
} }
} }
private fun parseContinueStatement(start: Pos, cc: CompilerContext): Statement { private fun parseContinueStatement(start: Pos): Statement {
val t = cc.next() val t = cc.next()
val label = if (t.pos.line != start.line || t.type != Token.Type.ATLABEL) { val label = if (t.pos.line != start.line || t.type != Token.Type.ATLABEL) {
@ -1370,38 +1362,38 @@ class Compiler(
} }
} }
private fun ensureRparen(tokens: CompilerContext): Pos { private fun ensureRparen(): Pos {
val t = tokens.next() val t = cc.next()
if (t.type != Token.Type.RPAREN) if (t.type != Token.Type.RPAREN)
throw ScriptError(t.pos, "expected ')'") throw ScriptError(t.pos, "expected ')'")
return t.pos return t.pos
} }
private fun ensureLparen(tokens: CompilerContext): Pos { private fun ensureLparen(): Pos {
val t = tokens.next() val t = cc.next()
if (t.type != Token.Type.LPAREN) if (t.type != Token.Type.LPAREN)
throw ScriptError(t.pos, "expected '('") throw ScriptError(t.pos, "expected '('")
return t.pos return t.pos
} }
private fun parseIfStatement(tokens: CompilerContext): Statement { private fun parseIfStatement(): Statement {
val start = ensureLparen(tokens) val start = ensureLparen()
val condition = parseExpression(tokens) val condition = parseExpression()
?: throw ScriptError(start, "Bad if statement: expected expression") ?: throw ScriptError(start, "Bad if statement: expected expression")
val pos = ensureRparen(tokens) val pos = ensureRparen()
val ifBody = parseStatement(tokens) ?: throw ScriptError(pos, "Bad if statement: expected statement") val ifBody = parseStatement() ?: throw ScriptError(pos, "Bad if statement: expected statement")
tokens.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
// could be else block: // could be else block:
val t2 = tokens.next() val t2 = cc.next()
// we generate different statements: optimization // we generate different statements: optimization
return if (t2.type == Token.Type.ID && t2.value == "else") { return if (t2.type == Token.Type.ID && t2.value == "else") {
val elseBody = val elseBody =
parseStatement(tokens) ?: throw ScriptError(pos, "Bad else statement: expected statement") parseStatement() ?: throw ScriptError(pos, "Bad else statement: expected statement")
return statement(start) { return statement(start) {
if (condition.execute(it).toBool()) if (condition.execute(it).toBool())
ifBody.execute(it) ifBody.execute(it)
@ -1409,7 +1401,7 @@ class Compiler(
elseBody.execute(it) elseBody.execute(it)
} }
} else { } else {
tokens.previous() cc.previous()
statement(start) { statement(start) {
if (condition.execute(it).toBool()) if (condition.execute(it).toBool())
ifBody.execute(it) ifBody.execute(it)
@ -1420,7 +1412,6 @@ class Compiler(
} }
private fun parseFunctionDeclaration( private fun parseFunctionDeclaration(
cc: CompilerContext,
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false, @Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
isExtern: Boolean = false isExtern: Boolean = false
@ -1446,20 +1437,20 @@ class Compiler(
if (t.type != Token.Type.LPAREN) if (t.type != Token.Type.LPAREN)
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'") throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
val argsDeclaration = parseArgsDeclaration(cc) val argsDeclaration = parseArgsDeclaration()
if (argsDeclaration == null || argsDeclaration.endTokenType != Token.Type.RPAREN) if (argsDeclaration == null || argsDeclaration.endTokenType != Token.Type.RPAREN)
throw ScriptError( throw ScriptError(
t.pos, t.pos,
"Bad function definition: expected valid argument declaration or () after 'fn ${name}'" "Bad function definition: expected valid argument declaration or () after 'fn ${name}'"
) )
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration(cc) if (cc.current().type == Token.Type.COLON) parseTypeDeclaration()
// Here we should be at open body // Here we should be at open body
val fnStatements = if (isExtern) val fnStatements = if (isExtern)
statement { raiseError("extern function not provided: $name") } statement { raiseError("extern function not provided: $name") }
else else
parseBlock(cc) parseBlock()
var closure: Context? = null var closure: Context? = null
@ -1496,14 +1487,14 @@ class Compiler(
} }
} }
private fun parseBlock(cc: CompilerContext, skipLeadingBrace: Boolean = false): Statement { private fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
val startPos = cc.currentPos() val startPos = cc.currentPos()
if (!skipLeadingBrace) { if (!skipLeadingBrace) {
val t = cc.next() val t = cc.next()
if (t.type != Token.Type.LBRACE) if (t.type != Token.Type.LBRACE)
throw ScriptError(t.pos, "Expected block body start: {") throw ScriptError(t.pos, "Expected block body start: {")
} }
val block = parseScript(startPos, cc) val block = parseScript()
return statement(startPos) { return statement(startPos) {
// block run on inner context: // block run on inner context:
block.execute(if (it.skipContextCreation) it else it.copy(startPos)) block.execute(if (it.skipContextCreation) it else it.copy(startPos))
@ -1517,27 +1508,26 @@ class Compiler(
private fun parseVarDeclaration( private fun parseVarDeclaration(
isMutable: Boolean, isMutable: Boolean,
visibility: Visibility, visibility: Visibility,
tokens: CompilerContext,
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false @Suppress("UNUSED_PARAMETER") isOpen: Boolean = false
): Statement { ): Statement {
val nameToken = tokens.next() val nameToken = cc.next()
val start = nameToken.pos val start = nameToken.pos
if (nameToken.type != Token.Type.ID) if (nameToken.type != Token.Type.ID)
throw ScriptError(nameToken.pos, "Expected identifier here") throw ScriptError(nameToken.pos, "Expected identifier here")
val name = nameToken.value val name = nameToken.value
val eqToken = tokens.next() val eqToken = cc.next()
var setNull = false var setNull = false
if (eqToken.type != Token.Type.ASSIGN) { if (eqToken.type != Token.Type.ASSIGN) {
if (!isMutable) if (!isMutable)
throw ScriptError(start, "val must be initialized") throw ScriptError(start, "val must be initialized")
else { else {
tokens.previous() cc.previous()
setNull = true setNull = true
} }
} }
val initialExpression = if (setNull) null else parseStatement(tokens, true) val initialExpression = if (setNull) null else parseStatement( true)
?: throw ScriptError(eqToken.pos, "Expected initializer expression") ?: throw ScriptError(eqToken.pos, "Expected initializer expression")
return statement(nameToken.pos) { context -> return statement(nameToken.pos) { context ->
@ -1571,6 +1561,10 @@ class Compiler(
companion object { companion object {
fun compile(source: Source): Script {
return Compiler(CompilerContext(parseLyng(source))).parseScript()
}
private var lastPriority = 0 private var lastPriority = 0
val allOps = listOf( val allOps = listOf(
// assignments, lowest priority // assignments, lowest priority
@ -1691,7 +1685,7 @@ class Compiler(
allOps.filter { it.priority == l }.associateBy { it.tokenType } allOps.filter { it.priority == l }.associateBy { it.tokenType }
} }
fun compile(code: String): Script = Compiler().compile(Source("<eval>", code)) fun compile(code: String): Script = compile(Source("<eval>", code))
/** /**
* The keywords that stop processing of expression term * The keywords that stop processing of expression term

View File

@ -1,13 +1,11 @@
package net.sergeych.lyng package net.sergeych.lyng
internal class CompilerContext(val tokens: List<Token>) { class CompilerContext(val tokens: List<Token>) {
val labels = mutableSetOf<String>() val labels = mutableSetOf<String>()
var breakFound = false var breakFound = false
private set
var loopLevel = 0 var loopLevel = 0
private set
inline fun <T> parseLoop(f: () -> T): Pair<Boolean, T> { inline fun <T> parseLoop(f: () -> T): Pair<Boolean, T> {
if (++loopLevel == 0) breakFound = false if (++loopLevel == 0) breakFound = false

View File

@ -123,7 +123,7 @@ open class Context(
fun addConst(name: String, value: Obj) = addItem(name, false, value) fun addConst(name: String, value: Obj) = addItem(name, false, value)
suspend fun eval(code: String): Obj = suspend fun eval(code: String): Obj =
Compiler().compile(code.toSource()).execute(this) Compiler.compile(code.toSource()).execute(this)
fun containsLocal(name: String): Boolean = name in objects fun containsLocal(name: String): Boolean = name in objects