int++ and int--
This commit is contained in:
parent
9ae9752634
commit
baf9eab3ba
@ -97,6 +97,10 @@ class Compiler {
|
||||
parseExpression(tokens)
|
||||
}
|
||||
}
|
||||
Token.Type.PLUS2, Token.Type.MINUS2 -> {
|
||||
tokens.previous()
|
||||
parseExpression(tokens)
|
||||
}
|
||||
|
||||
Token.Type.LABEL -> continue
|
||||
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
|
||||
@ -222,6 +226,25 @@ class Compiler {
|
||||
throw ScriptError(t.pos, "Expected identifier after '.'")
|
||||
}
|
||||
|
||||
Token.Type.PLUS2 -> {
|
||||
statement(id.pos) { context ->
|
||||
context.pos = id.pos
|
||||
val v = resolve(context).get(id.value)
|
||||
?: throw ScriptError(id.pos, "Undefined symbol: ${id.value}")
|
||||
v.value?.getAndIncrement(context)
|
||||
?: context.raiseNPE()
|
||||
}
|
||||
}
|
||||
|
||||
Token.Type.MINUS2 -> {
|
||||
statement(id.pos) { context ->
|
||||
context.pos = id.pos
|
||||
val v = resolve(context).get(id.value)
|
||||
?: throw ScriptError(id.pos, "Undefined symbol: ${id.value}")
|
||||
v.value?.getAndDecrement(context)
|
||||
?: context.raiseNPE()
|
||||
}
|
||||
}
|
||||
Token.Type.LPAREN -> {
|
||||
// function call
|
||||
// Load arg list
|
||||
@ -243,10 +266,11 @@ class Compiler {
|
||||
resolve(context).get(id.value) ?: throw ScriptError(id.pos, "Undefined function: ${id.value}")
|
||||
(v.value as? Statement)?.execute(
|
||||
context.copy(
|
||||
id.pos,
|
||||
Arguments(
|
||||
nt.pos,
|
||||
args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
?: throw ScriptError(id.pos, "Variable $id is not callable ($id)")
|
||||
@ -501,8 +525,9 @@ class Compiler {
|
||||
var closure: Context? = null
|
||||
|
||||
val fnBody = statement(t.pos) { callerContext ->
|
||||
callerContext.pos = start
|
||||
// restore closure where the function was defined:
|
||||
val context = closure ?: Context()
|
||||
val context = closure ?: callerContext.raiseError("bug: closure not set")
|
||||
// load params from caller context
|
||||
for ((i, d) in params.withIndex()) {
|
||||
if (i < callerContext.args.size)
|
||||
@ -539,7 +564,7 @@ class Compiler {
|
||||
val block = parseScript(t.pos, tokens)
|
||||
return statement(t.pos) {
|
||||
// block run on inner context:
|
||||
block.execute(it.copy())
|
||||
block.execute(it.copy(t.pos))
|
||||
}.also {
|
||||
val t1 = tokens.next()
|
||||
if (t1.type != Token.Type.RBRACE)
|
||||
|
@ -1,9 +1,27 @@
|
||||
package net.sergeych.ling
|
||||
|
||||
class Context(
|
||||
val parent: Context? = Script.defaultContext.copy(),
|
||||
val args: Arguments = Arguments.EMPTY
|
||||
val parent: Context?,
|
||||
val args: Arguments = Arguments.EMPTY,
|
||||
var pos: Pos = Pos.builtIn
|
||||
) {
|
||||
constructor(
|
||||
args: Arguments = Arguments.EMPTY,
|
||||
pos: Pos = Pos.builtIn
|
||||
)
|
||||
: this(Script.defaultContext, args, pos)
|
||||
|
||||
fun raiseNotImplemented(): Nothing = raiseError("operation not implemented")
|
||||
|
||||
fun raiseNPE(): Nothing = raiseError(ObjNullPointerError(this))
|
||||
|
||||
fun raiseError(message: String): Nothing {
|
||||
throw ExecutionError(ObjError(this, message))
|
||||
}
|
||||
|
||||
fun raiseError(obj: ObjError): Nothing {
|
||||
throw ExecutionError(obj)
|
||||
}
|
||||
|
||||
private val objects = mutableMapOf<String, StoredObj>()
|
||||
|
||||
@ -11,14 +29,20 @@ class Context(
|
||||
objects[name]
|
||||
?: parent?.get(name)
|
||||
|
||||
fun copy(args: Arguments = Arguments.EMPTY): Context = Context(this, args)
|
||||
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY): Context = Context(this, args, pos)
|
||||
|
||||
fun addItem(name: String, isMutable: Boolean, value: Obj?) {
|
||||
objects.put(name, StoredObj(name, value, isMutable))
|
||||
}
|
||||
|
||||
fun getOrCreateNamespace(name: String) =
|
||||
(objects.getOrPut(name) { StoredObj(name, ObjNamespace(name,copy()), isMutable = false) }.value as ObjNamespace)
|
||||
(objects.getOrPut(name) {
|
||||
StoredObj(
|
||||
name,
|
||||
ObjNamespace(name, copy(pos)),
|
||||
isMutable = false
|
||||
)
|
||||
}.value as ObjNamespace)
|
||||
.context
|
||||
|
||||
inline fun <reified T> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
|
||||
@ -43,7 +67,7 @@ class Context(
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> addConst(value: T,vararg names: String) {
|
||||
inline fun <reified T> addConst(value: T, vararg names: String) {
|
||||
val obj = Obj.from(value)
|
||||
for (name in names) {
|
||||
addItem(
|
||||
@ -59,8 +83,5 @@ class Context(
|
||||
|
||||
fun containsLocal(name: String): Boolean = name in objects
|
||||
|
||||
companion object {
|
||||
operator fun invoke() = Context()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ sealed class ClassDef(
|
||||
) {
|
||||
instanceLock.withLock {
|
||||
instanceMethods[name]?.let {
|
||||
if( !it.isMutable )
|
||||
if (!it.isMutable)
|
||||
throw ScriptError(pos, "existing method $name is frozen and can't be updated")
|
||||
it.value = body
|
||||
} ?: instanceMethods.put(name, Item(body, freeze))
|
||||
@ -36,7 +36,7 @@ sealed class ClassDef(
|
||||
|
||||
//suspend fun callInstanceMethod(context: Context, self: Obj,args: Arguments): Obj {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
object ObjClassDef : ClassDef("Obj")
|
||||
@ -49,6 +49,18 @@ sealed class Obj : Comparable<Obj> {
|
||||
|
||||
open val definition: ClassDef = ObjClassDef
|
||||
|
||||
open fun plus(context: Context, other: Obj): Obj {
|
||||
context.raiseNotImplemented()
|
||||
}
|
||||
|
||||
open fun getAndIncrement(context: Context): Obj {
|
||||
context.raiseNotImplemented()
|
||||
}
|
||||
|
||||
open fun getAndDecrement(context: Context): Obj {
|
||||
context.raiseNotImplemented()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
enum class Type {
|
||||
@SerialName("Void")
|
||||
@ -179,12 +191,27 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
||||
|
||||
@Serializable
|
||||
@SerialName("int")
|
||||
data class ObjInt(val value: Long) : Obj(), Numeric {
|
||||
override val asStr by lazy { ObjString(value.toString()) }
|
||||
override val longValue: Long by lazy { value }
|
||||
override val doubleValue: Double by lazy { value.toDouble() }
|
||||
override val toObjInt: ObjInt by lazy { ObjInt(value) }
|
||||
override val toObjReal: ObjReal by lazy { ObjReal(doubleValue) }
|
||||
data class ObjInt(var value: Long) : Obj(), Numeric {
|
||||
override val asStr get() = ObjString(value.toString())
|
||||
override val longValue get() = value
|
||||
override val doubleValue get() = value.toDouble()
|
||||
override val toObjInt get() = this
|
||||
override val toObjReal = ObjReal(doubleValue)
|
||||
|
||||
override fun getAndIncrement(context: Context): Obj {
|
||||
return ObjInt(value).also { value++ }
|
||||
}
|
||||
|
||||
override fun getAndDecrement(context: Context): Obj {
|
||||
return ObjInt(value).also { value-- }
|
||||
}
|
||||
|
||||
// override fun plus(context: Context, other: Obj): Obj {
|
||||
// if (other !is Numeric)
|
||||
// context.raiseError("cannot add $this with $other")
|
||||
// return if (other is ObjInt)
|
||||
//
|
||||
// }
|
||||
|
||||
override fun compareTo(other: Obj): Int {
|
||||
if (other !is Numeric) throw IllegalArgumentException("cannot compare $this with $other")
|
||||
@ -215,4 +242,13 @@ data class ObjNamespace(val name: String, val context: Context) : Obj() {
|
||||
override fun compareTo(other: Obj): Int {
|
||||
throw IllegalArgumentException("cannot compare namespaces")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class ObjError(val context: Context, val message: String) : Obj() {
|
||||
override val asStr: ObjString by lazy { ObjString("Error: $message") }
|
||||
override fun compareTo(other: Obj): Int {
|
||||
if (other === this) return 0 else return -1
|
||||
}
|
||||
}
|
||||
|
||||
class ObjNullPointerError(context: Context) : ObjError(context, "object is null")
|
||||
|
@ -50,8 +50,21 @@ private class Parser(fromPos: Pos) {
|
||||
Token("=", from, Token.Type.ASSIGN)
|
||||
}
|
||||
|
||||
'+' -> Token("+", from, Token.Type.PLUS)
|
||||
'-' -> Token("-", from, Token.Type.MINUS)
|
||||
'+' -> {
|
||||
if( currentChar == '+') {
|
||||
advance()
|
||||
Token("+", from, Token.Type.PLUS2)
|
||||
}
|
||||
else
|
||||
Token("+", from, Token.Type.PLUS)
|
||||
}
|
||||
'-' -> {
|
||||
if (currentChar == '-') {
|
||||
advance()
|
||||
Token("--", from, Token.Type.MINUS2)
|
||||
} else
|
||||
Token("-", from, Token.Type.MINUS)
|
||||
}
|
||||
'*' -> Token("*", from, Token.Type.STAR)
|
||||
'/' -> {
|
||||
if( currentChar == '/') {
|
||||
|
@ -15,10 +15,10 @@ class Script(
|
||||
return lastResult
|
||||
}
|
||||
|
||||
suspend fun execute() = execute(defaultContext.copy())
|
||||
suspend fun execute() = execute(defaultContext.copy(pos))
|
||||
|
||||
companion object {
|
||||
val defaultContext: Context = Context(null).apply {
|
||||
val defaultContext: Context = Context().apply {
|
||||
addFn("println") {
|
||||
print("yn: ")
|
||||
for( (i,a) in args.withIndex() ) {
|
||||
|
@ -1,9 +1,13 @@
|
||||
@file:Suppress("CanBeParameter")
|
||||
|
||||
package net.sergeych.ling
|
||||
|
||||
class ScriptError(val pos: Pos, val errorMessage: String) : Exception(
|
||||
open class ScriptError(val pos: Pos, val errorMessage: String) : Exception(
|
||||
"""
|
||||
$pos: Error: $errorMessage
|
||||
${pos.currentLine}
|
||||
${"-".repeat(pos.column)}^
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
class ExecutionError(val errorObject: ObjError) : ScriptError(errorObject.context.pos, errorObject.message)
|
||||
|
@ -8,6 +8,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
|
||||
ID, INT, REAL, HEX, STRING, LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET, COMMA,
|
||||
SEMICOLON, COLON,
|
||||
PLUS, MINUS, STAR, SLASH, ASSIGN,
|
||||
PLUS2, MINUS2,
|
||||
EQ, NEQ, LT, LTE, GT, GTE,
|
||||
AND, BITAND, OR, BITOR, NOT, DOT, ARROW, QUESTION, COLONCOLON, PERCENT,
|
||||
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
|
||||
|
@ -117,7 +117,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun varsAndConstsTest() = runTest {
|
||||
val context = Context()
|
||||
val context = Context(pos = Pos.builtIn)
|
||||
assertEquals(
|
||||
ObjVoid, context.eval(
|
||||
"""
|
||||
@ -137,7 +137,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun functionTest() = runTest {
|
||||
val context = Context()
|
||||
val context = Context(pos = Pos.builtIn)
|
||||
context.eval(
|
||||
"""
|
||||
fun foo(a, b) {
|
||||
@ -163,7 +163,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun simpleClosureTest() = runTest {
|
||||
val context = Context()
|
||||
val context = Context(pos = Pos.builtIn)
|
||||
context.eval(
|
||||
"""
|
||||
var global = 10
|
||||
@ -180,7 +180,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun nullAndVoidTest() = runTest {
|
||||
val context = Context()
|
||||
val context = Context(pos = Pos.builtIn)
|
||||
assertEquals(ObjVoid, context.eval("void"))
|
||||
assertEquals(ObjNull, context.eval("null"))
|
||||
}
|
||||
@ -252,7 +252,7 @@ class ScriptTest {
|
||||
@Test
|
||||
fun ifTest() = runTest {
|
||||
// if - single line
|
||||
var context = Context()
|
||||
var context = Context(pos = Pos.builtIn)
|
||||
context.eval(
|
||||
"""
|
||||
fn test1(n) {
|
||||
@ -267,7 +267,7 @@ class ScriptTest {
|
||||
assertEquals("more", context.eval("test1(1)").toString())
|
||||
|
||||
// if - multiline (block)
|
||||
context = Context()
|
||||
context = Context(pos = Pos.builtIn)
|
||||
context.eval(
|
||||
"""
|
||||
fn test1(n) {
|
||||
@ -286,7 +286,7 @@ class ScriptTest {
|
||||
assertEquals("answer: more", context.eval("test1(1)").toString())
|
||||
|
||||
// else single line1
|
||||
context = Context()
|
||||
context = Context(pos = Pos.builtIn)
|
||||
context.eval(
|
||||
"""
|
||||
fn test1(n) {
|
||||
@ -301,7 +301,7 @@ class ScriptTest {
|
||||
assertEquals("more", context.eval("test1(1)").toString())
|
||||
|
||||
// if/else with blocks
|
||||
context = Context()
|
||||
context = Context(pos = Pos.builtIn )
|
||||
context.eval(
|
||||
"""
|
||||
fn test1(n) {
|
||||
@ -443,4 +443,86 @@ class ScriptTest {
|
||||
.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncr() = runTest {
|
||||
val c = Context()
|
||||
c.eval("var x = 10")
|
||||
assertEquals(10, c.eval("x++").toInt())
|
||||
assertEquals(11, c.eval("x++").toInt())
|
||||
assertEquals(12, c.eval("x").toInt())
|
||||
|
||||
assertEquals(12, c.eval("x").toInt())
|
||||
assertEquals(12, c.eval("x").toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecr() = runTest {
|
||||
val c = Context()
|
||||
c.eval("var x = 9")
|
||||
assertEquals(9, c.eval("x--").toInt())
|
||||
assertEquals(8, c.eval("x--").toInt())
|
||||
assertEquals(7, c.eval("x--").toInt())
|
||||
assertEquals(6, c.eval("x--").toInt())
|
||||
assertEquals(5, c.eval("x").toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecrIncr() = runTest {
|
||||
val c = Context()
|
||||
c.eval("var x = 9")
|
||||
assertEquals(9, c.eval("x++").toInt())
|
||||
assertEquals(10, c.eval("x++").toInt())
|
||||
assertEquals(11, c.eval("x").toInt())
|
||||
assertEquals(11, c.eval("x--").toInt())
|
||||
assertEquals(10, c.eval("x--").toInt())
|
||||
assertEquals(9, c.eval("x--").toInt())
|
||||
assertEquals(8, c.eval("x--").toInt())
|
||||
assertEquals(7, c.eval("x + 0").toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecrIncr2() = runTest {
|
||||
val c = Context()
|
||||
c.eval("var x = 9")
|
||||
assertEquals(9, c.eval("x--").toInt())
|
||||
assertEquals(8, c.eval("x--").toInt())
|
||||
assertEquals(7, c.eval("x--").toInt())
|
||||
assertEquals(6, c.eval("x").toInt())
|
||||
assertEquals(6, c.eval("x++").toInt())
|
||||
assertEquals(7, c.eval("x++").toInt())
|
||||
assertEquals(8, c.eval("x")
|
||||
.also {
|
||||
println("${it.toDouble()} ${it.toInt()} ${it.toLong()} ${it.toInt()}")
|
||||
}
|
||||
.toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDecrIncr3() = runTest {
|
||||
val c = Context()
|
||||
c.eval("var x = 9")
|
||||
assertEquals(9, c.eval("x++").toInt())
|
||||
assertEquals(10, c.eval("x++").toInt())
|
||||
assertEquals(11, c.eval("x++").toInt())
|
||||
assertEquals(12, c.eval("x").toInt())
|
||||
assertEquals(12, c.eval("x--").toInt())
|
||||
assertEquals(11, c.eval("x").toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrAndDecr() = runTest {
|
||||
val c = Context()
|
||||
assertEquals( "8", c.eval("""
|
||||
var x = 5
|
||||
x--
|
||||
x--
|
||||
x++
|
||||
x * 2
|
||||
""").toString())
|
||||
|
||||
assertEquals( "4", c.eval("x").toString())
|
||||
// assertEquals( "8", c.eval("x*2").toString())
|
||||
// assertEquals( "4", c.eval("x+0").toString())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user