some cleanup
This commit is contained in:
parent
2130974929
commit
72f637e7de
@ -323,4 +323,18 @@ String literal could be multiline:
|
|||||||
|
|
||||||
though multiline literals is yet work in progress.
|
though multiline literals is yet work in progress.
|
||||||
|
|
||||||
|
# Built-in functions
|
||||||
|
|
||||||
|
See [math functions](math.md), and:
|
||||||
|
|
||||||
|
| name | description |
|
||||||
|
|----------------------------------------------|----------------------------------------------------------|
|
||||||
|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,109 +1,14 @@
|
|||||||
package net.sergeych.ling
|
package net.sergeych.ling
|
||||||
|
|
||||||
//sealed class ObjType(name: String, val defaultValue: Obj? = null) {
|
|
||||||
//
|
|
||||||
// class Str : ObjType("string", ObjString(""))
|
|
||||||
// class Int : ObjType("real", ObjReal(0.0))
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Descriptor for whatever object could be used as argument, return value,
|
|
||||||
// * field, etc.
|
|
||||||
// */
|
|
||||||
//data class ObjDescriptor(
|
|
||||||
// val type: ObjType,
|
|
||||||
// val mutable: Boolean,
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//data class MethodDescriptor(
|
|
||||||
// val args: Array<ObjDescriptor>,
|
|
||||||
// val result: ObjDescriptor
|
|
||||||
//)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Meta context contains map of symbols.
|
|
||||||
|
|
||||||
Each symbol can be:
|
|
||||||
|
|
||||||
- a var
|
|
||||||
- a const
|
|
||||||
- a function
|
|
||||||
- a type alias
|
|
||||||
- a class definition
|
|
||||||
|
|
||||||
Each have in common only its name.
|
|
||||||
|
|
||||||
Var has: type, value. Const is same as var but value is fixed. Function has args and return value,
|
|
||||||
type alias has target type name. So we have to have something that denotes a _type_
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
//data class MetaContext(val symbols: MutableMap<String, > = mutableMapOf()) {
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
class CompilerContext(val tokens: List<Token>) : ListIterator<Token> by tokens.listIterator() {
|
|
||||||
val labels = mutableSetOf<String>()
|
|
||||||
|
|
||||||
fun ensureLabelIsValid(pos: Pos, label: String) {
|
|
||||||
if (label !in labels)
|
|
||||||
throw ScriptError(pos, "Undefined label '$label'")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun requireId() = requireToken(Token.Type.ID, "identifier is required")
|
|
||||||
|
|
||||||
fun requireToken(type: Token.Type, message: String = "required ${type.name}"): Token =
|
|
||||||
next().also {
|
|
||||||
if (type != it.type) throw ScriptError(it.pos, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun syntaxError(at: Pos, message: String = "Syntax error"): Nothing {
|
|
||||||
throw ScriptError(at, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun currentPos() =
|
|
||||||
if (hasNext()) next().pos.also { previous() }
|
|
||||||
else previous().pos.also { next() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips next token if its type is `tokenType`, returns `true` if so.
|
* The LING compiler.
|
||||||
* @param errorMessage message to throw if next token is not `tokenType`
|
|
||||||
* @param isOptional if `true` and token is not of `tokenType`, just return `false` and does not skip it
|
|
||||||
* @return `true` if the token was skipped
|
|
||||||
* @throws ScriptError if [isOptional] is `false` and next token is not of [tokenType]
|
|
||||||
*/
|
*/
|
||||||
fun skipTokenOfType(tokenType: Token.Type, errorMessage: String="expected ${tokenType.name}", isOptional: Boolean = false): Boolean {
|
class Compiler(
|
||||||
val t = next()
|
@Suppress("UNUSED_PARAMETER")
|
||||||
return if (t.type != tokenType) {
|
settings: Settings = Settings()
|
||||||
if (!isOptional) {
|
) {
|
||||||
println("unexpected: $t (needed $tokenType)")
|
|
||||||
throw ScriptError(t.pos, errorMessage)
|
|
||||||
} else {
|
|
||||||
previous()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
|
class Settings
|
||||||
val t = next()
|
|
||||||
return if (t.type == typeId) {
|
|
||||||
f(t)
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
previous()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Compiler {
|
|
||||||
|
|
||||||
fun compile(source: Source): Script {
|
fun compile(source: Source): Script {
|
||||||
return parseScript(source.startPos, CompilerContext(parseLing(source)))
|
return parseScript(source.startPos, CompilerContext(parseLing(source)))
|
||||||
@ -262,7 +167,7 @@ class Compiler {
|
|||||||
* expr-=<expr>, expr*=<expr>, expr/=<expr>
|
* expr-=<expr>, expr*=<expr>, expr/=<expr>
|
||||||
* read expr: <expr>
|
* read expr: <expr>
|
||||||
*/
|
*/
|
||||||
fun parseTerm3(cc: CompilerContext): Statement? {
|
private fun parseTerm3(cc: CompilerContext): Statement? {
|
||||||
var operand: Accessor? = null
|
var operand: Accessor? = null
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -332,17 +237,20 @@ class Compiler {
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
return operand?.let { op -> statement(startPos) { op.getter(it) } }
|
return operand?.let { op -> statement(startPos) { op.getter(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
"if", "when", "do", "while", "return" -> {
|
"if", "when", "do", "while", "return" -> {
|
||||||
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(cc) ?: throw ScriptError(t.pos, "Expecting valid statement")
|
||||||
operand = Accessor { s.execute(it) }
|
operand = Accessor { s.execute(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
"break", "continue" -> {
|
"break", "continue" -> {
|
||||||
cc.previous()
|
cc.previous()
|
||||||
return operand?.let { op -> statement(startPos) { op.getter(it) } }
|
return operand?.let { op -> statement(startPos) { op.getter(it) } }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> operand?.let { left ->
|
else -> operand?.let { left ->
|
||||||
// selector: <lvalue>, '.' , <id>
|
// selector: <lvalue>, '.' , <id>
|
||||||
// we replace operand with selector code, that
|
// we replace operand with selector code, that
|
||||||
@ -417,11 +325,12 @@ class Compiler {
|
|||||||
"class" -> Accessor {
|
"class" -> Accessor {
|
||||||
operand.getter(it).objClass
|
operand.getter(it).objClass
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw ScriptError(t.pos, "Unknown scope operation: ${t.value}")
|
else -> throw ScriptError(t.pos, "Unknown scope operation: ${t.value}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseArgs(cc: CompilerContext): List<Arguments.Info> {
|
private fun parseArgs(cc: CompilerContext): List<Arguments.Info> {
|
||||||
val args = mutableListOf<Arguments.Info>()
|
val args = mutableListOf<Arguments.Info>()
|
||||||
do {
|
do {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
@ -438,7 +347,7 @@ class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun parseFunctionCall(cc: CompilerContext, left: Accessor): Accessor {
|
private fun parseFunctionCall(cc: CompilerContext, left: Accessor): Accessor {
|
||||||
// insofar, functions always return lvalue
|
// insofar, functions always return lvalue
|
||||||
val args = parseArgs(cc)
|
val args = parseArgs(cc)
|
||||||
|
|
||||||
@ -454,17 +363,7 @@ class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun parseReservedWord(word: String): Accessor? =
|
private fun parseAccessor(cc: CompilerContext): Accessor? {
|
||||||
// when(word) {
|
|
||||||
// "true" -> Accessor { ObjBool(true) }
|
|
||||||
// "false" -> Accessor { ObjBool(false) }
|
|
||||||
// "void" -> Accessor { ObjVoid }
|
|
||||||
// "null" -> Accessor { ObjNull }
|
|
||||||
// else -> null
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
fun parseAccessor(cc: CompilerContext): Accessor? {
|
|
||||||
// could be: literal
|
// could be: literal
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
return when (t.type) {
|
return when (t.type) {
|
||||||
@ -495,9 +394,7 @@ class Compiler {
|
|||||||
else -> {
|
else -> {
|
||||||
Accessor({
|
Accessor({
|
||||||
it.pos = t.pos
|
it.pos = t.pos
|
||||||
it.get(t.value)?.value?.also {
|
it.get(t.value)?.value
|
||||||
println("got ${t.value} -> $it")
|
|
||||||
}
|
|
||||||
?: 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.get(t.value)?.let { stored ->
|
||||||
@ -516,161 +413,7 @@ class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseNumber(isPlus: Boolean, tokens: CompilerContext): Obj {
|
||||||
// fun parseTerm(tokens: CompilerContext): Statement? {
|
|
||||||
// // call op
|
|
||||||
// // index op
|
|
||||||
// // unary op
|
|
||||||
// // parenthesis
|
|
||||||
// // number or string
|
|
||||||
// val t = tokens.next()
|
|
||||||
// // todoL var?
|
|
||||||
// return when (t.type) {
|
|
||||||
// Token.Type.ID -> {
|
|
||||||
// when (t.value) {
|
|
||||||
// "void" -> statement(t.pos, true) { ObjVoid }
|
|
||||||
// "null" -> statement(t.pos, true) { ObjNull }
|
|
||||||
// "true" -> statement(t.pos, true) { ObjBool(true) }
|
|
||||||
// "false" -> statement(t.pos, true) { ObjBool(false) }
|
|
||||||
// else -> parseVarAccess(t, tokens)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Token.Type.STRING -> statement(t.pos, true) { ObjString(t.value) }
|
|
||||||
//
|
|
||||||
// Token.Type.LPAREN -> {
|
|
||||||
// // ( subexpr )
|
|
||||||
// parseExpression(tokens)?.also {
|
|
||||||
// val tl = tokens.next()
|
|
||||||
// if (tl.type != Token.Type.RPAREN)
|
|
||||||
// throw ScriptError(t.pos, "unbalanced parenthesis: no ')' for it")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Token.Type.PLUS -> {
|
|
||||||
// val n = parseNumber(true, tokens)
|
|
||||||
// statement(t.pos, true) { n }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Token.Type.MINUS -> {
|
|
||||||
// val n = parseNumber(false, tokens)
|
|
||||||
// statement(t.pos, true) { n }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Token.Type.INT, Token.Type.REAL, Token.Type.HEX -> {
|
|
||||||
// tokens.previous()
|
|
||||||
// val n = parseNumber(true, tokens)
|
|
||||||
// statement(t.pos, true) { n }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// else -> null
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fun parseVarAccess(id: Token, tokens: CompilerContext, path: List<String> = emptyList()): Statement {
|
|
||||||
// val nt = tokens.next()
|
|
||||||
//
|
|
||||||
// fun resolve(context: Context): Context {
|
|
||||||
// var targetContext = context
|
|
||||||
// for (n in path) {
|
|
||||||
// val x = targetContext[n] ?: throw ScriptError(id.pos, "undefined symbol: $n")
|
|
||||||
// (x.value as? ObjNamespace)?.let { targetContext = it.context }
|
|
||||||
// ?: throw ScriptError(id.pos, "Invalid symbolic path (wrong type of ${n}: ${x.value}")
|
|
||||||
// }
|
|
||||||
// return targetContext
|
|
||||||
// }
|
|
||||||
// return when (nt.type) {
|
|
||||||
// Token.Type.DOT -> {
|
|
||||||
// // selector
|
|
||||||
// val t = tokens.next()
|
|
||||||
// if (t.type == Token.Type.ID) {
|
|
||||||
// parseVarAccess(t, tokens, path + id.value)
|
|
||||||
// } else
|
|
||||||
// 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
|
|
||||||
// val args = mutableListOf<Arguments.Info>()
|
|
||||||
// do {
|
|
||||||
// val t = tokens.next()
|
|
||||||
// when (t.type) {
|
|
||||||
// Token.Type.RPAREN, Token.Type.COMMA -> {}
|
|
||||||
// else -> {
|
|
||||||
// tokens.previous()
|
|
||||||
// parseStatement(tokens)?.let { args += Arguments.Info(it, t.pos) }
|
|
||||||
// ?: throw ScriptError(t.pos, "Expecting arguments list")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } while (t.type != Token.Type.RPAREN)
|
|
||||||
//
|
|
||||||
// statement(id.pos) { context ->
|
|
||||||
// val v =
|
|
||||||
// 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)")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Token.Type.LBRACKET -> {
|
|
||||||
// TODO("indexing")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// else -> {
|
|
||||||
// // just access the var
|
|
||||||
// tokens.previous()
|
|
||||||
//// val t = tokens.next()
|
|
||||||
//// tokens.previous()
|
|
||||||
//// println(t)
|
|
||||||
//// if( path.isEmpty() ) {
|
|
||||||
//// statement?
|
|
||||||
//// tokens.previous()
|
|
||||||
//// parseStatement(tokens) ?: throw ScriptError(id.pos, "Expecting expression/statement")
|
|
||||||
//// } else
|
|
||||||
// statement(id.pos) {
|
|
||||||
// val v =
|
|
||||||
// resolve(it).get(id.value) ?: throw ScriptError(id.pos, "Undefined variable: ${id.value}")
|
|
||||||
// v.value ?: throw ScriptError(id.pos, "Variable $id is not initialized")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
fun parseNumber(isPlus: Boolean, tokens: CompilerContext): Obj {
|
|
||||||
val t = tokens.next()
|
val t = tokens.next()
|
||||||
return when (t.type) {
|
return when (t.type) {
|
||||||
Token.Type.INT, Token.Type.HEX -> {
|
Token.Type.INT, Token.Type.HEX -> {
|
||||||
@ -704,7 +447,7 @@ class Compiler {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLabel(cc: CompilerContext, maxDepth: Int = 2): String? {
|
private fun getLabel(cc: CompilerContext, 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) {
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package net.sergeych.ling
|
||||||
|
|
||||||
|
internal class CompilerContext(val tokens: List<Token>) : ListIterator<Token> by tokens.listIterator() {
|
||||||
|
val labels = mutableSetOf<String>()
|
||||||
|
|
||||||
|
fun ensureLabelIsValid(pos: Pos, label: String) {
|
||||||
|
if (label !in labels)
|
||||||
|
throw ScriptError(pos, "Undefined label '$label'")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun requireId() = requireToken(Token.Type.ID, "identifier is required")
|
||||||
|
|
||||||
|
fun requireToken(type: Token.Type, message: String = "required ${type.name}"): Token =
|
||||||
|
next().also {
|
||||||
|
if (type != it.type) throw ScriptError(it.pos, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syntaxError(at: Pos, message: String = "Syntax error"): Nothing {
|
||||||
|
throw ScriptError(at, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun currentPos() =
|
||||||
|
if (hasNext()) next().pos.also { previous() }
|
||||||
|
else previous().pos.also { next() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips next token if its type is `tokenType`, returns `true` if so.
|
||||||
|
* @param errorMessage message to throw if next token is not `tokenType`
|
||||||
|
* @param isOptional if `true` and token is not of `tokenType`, just return `false` and does not skip it
|
||||||
|
* @return `true` if the token was skipped
|
||||||
|
* @throws ScriptError if [isOptional] is `false` and next token is not of [tokenType]
|
||||||
|
*/
|
||||||
|
fun skipTokenOfType(tokenType: Token.Type, errorMessage: String="expected ${tokenType.name}", isOptional: Boolean = false): Boolean {
|
||||||
|
val t = next()
|
||||||
|
return if (t.type != tokenType) {
|
||||||
|
if (!isOptional) {
|
||||||
|
println("unexpected: $t (needed $tokenType)")
|
||||||
|
throw ScriptError(t.pos, errorMessage)
|
||||||
|
} else {
|
||||||
|
previous()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
|
||||||
|
val t = next()
|
||||||
|
return if (t.type == typeId) {
|
||||||
|
f(t)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
previous()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -45,14 +45,20 @@ class Context(
|
|||||||
)
|
)
|
||||||
}.value as ObjNamespace)
|
}.value as ObjNamespace)
|
||||||
|
|
||||||
inline fun <reified T> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
|
inline fun addVoidFn(vararg names: String, crossinline fn: suspend Context.() -> Unit) {
|
||||||
|
addFn<ObjVoid>(*names) {
|
||||||
|
fn(this)
|
||||||
|
ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T: Obj> addFn(vararg names: String, crossinline fn: suspend Context.() -> T) {
|
||||||
val newFn = object : Statement() {
|
val newFn = object : Statement() {
|
||||||
override val pos: Pos = Pos.builtIn
|
override val pos: Pos = Pos.builtIn
|
||||||
|
|
||||||
override suspend fun execute(context: Context): Obj {
|
override suspend fun execute(context: Context): Obj {
|
||||||
return try {
|
return try {
|
||||||
from(context.fn())
|
context.fn()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
raise(e.message ?: "unexpected error")
|
raise(e.message ?: "unexpected error")
|
||||||
}
|
}
|
||||||
|
@ -30,24 +30,24 @@ class Script(
|
|||||||
}
|
}
|
||||||
addFn("floor") {
|
addFn("floor") {
|
||||||
val x = args.firstAndOnly()
|
val x = args.firstAndOnly()
|
||||||
if (x is ObjInt) x
|
(if (x is ObjInt) x
|
||||||
else ObjReal(floor(x.toDouble()))
|
else ObjReal(floor(x.toDouble()))) as Obj
|
||||||
}
|
}
|
||||||
addFn("ceil") {
|
addFn("ceil") {
|
||||||
val x = args.firstAndOnly()
|
val x = args.firstAndOnly()
|
||||||
if (x is ObjInt) x
|
(if (x is ObjInt) x
|
||||||
else ObjReal(ceil(x.toDouble()))
|
else ObjReal(ceil(x.toDouble()))) as Obj
|
||||||
}
|
}
|
||||||
addFn("round") {
|
addFn("round") {
|
||||||
val x = args.firstAndOnly()
|
val x = args.firstAndOnly()
|
||||||
if (x is ObjInt) x
|
(if (x is ObjInt) x
|
||||||
else ObjReal(round(x.toDouble()))
|
else ObjReal(round(x.toDouble()))) as Obj
|
||||||
}
|
}
|
||||||
addFn("sin") {
|
addFn("sin") {
|
||||||
sin(args.firstAndOnly().toDouble())
|
ObjReal(sin(args.firstAndOnly().toDouble()))
|
||||||
}
|
}
|
||||||
|
|
||||||
addFn("assert") {
|
addVoidFn("assert") {
|
||||||
val cond = args.required<ObjBool>(0, this)
|
val cond = args.required<ObjBool>(0, this)
|
||||||
if( !cond.value == true )
|
if( !cond.value == true )
|
||||||
raiseError(ObjAssertionError(this,"Assertion failed"))
|
raiseError(ObjAssertionError(this,"Assertion failed"))
|
||||||
|
@ -564,14 +564,5 @@ class ScriptTest {
|
|||||||
eval(src)
|
eval(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCallable1() = runTest {
|
|
||||||
val src = """
|
|
||||||
val callable = {
|
|
||||||
println("called")
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
println(eval(src).toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user