+if/else statement
+// comments
This commit is contained in:
1 parent
60db9f3b95
commit
0e065e0d7c
6 files changed
+311
-5
No files matched your search
@@ -88,8 +88,15 @@ class Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
|
||||
|
||||
Token.Type.SEMICOLON -> continue
|
||||
|
||||
Token.Type.LBRACE -> {
|
||||
tokens.previous()
|
||||
parseBlock(tokens)
|
||||
}
|
||||
|
||||
Token.Type.RBRACE -> {
|
||||
tokens.previous()
|
||||
return null
|
||||
@@ -276,9 +283,49 @@ class Compiler {
|
||||
"val" -> parseVarDeclaration(id.value, false, tokens)
|
||||
"var" -> parseVarDeclaration(id.value, true, tokens)
|
||||
"fn", "fun" -> parseFunctionDeclaration(tokens)
|
||||
"if" -> parseIfStatement(tokens)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun parseIfStatement(tokens: ListIterator<Token>): Statement {
|
||||
var t = tokens.next()
|
||||
val start = t.pos
|
||||
if( t.type != Token.Type.LPAREN)
|
||||
throw ScriptError(t.pos, "Bad if statement: expected '('")
|
||||
|
||||
val condition = parseExpression(tokens)
|
||||
?: throw ScriptError(t.pos, "Bad if statement: expected expression")
|
||||
|
||||
t = tokens.next()
|
||||
if( t.type != Token.Type.RPAREN)
|
||||
throw ScriptError(t.pos, "Bad if statement: expected ')' after condition expression")
|
||||
|
||||
val ifBody = parseStatement(tokens) ?: throw ScriptError(t.pos, "Bad if statement: expected statement")
|
||||
|
||||
// could be else block:
|
||||
val t2 = tokens.next()
|
||||
|
||||
// we generate different statements: optimization
|
||||
return if( t2.type == Token.Type.ID && t2.value == "else") {
|
||||
val elseBody = parseStatement(tokens) ?: throw ScriptError(t.pos, "Bad else statement: expected statement")
|
||||
return statement(start) {
|
||||
if (condition.execute(it).toBool())
|
||||
ifBody.execute(it)
|
||||
else
|
||||
elseBody.execute(it)
|
||||
}
|
||||
}
|
||||
else {
|
||||
tokens.previous()
|
||||
statement(start) {
|
||||
if (condition.execute(it).toBool())
|
||||
ifBody.execute(it)
|
||||
else
|
||||
ObjVoid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class FnParamDef(
|
||||
val name: String,
|
||||
val pos: Pos,
|
||||
@@ -317,8 +364,6 @@ class Compiler {
|
||||
params.add(FnParamDef(t.value, t.pos, defaultValue))
|
||||
} while (true)
|
||||
|
||||
println("arglist: $params")
|
||||
|
||||
// Here we should be at open body
|
||||
val fnStatements = parseBlock(tokens)
|
||||
|
||||
@@ -351,7 +396,11 @@ class Compiler {
|
||||
val t = tokens.next()
|
||||
if (t.type != Token.Type.LBRACE)
|
||||
throw ScriptError(t.pos, "Expected block body start: {")
|
||||
return parseScript(t.pos, tokens).also {
|
||||
val block = parseScript(t.pos, tokens)
|
||||
return statement(t.pos) {
|
||||
// block run on inner context:
|
||||
block.execute(it.copy())
|
||||
}.also {
|
||||
val t1 = tokens.next()
|
||||
if (t1.type != Token.Type.RBRACE)
|
||||
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
||||
@@ -367,7 +416,7 @@ class Compiler {
|
||||
var setNull = false
|
||||
if (eqToken.type != Token.Type.ASSIGN) {
|
||||
if (!mutable)
|
||||
throw ScriptError(eqToken.pos, "Expected initializator: '=' after '$kind ${name}'")
|
||||
throw ScriptError(eqToken.pos, "Expected initializer: '=' after '$kind ${name}'")
|
||||
else {
|
||||
tokens.previous()
|
||||
setNull = true
|
||||
|
||||
@@ -52,7 +52,14 @@ private class Parser(fromPos: Pos) {
|
||||
'+' -> Token("+", from, Token.Type.PLUS)
|
||||
'-' -> Token("-", from, Token.Type.MINUS)
|
||||
'*' -> Token("*", from, Token.Type.STAR)
|
||||
'/' -> Token("/", from, Token.Type.SLASH)
|
||||
'/' -> {
|
||||
if( currentChar == '/') {
|
||||
advance()
|
||||
Token(loadToEnd().trim(), from, Token.Type.SINLGE_LINE_COMMENT)
|
||||
}
|
||||
else
|
||||
Token("/", from, Token.Type.SLASH)
|
||||
}
|
||||
'%' -> Token("%", from, Token.Type.PERCENT)
|
||||
'.' -> Token(".", from, Token.Type.DOT)
|
||||
'<' -> {
|
||||
@@ -206,6 +213,31 @@ private class Parser(fromPos: Pos) {
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun loadUntil(endChars: Set<Char>): String {
|
||||
return if (pos.end) ""
|
||||
else {
|
||||
val result = StringBuilder()
|
||||
while (!pos.end) {
|
||||
val ch = pos.currentChar
|
||||
if (ch in endChars) break
|
||||
result.append(ch)
|
||||
pos.advance()
|
||||
}
|
||||
result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadToEnd(): String {
|
||||
val result = StringBuilder()
|
||||
val l = pos.line
|
||||
do {
|
||||
result.append(pos.currentChar)
|
||||
advance()
|
||||
} while (pos.line == l)
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* next non-whitespace char (newline are skipped too) or null if EOF
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
|
||||
PLUS, MINUS, STAR, SLASH, ASSIGN,
|
||||
EQ, NEQ, LT, LTE, GT, GTE,
|
||||
AND, BITAND, OR, BITOR, NOT, DOT, ARROW, QUESTION, COLONCOLON, PERCENT,
|
||||
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
|
||||
EOF,
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,66 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun ifTest() = runTest {
|
||||
// if - single line
|
||||
var context = Context()
|
||||
context.eval("""
|
||||
fn test1(n) {
|
||||
var result = "more"
|
||||
if( n >= 10 )
|
||||
result = "enough"
|
||||
result
|
||||
}
|
||||
""".trimIndent())
|
||||
assertEquals("enough", context.eval("test1(11)").toString())
|
||||
assertEquals("more", context.eval("test1(1)").toString())
|
||||
|
||||
// if - multiline (block)
|
||||
context = Context()
|
||||
context.eval("""
|
||||
fn test1(n) {
|
||||
var prefix = "answer: "
|
||||
var result = "more"
|
||||
if( n >= 10 ) {
|
||||
var prefix = "bad:" // local prefix
|
||||
prefix = "too bad:"
|
||||
result = "enough"
|
||||
}
|
||||
prefix + result
|
||||
}
|
||||
""".trimIndent())
|
||||
assertEquals("answer: enough", context.eval("test1(11)").toString())
|
||||
assertEquals("answer: more", context.eval("test1(1)").toString())
|
||||
|
||||
// else single line1
|
||||
context = Context()
|
||||
context.eval("""
|
||||
fn test1(n) {
|
||||
if( n >= 10 )
|
||||
"enough"
|
||||
else
|
||||
"more"
|
||||
}
|
||||
""".trimIndent())
|
||||
assertEquals("enough", context.eval("test1(11)").toString())
|
||||
assertEquals("more", context.eval("test1(1)").toString())
|
||||
|
||||
// if/else with blocks
|
||||
context = Context()
|
||||
context.eval("""
|
||||
fn test1(n) {
|
||||
if( n > 20 ) {
|
||||
"too much"
|
||||
} else if( n >= 10 ) {
|
||||
"enough"
|
||||
}
|
||||
else {
|
||||
"more"
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
assertEquals("enough", context.eval("test1(11)").toString())
|
||||
assertEquals("more", context.eval("test1(1)").toString())
|
||||
assertEquals("too much", context.eval("test1(100)").toString())
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user