LispOnZscript v0.0.1
Table of Contents
LispOnZscript is a toy Lisp-like language written on top of ZScript for GZDoom.
LispOnZscript is an experiment, a programming exercise, and isn't suitable for any
practical purpose. I'll be surprised if it finds any practical purpose. See Test to
see what expressions it can evaluate at the moment, also it contains tl_Eval.eval
usage example. Search for TODO in this file to see what remains to be done to make
LispOnZscript more usable.
Key function is tl_Eval.eval function (see Eval). It takes a parsed expression
(from tl_Parser.parse, see 5) and an environment (normally from
tl_Env.makeGlobalEnv, see 8).
Adapted from Lisp as the Maxwell’s equations of software by Michael Nielsen.
TODO: add loading LispOnZscript code from a lump.
Things that I haven't find ways to do to make LispOnZscript more usable:
- Exposing ZScript class attributes. Only functions can be exposed now.
- More generic ZScript class functions exposing. At the moment their signatures must be hard-coded.
- Exposing ZScript structs. For example Cvar, vector. It can be done with wrapper classes, but it's not fun.
- Exposing ZScript
enumconstants.
1. License
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: © 2025 Alexander Kromm <mmaulwurff@gmail.com> // SPDX-License-Identifier: GPL-3.0-only
2. Project setup
version 4.14.2 #include "zscript/tl_Globals.zs" #include "zscript/tl_Env.zs" #include "zscript/tl_Expression.zs" #include "zscript/tl_Eval.zs" #include "zscript/tl_Parser.zs" #include "zscript/tl_EventHandler.zs" #include "zscript/tl_Test.zs"
3. Test
class tl_Test { static tl_Test from() { let result = new("tl_Test"); // Expression, expected evaluation result. result.add("0", "0"); result.add("1", "1"); result.add("(+ 1 2)", "3"); result.add("(+ (* 2 3) 4)", "10"); result.add("(quote 2)", "2"); result.add("(quote (+ 1 2))", "(+ 1 2)"); result.add("(atom? (+ 1 2))", "True"); result.add("(atom? (q (+ 1 2)))", "False"); result.add("(eq? 3 3)", "True"); result.add("(car (quote (1 2 3)))", "1"); result.add("(cdr (quote (1 2 3)))", "(2 3)"); result.add("(cons 1 (quote (2 3)))", "(1 2 3)"); result.add("(cond ((< 1 0) 2) ((< 0 1) 3))", "3"); result.add("(null? (quote ()))", "True"); result.add("(null? 2)", "False"); result.add("(if True 1 2)", "1"); result.add("(if False 1 2)", "2"); result.add("(begin 1 2 3)", "3"); result.add("(begin (set! abc 3) abc)", "3"); result.add("(begin (define abc 3) abc)", "3"); result.add("(begin (define square (lambda (x) (* x x))) (square 3))", "9"); result.add("(begin (define cube (lambda (x) (* x (* x x)))) (cube 2))", "8"); result.add("\"hello world\"", "\"hello world\""); result.add("", "Error: unexpected EOF while reading."); result.add("()", "Error: unexpected empty list."); result.add("(", "Error: unmatched \"(\"."); result.add(")", "Error: unexpected \")\"."); result.add("(2)", "Error: \"2\" is not a function."); result.add("(+ 1 2 3)", "Error: TODO: add support for different number of parameters."); result.add("player", "DoomPlayer"); result.add("(getCharacterName player)", "DoomPlayer"); result.add("(playerNumber player)", "0"); result.add("(isFrozen player)", "False"); result.add("(isFriend player player)", "True"); return result; } void run() { int successCount = 0; foreach (aCase : cases) { Console.printf("Testing \"%s\", expecting \"%s\"", aCase.input, aCase.expected); tl_Expression inputExpression = tl_Parser.parse(aCase.input); if (inputExpression == NULL) { Console.printf("Fail: parse result is NULL"); continue; } let env = tl_Env.makeGlobalEnv(); tl_Expression resultExpression = tl_Eval.eval(inputExpression, env); if (resultExpression == NULL) { Console.printf("Fail: eval result is NULL"); continue; } let result = resultExpression.toString(); if (result == aCase.expected) ++successCount; else Console.printf("Fail: expected: \"%s\", got \"%s\"", aCase.expected, result); } string result = (successCount == cases.size()) ? "Success" : "Fail"; Console.printf("%s: %d succeeded of %d", result, successCount, cases.size()); } private void add(string input, string expectedResult) { cases.push(tl_TestCase.from(input, expectedResult)); } Array<tl_TestCase> cases; } class tl_TestCase { static tl_TestCase from(string input, string expected) { let result = new("tl_TestCase"); result.input = input; result.expected = expected; return result; } string input; string expected; }
4. Event Handler
GameInfo { EventHandlers = "tl_EventHandler" }
class tl_EventHandler : StaticEventHandler { override void networkProcess(ConsoleEvent event) { if (event.name == "lisp-test") { let test = tl_Test.from(); test.run(); return; } } }
5. Parser
class tl_Parser { static tl_Expression parse(string input) { Array<string> tokens; tokenize(input, tokens); return readFrom(tokens); } private static tl_Expression readFrom(out Array<string> tokens) { if (tokens.size() == 0) return tl_Error.from("unexpected EOF while reading"); string token = tokens[0]; tokens.delete(0); if (token == "(") { if (tokens.size() == 0) return tl_Error.from("unmatched \"(\""); let list = tl_List.from(); while (tokens[0] != ")") { let expression = readFrom(tokens); if (expression is "tl_Error") return expression; if (tokens.size() == 0) return tl_Error.from("unmatched \"(\""); list.expressions.push(expression); } // Pop off ")" tokens.delete(0); return list; } else if (token == ")") return tl_Error.from("unexpected \")\""); else return atom(token); } private static void tokenize(string input, out Array<string> tokens) { bool isInString = false; string currentToken; uint length = input.length(); for (uint i = 0; i < length;) { let [character, next] = input.getNextCodePoint(i); if (isInString) { currentToken.appendCharacter(character); if (character == 0x22) // '"' { isInString = false; tokens.push(currentToken); currentToken = ""; } } else { if (isSpace(character)) { if (currentToken.length() != 0) { tokens.push(currentToken); currentToken = ""; } } else if (character == 0x28 || character == 0x29) // '(', ')' { if (currentToken.length() != 0) { tokens.push(currentToken); currentToken = ""; } tokens.push(string.format("%c", character)); } else if (character == 0x22) // '"' { if (currentToken.length() != 0) { tokens.push(currentToken); currentToken = ""; } isInString = true; currentToken.appendCharacter(0x22); } else { currentToken.appendCharacter(character); } } i = next; } if (currentToken.length() != 0) tokens.push(currentToken); } private static bool isSpace(int character) { return character == 0x20 // ' ' || character == 0x9 // '\t' || character == 0xA // '\n' || character == 0xD; // '\r' } private static bool isInt(string token) { return token == "0" || token.toInt() != 0; } private static tl_Expression atom(string token) { if (token.getNextCodePoint(0) == 0x22) return tl_String.from(token); // TODO: support doubles, replace tl_Int with tl_Number. return isInt(token) ? tl_Int.from(token) : tl_Symbol.from(token); } }
6. Eval
class tl_Eval { static tl_Expression eval(tl_Expression expression, tl_Env env) { if (expression == NULL) return error("NULL expression"); if (env == NULL) return error("NULL env"); if (expression is "tl_Error") return expression; if (expression is "tl_Symbol") { string key = expression.toString(); tl_Env foundEnv = env.find(key); let [value, isFound] = foundEnv.vars.checkValue(key); if (isFound) return tl_Expression(value); expression = tl_AutoFunction.from(key); } let list = tl_List(expression); if (list == NULL) return expression; int listSize = list.expressions.size(); if (listSize == 0) return error("unexpected empty list"); let symbol = tl_Symbol(list.expressions[0]); if (symbol != NULL) { if (symbol.name == "quote" || symbol.name == "q") { if (listSize != 2) return error("expected one argument for quote"); return list.expressions[1]; } if (symbol.name == "atom?") { if (listSize != 2) return error("expected one argument for atom?"); let result = eval(list.expressions[1], env); if (result is "tl_Error") return result; return tl_Bool.from(!(result is "tl_List")); } // TODO: why built-in eq?, if there is "=" function? if (symbol.name == "eq?") { if (listSize != 3) return error("expected two arguments for eq?"); let lhs = eval(list.expressions[1], env); if (lhs is "tl_Error") return lhs; let rhs = eval(list.expressions[2], env); if (rhs is "tl_Error") return rhs; return tl_Bool.from(!(lhs is "tl_List") && lhs.eq(rhs)); } if (symbol.name == "car") { if (listSize != 2) return error("expected one argument for car"); let result = eval(list.expressions[1], env); if (result is "tl_Error") return result; let resultList = tl_List(result); if (resultList == NULL) return error("car argument is not a list"); if (resultList.expressions.size() < 1) return error("empty list for car"); return resultList.expressions[0]; } if (symbol.name == "cdr") { if (listSize != 2) return error("expected one argument for cdr"); let result = eval(list.expressions[1], env); if (result is "tl_Error") return result; let resultList = tl_List(result); if (resultList == NULL) return error("cdr argument is not a list"); if (resultList.expressions.size() < 1) return error("empty list for cdr"); resultList.expressions.delete(0); return resultList; } if (symbol.name == "cons") { if (listSize != 3) return error("expected two arguments for cons"); let lhs = eval(list.expressions[1], env); if (lhs is "tl_Error") return lhs; let rhs = eval(list.expressions[2], env); if (rhs is "tl_Error") return rhs; let list = tl_List(rhs); if (list == NULL) return error("rhs of cons expected to be a list"); list.expressions.insert(0, lhs); return rhs; } if (symbol.name == "cond") { if (listSize < 2) return error("expected at least one argument for cond"); for (int i = 1; i < listSize; ++i) { let condition = tl_List(list.expressions[i]); if (condition == NULL) return error("expected condition to be a list"); if (condition.expressions.size() != 2) return error("expected condition has two parts"); let check = eval(condition.expressions[0], env); if (check is "tl_Error") return check; if (!isTrue(check)) continue; return eval(condition.expressions[1], env); } // TODO: clarify what cond should return if no check is true. False? []? return tl_Bool.from(false); } if (symbol.name == "null?") { if (listSize != 2) return error("expected one argument for null?"); let result = eval(list.expressions[1], env); if (result is "tl_Error") return result; let resultList = tl_List(result); if (resultList == NULL) return tl_Bool.from(false); return tl_Bool.from(resultList.expressions.size() == 0); } if (symbol.name == "if") { if (listSize != 4) return error("expected three arguments for if"); let test = eval(list.expressions[1], env); if (test is "tl_Error") return test; let toEval = isTrue(test) ? 2 : 3; return eval(list.expressions[toEval], env); } if (symbol.name == "set!") { if (listSize != 3) return error("expected two arguments for set!"); let result = eval(list.expressions[2], env); if (result is "tl_Error") return result; let variable = list.expressions[1]; if (!(variable is "tl_Symbol")) return error("expected symbol for set!"); let name = variable.toString(); let environment = env.find(name); environment.vars.insert(name, result); // TODO: clarify what set! returns. return result; } if (symbol.name == "define") { if (listSize != 3) return error("expected two arguments for define"); let result = eval(list.expressions[2], env); if (result is "tl_Error") return result; let variable = list.expressions[1]; if (!(variable is "tl_Symbol")) return error("expected symbol for set!"); let name = variable.toString(); env.vars.insert(name, result); // TODO: clarify what define returns. return result; } if (symbol.name == "lambda") // (define square (lambda (x) (* x x))) { if (listSize != 3) return error("expected two arguments for lambda"); let vars = tl_List(list.expressions[1]); if (vars == NULL) return error("lambda arguments must be a list"); int varsCount = vars.expressions.size(); for (int i = 0; i < varsCount; ++i) { if (!(vars.expressions[i] is "tl_Symbol")) return error(string.format("expected variable in lambda, got\"%s\"", vars.expressions[i].toString())); } let exp = list.expressions[2]; return tl_Lambda.from(vars, exp, env); } if (symbol.name == "begin") { if (listSize < 2) return error("expected at least one argument for begin"); tl_Expression result; for (int i = 1; i < listSize; ++i) { result = eval(list.expressions[i], env); if (result is "tl_Error") return result; } return result; } } Array<tl_Expression> exps; foreach (exp : list.expressions) { let result = eval(exp, env); if (result is "tl_Error") return result; exps.push(result); } let proc = exps[0]; exps.delete(0); let procFunction = tl_Function(proc); if (procFunction == NULL) return error(string.format("\"%s\" is not a function", proc.toString())); return procFunction.execute(exps); } private static tl_Expression error(string message) { return tl_Error.from(message); } private static bool isTrue(tl_Expression expression) { let boolCheck = tl_Bool(expression); return boolCheck == NULL || boolCheck.value; } }
7. Expressions
7.1. Expression
class tl_Expression abstract { virtual string toString() const { throwAbortException("forgot to implement toString for %s", getClassName()); return ""; } virtual bool eq(tl_Expression other) const { throwAbortException("forgot to implement eq for %s", getClassName()); return true; } }
7.2. Error
// Not a real expression, used to report errors. class tl_Error : tl_Expression { static tl_Expression from(string message) { let result = new("tl_Error"); result.message = message; return result; } override string toString() const { return string.format("Error: %s.", message); } override bool eq(tl_Expression other) const { let otherError = tl_Error(other); if (otherError == NULL) return false; return message == otherError.message; } string message; }
7.3. List
class tl_List : tl_Expression { static tl_List from() { return new("tl_List"); } override string toString() const { string result = "("; foreach (expression : expressions) result.appendFormat("%s ", expression.toString()); result.deleteLastCharacter(); result.appendFormat(")"); return result; } override bool eq(tl_Expression other) const { let otherList = tl_List(other); if (otherList == NULL) return false; int size = expressions.size(); if (size != otherList.expressions.size()) return false; for (int i = 0; i < size; ++i) if (expressions[i] != otherList.expressions[i]) return false; return true; } Array<tl_Expression> expressions; }
7.4. Number
// TODO: replace with double. class tl_Int : tl_Expression { static tl_Expression from(string input) { let result = new("tl_Int"); result.value = input.toInt(); return result; } static tl_Expression fromInt(int value) { let result = new("tl_Int"); result.value = value; return result; } override string toString() const { return string.format("%d", value); } override bool eq(tl_Expression other) const { let otherInt = tl_Int(other); if (otherInt == NULL) return false; return value == otherInt.value; } int value; }
7.5. Symbol
class tl_Symbol : tl_Expression { static tl_Expression from(string name) { let result = new("tl_Symbol"); result.name = name; return result; } override string toString() const { return string.format("%s", name); } override bool eq(tl_Expression other) const { let otherSymbol = tl_Symbol(other); if (otherSymbol == NULL) return false; return name == otherSymbol.name; } string name; }
7.6. Function
class tl_Function : tl_Expression { static tl_Function from(string className, string functionName) { let result = new("tl_Function"); result.className = className; result.functionName = functionName; return result; } virtual tl_Expression execute(Array<tl_Expression> exps) { if (exps.size() != 2) return tl_Error.from("TODO: add support for different number of parameters"); class<Object> aClass = className; if (aClass == NULL) return tl_Error.from(string.format("class %s not found", className)); let aFunction = (Function<clearscope tl_Expression(tl_Expression, tl_Expression)>) (findFunction(aClass, functionName)); if (aFunction == NULL) return tl_Error.from(string.format("function %s.%s not found", className, functionName)); return aFunction.call(exps[0], exps[1]); } override bool eq(tl_Expression other) const { let otherFunction = tl_Function(other); if (otherFunction == NULL) return false; return className == otherFunction.className && functionName == otherFunction.functionName; } string className; string functionName; }
7.7. Lambda
class tl_Lambda : tl_Function { static tl_Lambda from(tl_List vars, tl_Expression expression, tl_Env env) { let result = new("tl_Lambda"); result.vars = vars; result.expression = expression; result.env = env; return result; } override tl_Expression execute(Array<tl_Expression> exps) { int varsCount = vars.expressions.size(); if (exps.size() != varsCount) { return tl_Error.from(string.format("expected %d arguments, got %d", varsCount, exps.size())); } let innerEnvironment = tl_Env.from(env); for (int i = 0; i < varsCount; ++i) { let varSymbol = tl_Symbol(vars.expressions[i]); innerEnvironment.vars.insert(varSymbol.name, exps[i]); } return tl_Eval.eval(expression, innerEnvironment); } override bool eq(tl_Expression other) const { let otherLambda = tl_Lambda(other); if (otherLambda == NULL) return false; return vars == otherLambda.vars && expression == otherLambda.expression && env == otherLambda.env; } tl_List vars; tl_Expression expression; tl_Env env; }
7.8. Auto Function
class tl_AutoFunction : tl_Function { static tl_AutoFunction from(string name) { let result = new("tl_AutoFunction"); result.name = name; return result; } override bool eq(tl_Expression other) const { let otherAutoFunction = tl_AutoFunction(other); if (otherAutoFunction == NULL) return false; return name == otherAutoFunction.name; } override tl_Expression execute(Array<tl_Expression> exps) { // todo : search in tl_Globals? if (exps.size() == 0) return error(string.format("cannot call \"%s\": no object", name)); let objectExpression = tl_Object(exps[0]); if (objectExpression == NULL) return error(string.format("cannot call \"%s\": \"%s\" is not an object", name, exps[0].toString())); Object anObject = objectExpression.value; let aFunction = findFunction(anObject.getClass(), name); if (aFunction == NULL) return error(string.format("cannot call \"%s\": not found in \"%s\"", name, anObject.getClassName())); let result = trySignatures(aFunction, exps); if (result != NULL) return result; return error(string.format("cannot call \"%s\" with %d arguments:" " signature not supported", name, exps.size())); } private static tl_Expression trySignatures(Function<void> aFunction, Array<tl_Expression> exps) { switch (exps.size()) { // TODO: add support for static functions. case 0: return NULL; case 1: { Actor anActor = asActor(exps[0]); if (anActor != NULL) return tryActorSignatures(aFunction, anActor); return NULL; } case 2: { Actor anActor1 = asActor(exps[0]); Actor anActor2 = asActor(exps[1]); if (anActor1 != NULL && anActor2 != NULL) return tryActorActorsignatures(aFunction, anActor1, anActor2); return NULL; } } return NULL; } private static Actor asActor(tl_Expression argument) { let anObject = tl_Object(argument); return (anObject == NULL) ? NULL : Actor(anObject.value); } private static tl_Expression tryActorSignatures(Function<void> aFunction, Actor anActor) { { let casted = (Function<clearscope string(Actor)>)(aFunction); if (casted != NULL) return tl_String.from(casted.call(anActor)); } { let casted = (Function<clearscope int(Actor)>)(aFunction); if (casted != NULL) return tl_Int.fromInt(casted.call(anActor)); } { let casted = (Function<clearscope bool(Actor)>)(aFunction); if (casted != NULL) return tl_Bool.from(casted.call(anActor)); } return NULL; } private static tl_Expression tryActorStringSignatures(Function<void> aFunction, Actor anActor, string aString) { { let casted = (Function<clearscope string(Actor, string)>)(aFunction); if (casted != NULL) return tl_String.from(casted.call(anActor, aString)); } return NULL; } private static tl_Expression tryActorActorSignatures(Function<void> aFunction, Actor anActor1, Actor anActor2) { { let casted = (Function<clearscope bool(Actor, Actor)>)(aFunction); if (casted != NULL) return tl_Bool.from(casted.call(anActor1, anActor2)); } return NULL; } private static tl_Expression error(string message) { return tl_Error.from(message); } string name; }
7.9. Bool
class tl_Bool : tl_Expression { static tl_Bool from(bool value) { let result = new("tl_Bool"); result.value = value; return result; } override string toString() const { return string.format("%s", value ? "True" : "False"); } override bool eq(tl_Expression other) const { let otherBool = tl_Bool(other); if (otherBool == NULL) return false; return value == otherBool.value; } bool value; }
7.10. String
class tl_String : tl_Expression { static tl_String from(string value) { let result = new("tl_String"); result.value = value; return result; } override string toString() const { return value; } override bool eq(tl_Expression other) const { let otherString = tl_String(other); if (otherString == NULL) return false; return value == otherString.value; } string value; }
7.11. Object
class tl_Object : tl_Expression { static tl_Object from(Object value) { let result = new("tl_Object"); result.value = value; return result; } override string toString() const { return (value == NULL) ? "NULL" : string.format("%s", value.getClassName()); } override bool eq(tl_Expression other) { let otherObject = tl_Object(other); if (otherObject == NULL) return false; return value == otherObject.value; } Object value; }
8. Env
class tl_Env { static tl_Env makeGlobalEnv() { let result = new("tl_Env"); result = tl_Env.from(NULL); result.vars.insert("+", tl_Function.from("tl_Globals", "add")); result.vars.insert("-", tl_Function.from("tl_Globals", "sub")); result.vars.insert("*", tl_Function.from("tl_Globals", "mul")); result.vars.insert("/", tl_Function.from("tl_Globals", "div")); result.vars.insert("=", tl_Function.from("tl_Globals", "eq")); result.vars.insert(">", tl_Function.from("tl_Globals", "gt")); result.vars.insert("<", tl_Function.from("tl_Globals", "lt")); result.vars.insert(">=", tl_Function.from("tl_Globals", "ge")); result.vars.insert("<=", tl_Function.from("tl_Globals", "le")); result.vars.insert("True", tl_Bool.from(true)); result.vars.insert("False", tl_Bool.from(false)); result.vars.insert("player", tl_Object.from(players[consolePlayer].mo)); return result; } static tl_Env from(tl_Env outer) { let result = new("tl_Env"); result.outer = outer; return result; } tl_Env find(string aVar) { return (outer == NULL || vars.checkKey(aVar)) ? self : outer.find(aVar); } tl_Env outer; Map<string, Object> vars; }
9. Global Functions
class tl_Globals { static tl_Expression add(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Int.fromInt(lhsInt.value + rhsInt.value); return tl_Error.from(string.format("add not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression sub(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Int.fromInt(lhsInt.value - rhsInt.value); return tl_Error.from(string.format("sub not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression mul(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Int.fromInt(lhsInt.value * rhsInt.value); return tl_Error.from(string.format("mul not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression div(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Int.fromInt(lhsInt.value / rhsInt.value); return tl_Error.from(string.format("div not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression gt(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Bool.from(lhsInt.value > rhsInt.value); return tl_Error.from(string.format("gt not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression lt(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Bool.from(lhsInt.value < rhsInt.value); return tl_Error.from(string.format("lt not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression ge(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Bool.from(lhsInt.value >= rhsInt.value); return tl_Error.from(string.format("ge not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression le(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Bool.from(lhsInt.value >= rhsInt.value); return tl_Error.from(string.format("le not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } static tl_Expression eq(tl_Expression lhs, tl_Expression rhs) { let lhsInt = tl_Int(lhs); let rhsInt = tl_Int(rhs); if (lhsInt != NULL && rhsInt != NULL) return tl_Bool.from(lhsInt.value == rhsInt.value); return tl_Error.from(string.format("eq not implemented for \"%s\" and \"%s\".", lhs.getClassName(), rhs.getClassName())); } }
10. Run
wait 2; map map01; wait 2; netevent lisp-test; wait 2; quit