2 Commits

Author SHA1 Message Date
95780bd06c implement method/function call
Some checks failed
CI / test-core (14, ubuntu-latest, 3.x, js) (push) Successful in 48s
CI / test-core (14, ubuntu-latest, 3.x, interp) (push) Successful in 1m2s
CI / test-core (14, ubuntu-latest, 3.x, nodejs) (push) Successful in 57s
CI / test-core (14, ubuntu-latest, 3.x, cpp) (push) Successful in 1m49s
CI / test-core (14, ubuntu-latest, 3.x, py) (push) Failing after 1m19s
2025-09-12 20:51:44 -05:00
269156dd09 implement field access 2025-09-12 20:35:00 -05:00
3 changed files with 70 additions and 5 deletions

View File

@@ -4,6 +4,8 @@
(import kiss.Reader.ReadTable)
(import kiss.ReaderExp)
(import kiss.ReaderExp.ReaderExpDef)
(import kiss.ReaderExp.ReaderExpDef.Symbol)
(import kiss.ReaderExp.ReaderExpDef.FieldExp)
(import kiss.Stream)
(prop &mut :ReadTable readTable (Reader.builtins))
@@ -20,7 +22,7 @@
(method :Map<String,(Array<ReaderExp>,Dynamic->Void)->Void> _specialForms [] [
=>"if"
->[args cc]
(evalCC (first ~args)
(evalCC (first args)
->val
(if val
(evalCC (second args) cc)
@@ -42,14 +44,46 @@
(throw "Couldn't read valid expression from $s")))
([:ReaderExpDef def]
(case def
// Special form call
((when (specialForms.exists form) (CallExp (object def (Symbol form)) args))
((dictGet specialForms form) args cc))
// Method/function call
((CallExp callable args)
(evalCC callable ->f
(_evalAllCC args ->values
(case callable
// Method call
((object def (FieldExp field obj safe))
(evalCC obj ->o
(cc (Reflect.callMethod o f values))))
// Function call
(otherwise (cc (Reflect.callMethod null f values)))))))
// Field access
((FieldExp field obj safe)
(evalCC obj ->v
(if v
(cc (Reflect.getProperty v field))
(if safe
(cc null)
(throw "field access on null! $(Reader.toString obj.def)")))))
// Symbol
((Symbol ident)
// Check for numbers
(let [f (Std.parseFloat ident)]
(unless (Math.isNaN f)
(cc f)
(return)))
// Check for field access
(let [idx (ident.indexOf ".")]
(unless (= -1 idx)
(let [parts (ident.split ".")
&mut exp (object pos null def (Symbol (first parts)))]
(doFor part (parts.slice 1)
(let [safe (StringTools.startsWith part "?")]
(when safe (set part (part.substr 1)))
(set exp (object pos null def (FieldExp part exp safe)))))
(evalCC exp cc)
(return))))
(doFor i (range localScopes.length)
(let [scope (nth localScopes (- localScopes.length i 1))]
(when (scope.exists ident)
@@ -57,4 +91,13 @@
(return))))
(cc (dictGet globals ident)))
(never otherwise)))
(otherwise (throw "Can't interpret ${input}"))))
(otherwise (throw "Can't interpret ${input}"))))
(method :Void _evalAllCC [:Array<Dynamic> inputs :Array<Dynamic>->Void cc &opt :Array<Dynamic> outputs]
(unless outputs (set outputs []))
(if inputs
(evalCC (inputs.shift) ->v {
(outputs.push v)
(_evalAllCC inputs cc outputs)
})
(cc outputs)))

View File

@@ -19,4 +19,10 @@ class KissInterp2TestCase extends Test {
function testIf() {
_testIf();
}
function testField() {
_testField();
}
function testCallMethod() {
_testCallMethod();
}
}

View File

@@ -1,9 +1,25 @@
(defMacro assertEval [expected exp]
`(interp.evalCC ,exp ->v (Assert.equals ,expected v)))
(function _testEvalGlobal []
(let [interp (new Interp)]
(dictSet interp.globals "a" 5)
(interp.evalCC "a" ->v (Assert.equals 5 v))))
(assertEval 5 "a")))
(function _testIf []
(let [interp (new Interp)]
(interp.evalCC "(if true 5 3)" ->v (Assert.equals 5 v))
(interp.evalCC "(if false 5 3)" ->v (Assert.equals 3 v))))
(assertEval 5 "(if true 5 3)")
(assertEval 3 "(if false 5 3)")))
(function _testField []
(let [interp (new Interp)]
(dictSet interp.globals "obj" (object a 5 b (object c 3)))
(assertEval 5 "obj.a")
(assertEval 3 "obj.b.c")))
(function _testCallMethod []
(let [interp (new Interp)]
(dictSet interp.globals "f" ->[a b] (+ a b))
(dictSet interp.globals "o" (object f ->[a b] (- a b)))
(assertEval 2 "(f -1 3)")
(assertEval 2 "(o.f 5 3)")))