Compare commits
2 Commits
a8067d0a6b
...
ea2cf1f373
Author | SHA1 | Date | |
---|---|---|---|
ea2cf1f373 | |||
eee6d75587 |
34
docs/OOP.md
34
docs/OOP.md
@ -160,6 +160,40 @@ For example, for our class Point:
|
|||||||
Point(1,1+1)
|
Point(1,1+1)
|
||||||
>>> Point(x=1,y=2)
|
>>> Point(x=1,y=2)
|
||||||
|
|
||||||
|
# Extending classes
|
||||||
|
|
||||||
|
It sometimes happen that the class is missing some particular functionality that can be _added to it_ without rewriting its inner logic and using its private state. In this case _extension methods_ could be used, for example. we want to create an extension method
|
||||||
|
that would test if some object of unknown type contains something that can be interpreted
|
||||||
|
as an integer. In this case we _extend_ class `Object`, as it is the parent class for any instance of any type:
|
||||||
|
|
||||||
|
fun Object.isInteger() {
|
||||||
|
when(this) {
|
||||||
|
// already Int?
|
||||||
|
is Int -> true
|
||||||
|
|
||||||
|
// real, but with no declimal part?
|
||||||
|
is Real -> toInt() == this
|
||||||
|
|
||||||
|
// string with int or real reuusig code above
|
||||||
|
is String -> toReal().isInteger()
|
||||||
|
|
||||||
|
// otherwise, no:
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's test:
|
||||||
|
assert( 12.isInteger() == true )
|
||||||
|
assert( 12.1.isInteger() == false )
|
||||||
|
assert( "5".isInteger() )
|
||||||
|
assert( ! "5.2".isInteger() )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
__Important note__ as for version 0.6.9, extensions are in __global scope__. It means, that once applied to a global type (Int in our sample), they will be available for _all_ contexts, even new created,
|
||||||
|
as they are modifying the type, not the context.
|
||||||
|
|
||||||
|
Beware of it. We might need to reconsider it later.
|
||||||
|
|
||||||
# Theory
|
# Theory
|
||||||
|
|
||||||
## Basic principles:
|
## Basic principles:
|
||||||
|
16
docs/Real.md
16
docs/Real.md
@ -15,11 +15,11 @@ you can use it's class to ensure type:
|
|||||||
|
|
||||||
## Member functions
|
## Member functions
|
||||||
|
|
||||||
| name | meaning | type |
|
| name | meaning | type |
|
||||||
|-----------------|------------------------------------|------|
|
|-----------------|-------------------------------------------------------------|------|
|
||||||
| `.roundToInt()` | round to nearest int like round(x) | Int |
|
| `.roundToInt()` | round to nearest int like round(x) | Int |
|
||||||
| | | |
|
| `.toInt()` | convert integer part of real to `Int` dropping decimal part | Int |
|
||||||
| | | |
|
| | | |
|
||||||
| | | |
|
| | | |
|
||||||
| | | |
|
| | | |
|
||||||
| | | |
|
| | | |
|
||||||
|
@ -1214,6 +1214,9 @@ Typical set of String functions includes:
|
|||||||
| [Range] | substring at range |
|
| [Range] | substring at range |
|
||||||
| s1 + s2 | concatenation |
|
| s1 + s2 | concatenation |
|
||||||
| s1 += s2 | self-modifying concatenation |
|
| s1 += s2 | self-modifying concatenation |
|
||||||
|
| toReal() | attempts to parse string as a Real value |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.6.8-SNAPSHOT"
|
version = "0.6.9-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -83,8 +83,7 @@ class Compiler(
|
|||||||
private fun parseExpressionLevel(tokens: CompilerContext, level: Int = 0): Accessor? {
|
private fun parseExpressionLevel(tokens: CompilerContext, level: Int = 0): Accessor? {
|
||||||
if (level == lastLevel)
|
if (level == lastLevel)
|
||||||
return parseTerm(tokens)
|
return parseTerm(tokens)
|
||||||
var lvalue = parseExpressionLevel(tokens, level + 1)
|
var lvalue: Accessor? = parseExpressionLevel(tokens, level + 1) ?: return null
|
||||||
if (lvalue == null) return null
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ class Compiler(
|
|||||||
Token.Type.DOT, Token.Type.NULL_COALESCE -> {
|
Token.Type.DOT, Token.Type.NULL_COALESCE -> {
|
||||||
val isOptional = t.type == Token.Type.NULL_COALESCE
|
val isOptional = t.type == Token.Type.NULL_COALESCE
|
||||||
operand?.let { left ->
|
operand?.let { left ->
|
||||||
// dotcall: calling method on the operand, if next is ID, "("
|
// dot call: calling method on the operand, if next is ID, "("
|
||||||
var isCall = false
|
var isCall = false
|
||||||
val next = cc.next()
|
val next = cc.next()
|
||||||
if (next.type == Token.Type.ID) {
|
if (next.type == Token.Type.ID) {
|
||||||
@ -154,7 +153,6 @@ class Compiler(
|
|||||||
|
|
||||||
|
|
||||||
Token.Type.LBRACE, Token.Type.NULL_COALESCE_BLOCKINVOKE -> {
|
Token.Type.LBRACE, Token.Type.NULL_COALESCE_BLOCKINVOKE -> {
|
||||||
// isOptional = nt.type == Token.Type.NULL_COALESCE_BLOCKINVOKE
|
|
||||||
// single lambda arg, like assertTrows { ... }
|
// single lambda arg, like assertTrows { ... }
|
||||||
cc.next()
|
cc.next()
|
||||||
isCall = true
|
isCall = true
|
||||||
@ -300,12 +298,12 @@ class Compiler(
|
|||||||
operand?.let { left ->
|
operand?.let { left ->
|
||||||
// post increment
|
// post increment
|
||||||
left.setter(startPos)
|
left.setter(startPos)
|
||||||
operand = Accessor({ cxt ->
|
operand = Accessor { cxt ->
|
||||||
val x = left.getter(cxt)
|
val x = left.getter(cxt)
|
||||||
if (x.isMutable)
|
if (x.isMutable)
|
||||||
x.value.getAndIncrement(cxt).asReadonly
|
x.value.getAndIncrement(cxt).asReadonly
|
||||||
else cxt.raiseError("Cannot increment immutable value")
|
else cxt.raiseError("Cannot increment immutable value")
|
||||||
})
|
}
|
||||||
} ?: 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(cc) ?: throw ScriptError(t.pos, "Expecting expression")
|
||||||
@ -421,7 +419,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseArrayLiteral(cc: CompilerContext): List<ListEntry> {
|
private fun parseArrayLiteral(cc: CompilerContext): List<ListEntry> {
|
||||||
// it should be called after 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) {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
@ -554,8 +552,6 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// arg list is valid:
|
|
||||||
checkNotNull(endTokenType)
|
|
||||||
return ArgsDeclaration(result, endTokenType)
|
return ArgsDeclaration(result, endTokenType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,10 +683,10 @@ class Compiler(
|
|||||||
else -> {
|
else -> {
|
||||||
Accessor({
|
Accessor({
|
||||||
it.pos = t.pos
|
it.pos = t.pos
|
||||||
it.get(t.value)
|
it[t.value]
|
||||||
?: it.raiseError("symbol not defined: '${t.value}'")
|
?: it.raiseError("symbol not defined: '${t.value}'")
|
||||||
}) { ctx, newValue ->
|
}) { ctx, newValue ->
|
||||||
ctx.get(t.value)?.let { stored ->
|
ctx[t.value]?.let { stored ->
|
||||||
ctx.pos = t.pos
|
ctx.pos = t.pos
|
||||||
if (stored.isMutable)
|
if (stored.isMutable)
|
||||||
stored.value = newValue
|
stored.value = newValue
|
||||||
@ -935,7 +931,7 @@ class Compiler(
|
|||||||
cc.skipTokens(Token.Type.NEWLINE)
|
cc.skipTokens(Token.Type.NEWLINE)
|
||||||
t = cc.next()
|
t = cc.next()
|
||||||
} else {
|
} else {
|
||||||
// no (e: Exception) block: should be shortest variant `catch { ... }`
|
// no (e: Exception) block: should be the shortest variant `catch { ... }`
|
||||||
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"),
|
||||||
@ -1098,8 +1094,8 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return statement(body.pos) {
|
return statement(body.pos) { ctx ->
|
||||||
val forContext = it.copy(start)
|
val forContext = ctx.copy(start)
|
||||||
|
|
||||||
// loop var: StoredObject
|
// loop var: StoredObject
|
||||||
val loopSO = forContext.addItem(tVar.value, true, ObjNull)
|
val loopSO = forContext.addItem(tVar.value, true, ObjNull)
|
||||||
@ -1155,7 +1151,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!breakCaught && elseStatement != null) {
|
if (!breakCaught && elseStatement != null) {
|
||||||
result = elseStatement.execute(it)
|
result = elseStatement.execute(ctx)
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -1432,11 +1428,22 @@ class Compiler(
|
|||||||
): Statement {
|
): Statement {
|
||||||
var t = cc.next()
|
var t = cc.next()
|
||||||
val start = t.pos
|
val start = t.pos
|
||||||
val name = if (t.type != Token.Type.ID)
|
var extTypeName: String? = null
|
||||||
throw ScriptError(t.pos, "Expected identifier after 'fn'")
|
var name = if (t.type != Token.Type.ID)
|
||||||
|
throw ScriptError(t.pos, "Expected identifier after 'fun'")
|
||||||
else t.value
|
else t.value
|
||||||
|
|
||||||
t = cc.next()
|
t = cc.next()
|
||||||
|
// Is extension?
|
||||||
|
if( t.type == Token.Type.DOT) {
|
||||||
|
extTypeName = name
|
||||||
|
t = cc.next()
|
||||||
|
if( t.type != Token.Type.ID)
|
||||||
|
throw ScriptError(t.pos, "illegal extension format: expected function name")
|
||||||
|
name = t.value
|
||||||
|
t = cc.next()
|
||||||
|
}
|
||||||
|
|
||||||
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}'")
|
||||||
|
|
||||||
@ -1465,13 +1472,25 @@ class Compiler(
|
|||||||
|
|
||||||
// load params from caller context
|
// load params from caller context
|
||||||
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
|
||||||
|
if( extTypeName != null ) {
|
||||||
|
context.thisObj = callerContext.thisObj
|
||||||
|
}
|
||||||
fnStatements.execute(context)
|
fnStatements.execute(context)
|
||||||
}
|
}
|
||||||
return statement(start) { context ->
|
return statement(start) { context ->
|
||||||
// we added fn in the context. now we must save closure
|
// we added fn in the context. now we must save closure
|
||||||
// for the function
|
// for the function
|
||||||
closure = context
|
closure = context
|
||||||
context.addItem(name, false, fnBody, visibility)
|
extTypeName?.let { typeName ->
|
||||||
|
// class extension method
|
||||||
|
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
|
||||||
|
if( type !is ObjClass ) context.raiseClassCastError("$typeName is not the class instance")
|
||||||
|
type.addFn( name, isOpen = true) {
|
||||||
|
fnBody.execute(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// regular function/method
|
||||||
|
?: context.addItem(name, false, fnBody, visibility)
|
||||||
// as the function can be called from anywhere, we have
|
// as the function can be called from anywhere, we have
|
||||||
// saved the proper context in the closure
|
// saved the proper context in the closure
|
||||||
fnBody
|
fnBody
|
||||||
@ -1526,7 +1545,7 @@ class Compiler(
|
|||||||
if (context.containsLocal(name))
|
if (context.containsLocal(name))
|
||||||
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
throw ScriptError(nameToken.pos, "Variable $name is already defined")
|
||||||
|
|
||||||
// init value could be a val; when we init by-value type var with it, we need to
|
// init value could be a val; when we initialize by-value type var with it, we need to
|
||||||
// create a separate copy:
|
// create a separate copy:
|
||||||
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
||||||
|
|
||||||
@ -1544,19 +1563,19 @@ class Compiler(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun simple(tokenType: Token.Type, priority: Int, f: suspend (Context, Obj, Obj) -> Obj): Operator =
|
fun simple(tokenType: Token.Type, priority: Int, f: suspend (Context, Obj, Obj) -> Obj): Operator =
|
||||||
Operator(tokenType, priority, 2, { _: Pos, a: Accessor, b: Accessor ->
|
Operator(tokenType, priority, 2) { _: Pos, a: Accessor, b: Accessor ->
|
||||||
Accessor { f(it, a.getter(it).value, b.getter(it).value).asReadonly }
|
Accessor { f(it, a.getter(it).value, b.getter(it).value).asReadonly }
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private var lastPrty = 0
|
private var lastPriority = 0
|
||||||
val allOps = listOf(
|
val allOps = listOf(
|
||||||
// assignments, lowest priority
|
// assignments, lowest priority
|
||||||
Operator(Token.Type.ASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.ASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val value = b.getter(it).value
|
val value = b.getter(it).value
|
||||||
val access = a.getter(it)
|
val access = a.getter(it)
|
||||||
@ -1566,7 +1585,7 @@ class Compiler(
|
|||||||
value.asReadonly
|
value.asReadonly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operator(Token.Type.PLUSASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.PLUSASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val x = a.getter(it).value
|
val x = a.getter(it).value
|
||||||
val y = b.getter(it).value
|
val y = b.getter(it).value
|
||||||
@ -1577,7 +1596,7 @@ class Compiler(
|
|||||||
}).asReadonly
|
}).asReadonly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operator(Token.Type.MINUSASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.MINUSASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val x = a.getter(it).value
|
val x = a.getter(it).value
|
||||||
val y = b.getter(it).value
|
val y = b.getter(it).value
|
||||||
@ -1588,7 +1607,7 @@ class Compiler(
|
|||||||
}).asReadonly
|
}).asReadonly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operator(Token.Type.STARASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.STARASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val x = a.getter(it).value
|
val x = a.getter(it).value
|
||||||
val y = b.getter(it).value
|
val y = b.getter(it).value
|
||||||
@ -1600,7 +1619,7 @@ class Compiler(
|
|||||||
}).asReadonly
|
}).asReadonly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operator(Token.Type.SLASHASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.SLASHASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val x = a.getter(it).value
|
val x = a.getter(it).value
|
||||||
val y = b.getter(it).value
|
val y = b.getter(it).value
|
||||||
@ -1611,7 +1630,7 @@ class Compiler(
|
|||||||
}).asReadonly
|
}).asReadonly
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Operator(Token.Type.PERCENTASSIGN, lastPrty) { pos, a, b ->
|
Operator(Token.Type.PERCENTASSIGN, lastPriority) { pos, a, b ->
|
||||||
Accessor {
|
Accessor {
|
||||||
val x = a.getter(it).value
|
val x = a.getter(it).value
|
||||||
val y = b.getter(it).value
|
val y = b.getter(it).value
|
||||||
@ -1623,42 +1642,42 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// logical 1
|
// logical 1
|
||||||
Operator.simple(Token.Type.OR, ++lastPrty) { ctx, a, b -> a.logicalOr(ctx, b) },
|
Operator.simple(Token.Type.OR, ++lastPriority) { ctx, a, b -> a.logicalOr(ctx, b) },
|
||||||
// logical 2
|
// logical 2
|
||||||
Operator.simple(Token.Type.AND, ++lastPrty) { ctx, a, b -> a.logicalAnd(ctx, b) },
|
Operator.simple(Token.Type.AND, ++lastPriority) { ctx, a, b -> a.logicalAnd(ctx, b) },
|
||||||
// bitwise or 2
|
// bitwise or 2
|
||||||
// bitwise and 3
|
// bitwise and 3
|
||||||
// equality/ne 4
|
// equality/not equality 4
|
||||||
Operator.simple(Token.Type.EQARROW, ++lastPrty) { c, a, b -> ObjMapEntry(a, b) },
|
Operator.simple(Token.Type.EQARROW, ++lastPriority) { _, a, b -> ObjMapEntry(a, b) },
|
||||||
//
|
//
|
||||||
Operator.simple(Token.Type.EQ, ++lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) == 0) },
|
Operator.simple(Token.Type.EQ, ++lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) == 0) },
|
||||||
Operator.simple(Token.Type.NEQ, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) != 0) },
|
Operator.simple(Token.Type.NEQ, lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) != 0) },
|
||||||
Operator.simple(Token.Type.REF_EQ, lastPrty) { _, a, b -> ObjBool(a === b) },
|
Operator.simple(Token.Type.REF_EQ, lastPriority) { _, a, b -> ObjBool(a === b) },
|
||||||
Operator.simple(Token.Type.REF_NEQ, lastPrty) { _, a, b -> ObjBool(a !== b) },
|
Operator.simple(Token.Type.REF_NEQ, lastPriority) { _, a, b -> ObjBool(a !== b) },
|
||||||
// relational <=,... 5
|
// relational <=,... 5
|
||||||
Operator.simple(Token.Type.LTE, ++lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) <= 0) },
|
Operator.simple(Token.Type.LTE, ++lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) <= 0) },
|
||||||
Operator.simple(Token.Type.LT, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) < 0) },
|
Operator.simple(Token.Type.LT, lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) < 0) },
|
||||||
Operator.simple(Token.Type.GTE, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) >= 0) },
|
Operator.simple(Token.Type.GTE, lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) >= 0) },
|
||||||
Operator.simple(Token.Type.GT, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) > 0) },
|
Operator.simple(Token.Type.GT, lastPriority) { c, a, b -> ObjBool(a.compareTo(c, b) > 0) },
|
||||||
// in, is:
|
// in, is:
|
||||||
Operator.simple(Token.Type.IN, lastPrty) { c, a, b -> ObjBool(b.contains(c, a)) },
|
Operator.simple(Token.Type.IN, lastPriority) { c, a, b -> ObjBool(b.contains(c, a)) },
|
||||||
Operator.simple(Token.Type.NOTIN, lastPrty) { c, a, b -> ObjBool(!b.contains(c, a)) },
|
Operator.simple(Token.Type.NOTIN, lastPriority) { c, a, b -> ObjBool(!b.contains(c, a)) },
|
||||||
Operator.simple(Token.Type.IS, lastPrty) { c, a, b -> ObjBool(a.isInstanceOf(b)) },
|
Operator.simple(Token.Type.IS, lastPriority) { _, a, b -> ObjBool(a.isInstanceOf(b)) },
|
||||||
Operator.simple(Token.Type.NOTIS, lastPrty) { c, a, b -> ObjBool(!a.isInstanceOf(b)) },
|
Operator.simple(Token.Type.NOTIS, lastPriority) { _, a, b -> ObjBool(!a.isInstanceOf(b)) },
|
||||||
|
|
||||||
Operator.simple(Token.Type.ELVIS, ++lastPrty) { c, a, b -> if (a == ObjNull) b else a },
|
Operator.simple(Token.Type.ELVIS, ++lastPriority) { _, a, b -> if (a == ObjNull) b else a },
|
||||||
|
|
||||||
// shuttle <=> 6
|
// shuttle <=> 6
|
||||||
Operator.simple(Token.Type.SHUTTLE, ++lastPrty) { c, a, b ->
|
Operator.simple(Token.Type.SHUTTLE, ++lastPriority) { c, a, b ->
|
||||||
ObjInt(a.compareTo(c, b).toLong())
|
ObjInt(a.compareTo(c, b).toLong())
|
||||||
},
|
},
|
||||||
// bit shifts 7
|
// bit shifts 7
|
||||||
Operator.simple(Token.Type.PLUS, ++lastPrty) { ctx, a, b -> a.plus(ctx, b) },
|
Operator.simple(Token.Type.PLUS, ++lastPriority) { ctx, a, b -> a.plus(ctx, b) },
|
||||||
Operator.simple(Token.Type.MINUS, lastPrty) { ctx, a, b -> a.minus(ctx, b) },
|
Operator.simple(Token.Type.MINUS, lastPriority) { ctx, a, b -> a.minus(ctx, b) },
|
||||||
|
|
||||||
Operator.simple(Token.Type.STAR, ++lastPrty) { ctx, a, b -> a.mul(ctx, b) },
|
Operator.simple(Token.Type.STAR, ++lastPriority) { ctx, a, b -> a.mul(ctx, b) },
|
||||||
Operator.simple(Token.Type.SLASH, lastPrty) { ctx, a, b -> a.div(ctx, b) },
|
Operator.simple(Token.Type.SLASH, lastPriority) { ctx, a, b -> a.div(ctx, b) },
|
||||||
Operator.simple(Token.Type.PERCENT, lastPrty) { ctx, a, b -> a.mod(ctx, b) },
|
Operator.simple(Token.Type.PERCENT, lastPriority) { ctx, a, b -> a.mod(ctx, b) },
|
||||||
)
|
)
|
||||||
|
|
||||||
// private val assigner = allOps.first { it.tokenType == Token.Type.ASSIGN }
|
// private val assigner = allOps.first { it.tokenType == Token.Type.ASSIGN }
|
||||||
@ -1667,11 +1686,10 @@ class Compiler(
|
|||||||
// assigner.generate(context.pos, left, right)
|
// assigner.generate(context.pos, left, right)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
val lastLevel = lastPrty + 1
|
val lastLevel = lastPriority + 1
|
||||||
|
|
||||||
val byLevel: List<Map<Token.Type, Operator>> = (0..<lastLevel).map { l ->
|
val byLevel: List<Map<Token.Type, Operator>> = (0..<lastLevel).map { l ->
|
||||||
allOps.filter { it.priority == l }
|
allOps.filter { it.priority == l }.associateBy { it.tokenType }
|
||||||
.map { it.tokenType to it }.toMap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compile(code: String): Script = Compiler().compile(Source("<eval>", code))
|
fun compile(code: String): Script = Compiler().compile(Source("<eval>", code))
|
||||||
|
@ -72,6 +72,7 @@ open class Context(
|
|||||||
else {
|
else {
|
||||||
objects[name]
|
objects[name]
|
||||||
?: parent?.get(name)
|
?: parent?.get(name)
|
||||||
|
?: thisObj.objClass.getInstanceMemberOrNull(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
|
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
|
||||||
|
@ -64,6 +64,9 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
|||||||
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
(it.thisObj as ObjReal).value.roundToLong().toObj()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
addFn("toInt") {
|
||||||
|
ObjInt(thisAs<ObjReal>().value.toLong())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -112,6 +112,7 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
thisAs<ObjString>().value.uppercase().let(::ObjString)
|
thisAs<ObjString>().value.uppercase().let(::ObjString)
|
||||||
}
|
}
|
||||||
addFn("size") { ObjInt(thisAs<ObjString>().value.length.toLong()) }
|
addFn("size") { ObjInt(thisAs<ObjString>().value.length.toLong()) }
|
||||||
|
addFn("toReal") { ObjReal(thisAs<ObjString>().value.toDouble())}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2284,4 +2284,31 @@ class ScriptTest {
|
|||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testExtend() = runTest() {
|
||||||
|
eval("""
|
||||||
|
|
||||||
|
fun Int.isEven() {
|
||||||
|
this % 2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Object.isInteger() {
|
||||||
|
when(this) {
|
||||||
|
is Int -> true
|
||||||
|
is Real -> toInt() == this
|
||||||
|
is String -> toReal().isInteger()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( 4.isEven() )
|
||||||
|
assert( !5.isEven() )
|
||||||
|
|
||||||
|
assert( 12.isInteger() == true )
|
||||||
|
assert( 12.1.isInteger() == false )
|
||||||
|
assert( "5".isInteger() )
|
||||||
|
assert( ! "5.2".isInteger() )
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user