From f3600dacea3e38cd541bf57076f8a7141987e10b Mon Sep 17 00:00:00 2001 From: nea Date: Wed, 9 Aug 2023 17:05:46 +0200 Subject: Add addition --- src/CoreBindings.kt | 19 +++++++++++++++++-- src/LispAst.kt | 6 ++++++ src/LispData.kt | 32 +++++++------------------------- src/LispExecutionContext.kt | 11 ++++------- src/LispParser.kt | 15 +++++++++++++++ test/res/test.lisp | 5 +---- 6 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/CoreBindings.kt b/src/CoreBindings.kt index 9213917..13c6de3 100644 --- a/src/CoreBindings.kt +++ b/src/CoreBindings.kt @@ -82,20 +82,34 @@ object CoreBindings { } lastResult ?: context.reportError("Seq cannot be invoked with 0 argumens", callsite) } + val debuglog = LispData.externalRawCall { context, callsite, stackFrame, args -> println(args.joinToString(" ") { arg -> when (val resolved = context.resolveValue(stackFrame, arg)) { is LispData.Atom -> ":${resolved.label}" is LispData.JavaExecutable -> "" LispData.LispNil -> "nil" - is LispData.LispNumber -> resolved.number.toString() is LispData.LispNode -> resolved.node.toSource() - is LispData.LispObject<*> -> resolved.data.toString() + is LispData.LispString -> resolved.string + is LispData.LispNumber -> resolved.value.toString() is LispData.LispInterpretedCallable -> "" } }) LispData.LispNil } + val add = LispData.externalCall { args, reportError -> + if (args.size == 0) { + return@externalCall reportError("Cannot call add without at least 1 argument") + } + LispData.LispNumber(args.fold(0.0) { a, b -> + a + (b as? LispData.LispNumber + ?: return@externalCall reportError("Unexpected argument $b, expected number")).value + }) + } + + fun offerArithmeticTo(bindings: StackFrame) { + bindings.setValueLocal("+", add) + } fun offerAllTo(bindings: StackFrame) { bindings.setValueLocal("nil", nil) @@ -105,5 +119,6 @@ object CoreBindings { bindings.setValueLocal("defun", defun) bindings.setValueLocal("seq", seq) bindings.setValueLocal("debuglog", debuglog) + offerArithmeticTo(bindings) } } \ No newline at end of file diff --git a/src/LispAst.kt b/src/LispAst.kt index 79fee8a..6ac7032 100644 --- a/src/LispAst.kt +++ b/src/LispAst.kt @@ -33,6 +33,12 @@ sealed class LispAst : HasLispPosition { } } + data class NumberLiteral(override val position: LispPosition, val numberValue: Double) : LispNode() { + override fun toSource(): String { + return numberValue.toString() + } + } + data class StringLiteral(override val position: LispPosition, val parsedString: String) : LispNode() { override fun toSource(): String { return "\"${parsedString.replace("\\", "\\\\").replace("\"", "\\\"")}\"" // TODO: better escaping diff --git a/src/LispData.kt b/src/LispData.kt index 1f13ad3..11a2451 100644 --- a/src/LispData.kt +++ b/src/LispData.kt @@ -2,17 +2,11 @@ package moe.nea.lisp sealed class LispData { - fun lispCastObject(lClass: LispClass): LispObject? { - if (this !is LispObject<*>) return null - if (this.handler != lClass) return null - return this as LispObject - } - object LispNil : LispData() data class Atom(val label: String) : LispData() + data class LispString(val string: String) : LispData() + data class LispNumber(val value: Double) : LispData() data class LispNode(val node: LispAst.LispNode) : LispData() - data class LispNumber(val number: Double) : LispData() - data class LispObject(val data: T, val handler: LispClass) : LispData() sealed class LispExecutable() : LispData() { abstract fun execute( executionContext: LispExecutionContext, @@ -22,9 +16,7 @@ sealed class LispData { ): LispData } - - abstract class JavaExecutable : LispExecutable() { - } + abstract class JavaExecutable : LispExecutable() data class LispInterpretedCallable( val declarationStackFrame: StackFrame, @@ -39,7 +31,10 @@ sealed class LispData { args: List ): LispData { if (argNames.size != args.size) { - TODO("ERROR") + return executionContext.reportError( + "Expected ${argNames.size} arguments, got ${args.size} instead", + callsite + ) } val invocationFrame = declarationStackFrame.fork() @@ -50,21 +45,8 @@ sealed class LispData { } } - interface LispClass { - fun access(obj: T, name: String): LispData - fun instantiate(obj: T) = LispObject(obj, this) - } - - object LispStringClass : LispClass { - override fun access(obj: String, name: String): LispData { - return LispNil - } - } companion object { - fun string(value: String): LispObject = - LispStringClass.instantiate(value) - fun externalRawCall(callable: (context: LispExecutionContext, callsite: LispAst.LispNode, stackFrame: StackFrame, args: List) -> LispData): LispExecutable { return object : JavaExecutable() { override fun execute( diff --git a/src/LispExecutionContext.kt b/src/LispExecutionContext.kt index f169ba9..20d581e 100644 --- a/src/LispExecutionContext.kt +++ b/src/LispExecutionContext.kt @@ -31,14 +31,11 @@ class LispExecutionContext() { val rest = node.items.drop(1) return when (val resolvedValue = resolveValue(stackFrame, first)) { - is LispData.Atom -> reportError("Cannot execute atom", node) - LispData.LispNil -> reportError("Cannot execute nil", node) - is LispData.LispNumber -> reportError("Cannot execute number", node) - is LispData.LispNode -> reportError("Cannot execute node", node) - is LispData.LispObject<*> -> reportError("Cannot execute object-value", node) is LispData.LispExecutable -> { resolvedValue.execute(this, node, stackFrame, rest) } + + else -> reportError("Cannot evaluate expression of type $resolvedValue", node) } } @@ -53,8 +50,8 @@ class LispExecutionContext() { is LispAst.Parenthesis -> executeLisp(stackFrame, node) is LispAst.Reference -> stackFrame.resolveReference(node.label) ?: reportError("Could not resolve variable ${node.label}", node) - - is LispAst.StringLiteral -> LispData.string(node.parsedString) + is LispAst.NumberLiteral -> LispData.LispNumber(node.numberValue) + is LispAst.StringLiteral -> LispData.LispString(node.parsedString) } } } diff --git a/src/LispParser.kt b/src/LispParser.kt index 1117397..e005c2d 100644 --- a/src/LispParser.kt +++ b/src/LispParser.kt @@ -49,6 +49,9 @@ class LispParser private constructor(filename: String, string: String) { if (paren == "\"") { return parseString() } + if (paren in digits) { + return parseNumber() + } if (paren == ":") { return parseAtom() } @@ -56,6 +59,18 @@ class LispParser private constructor(filename: String, string: String) { return LispAst.Reference(racer.span(start), ident) } + fun parseNumber(): LispAst.NumberLiteral { + val start = racer.idx + racer.pushState() + val number = racer.consumeWhile { it.last().let { it in digits || it == '.' } } + val double = number.toDoubleOrNull() + if (double == null) { + racer.popState() + racer.error("Could not parse number") + } + racer.discardState() + return LispAst.NumberLiteral(racer.span(start), double) + } fun parseAtom(): LispAst.Atom { val start = racer.idx diff --git a/test/res/test.lisp b/test/res/test.lisp index f3d21d9..06e9f43 100644 --- a/test/res/test.lisp +++ b/test/res/test.lisp @@ -5,7 +5,4 @@ (debuglog a) (def helloworld (pure "hello world")) (debuglog helloworld (helloworld)) -(defun + () (seq - (debuglog "also multiplication") - (debuglog "addition"))) -(debuglog +) +(debuglog + (+ 1.2 15)) -- cgit