continue, break in KissInterp2
Some checks failed
CI / test (push) Failing after 53s
CI / test-core (14, ubuntu-latest, 3.x, py) (push) Failing after 1m46s
CI / test-core (14, ubuntu-latest, 3.x, cpp) (push) Failing after 1m43s
CI / test-core (14, ubuntu-latest, 3.x, js) (push) Failing after 2m1s
CI / test-core (14, ubuntu-latest, 3.x, interp) (push) Failing after 2m8s
CI / test-core (14, ubuntu-latest, 3.x, nodejs) (push) Failing after 2m2s

This commit is contained in:
2025-11-17 15:54:10 -06:00
parent 886b6f7ce5
commit 7117846294
4 changed files with 131 additions and 45 deletions

View File

@@ -53,4 +53,10 @@ class KissInterp2 {
return fields;
}
}
#end
#end
enum ControlFlow {
Continue;
Break;
Return(v:Dynamic);
}

View File

@@ -60,9 +60,17 @@
(localScopes.pop)
(cc results)
(return))
(evalCC (callSymbol "let" (.concat [(listExp [varName (callSymbol "__iter__.next" [])])] body))
->v (when collect
(results.push v)))
(try
(handleLet (.concat [(listExp [varName (callSymbol "__iter__.next" [])])] body)
->v
(when collect
(results.push v)) true)
(catch [:kiss.KissInterp2.ControlFlow control]
(case control
(Break (localScopes.pop)
(cc results))
(Continue 0)
(otherwise (throw control)))))
(run))
(run)))))
@@ -97,16 +105,56 @@
(if hasOpt
null
(throw "not enough arguments! need $argName")))))
(evalCC (callSymbol "begin" bodyExps)
->v
{
(set localScopes currentLocals)
(set returnValue v)
})
(handleBegin bodyExps ->v (set returnValue v) false true)
returnValue))
f (Reflect.makeVarArgs f_)]
f)))
(method handleBegin [:Array<ReaderExp> args :Dynamic->Void cc &opt :Bool pushScope :Bool handleReturn :Bool handleLoopFlow]
(when pushScope (localScopes.push (new Map<String,Dynamic>)))
(when handleReturn (dictSet (last localScopes) "return" ->v (throw (kiss.KissInterp2.ControlFlow.Return v))))
(when handleLoopFlow
(dictSet (last localScopes) "continue" ->[] (throw kiss.KissInterp2.ControlFlow.Continue))
(dictSet (last localScopes) "break" ->(throw kiss.KissInterp2.ControlFlow.Break)))
(try
(_evalAllCC args ->values {
(when pushScope (localScopes.pop))
(cc (values.pop))
})
(catch [:kiss.KissInterp2.ControlFlow control]
(when pushScope (localScopes.pop)) // Not needed if it's a return, but also not a problem
(case control
((when handleReturn (Return v))
(cc v))
(otherwise (throw control))))))
(method handleLet [:Array<ReaderExp> args :Dynamic->Void cc &opt :Bool forLoop]
(let [bindings (first args)
bindings (.copy (Prelude.bindingList bindings "let"))
body (rest args)
scope (new Map<String,Dynamic>)]
(localScopes.push scope)
(withFunctions [
(nextVar []
(if bindings
(let [v (bindings.shift)
val (bindings.shift)]
(evalCC val ->val
(case v.def
((Symbol vName)
(dictSet scope vName val))
((ListExp names)
(doFor [i s] (enumerate names)
(dictSet scope (symbolNameValue s) (nth val i))))
(never otherwise)))
(nextVar))
(handleBegin body ->result {
(localScopes.pop)
(cc result)
} false false ?forLoop)))
] (nextVar))))
(method :Map<String,(Array<ReaderExp>,Dynamic->Void)->Void> _specialForms [] [
=>"if"
->[args cc]
@@ -115,49 +163,29 @@
(if val
(evalCC (second args) cc)
(evalCC (third args) cc)))
// TODO handle early return, loop break/continue
// TODO this should make a local scope!
=>"begin"
->[args cc]
(_evalAllCC args ->values
(cc (values.pop)))
->[args cc] (handleBegin args cc true) // push scope
=>"var"
(declareInScope globals)
=>"localVar"
(declareInScope (nth localScopes -1))
=>"let"
->[args cc]
(let [bindings (first args)
bindings (.copy (Prelude.bindingList bindings "let"))
body (rest args)
scope (new Map<String,Dynamic>)]
(localScopes.push scope)
(withFunctions [
(nextVar []
(if bindings
(let [v (bindings.shift)
val (bindings.shift)]
(evalCC val ->val
(case v.def
((Symbol vName)
(dictSet scope vName val))
((ListExp names)
(doFor [i s] (enumerate names)
(dictSet scope (symbolNameValue s) (nth val i))))
(never otherwise)))
(nextVar))
(evalCC (callSymbol "begin" body) ->result {
(localScopes.pop)
(cc result)
})))
] (nextVar)))
(handleLet args cc)
=>"new"
->[args cc]
// TODO hscript tries to resolve types by name using Type.resolveClass before trying to resolve them with class objects
// in the interp variables. That might be nice.
(_evalAllCC args ->vals
(cc (Type.createInstance (first vals) (rest vals))))
// Special case: Types that can't be stored as values and constructed with reflection
(case .def (first args)
((Symbol "Map")
(cc (new Map<String,Dynamic>)))
(otherwise
// TODO hscript tries to resolve types by name using Type.resolveClass before trying to resolve them with class objects
// in the interp variables. That might be nice. Right now they have to be in globals
(_evalAllCC args ->vals
(cc (Type.createInstance (first vals) (rest vals))))))
// TODO nth, dictGet, setNth, dictSet would all be more DRY as functions.
// Should they be?
=>"nth"
->[args cc]
(evalCC (first args)
@@ -172,6 +200,29 @@
(evalCC (second args)
->key
(cc (.get (cast container Map<String,Dynamic>) key))))
=>"setNth"
->[args cc]
(evalCC (first args)
->container
(evalCC (second args)
->index
(evalCC (third args)
->value
{
(setNth container index value)
(cc value)
})))
=>"dictSet"
->[args cc]
(evalCC (first args)
->container
(evalCC (second args)
->key
(evalCC (third args)
->value {
(.set (cast container Map<String,Dynamic>) key value)
(cc value)
})))
=>"lambda"
->[args cc]
(let [argExps (Prelude.argList (first args) "lambda") bodyExps (rest args)]
@@ -199,7 +250,15 @@
(evalCC condition
->v
(if v
(evalCC (callSymbol "begin" body) ->_ (run))
(try
(handleBegin body ->_ (run) true false true)
(catch [:kiss.KissInterp2.ControlFlow control]
(case control
(Continue
(run))
(Break
(cc null))
(otherwise (throw control)))))
(cc null))))
(run))
=>"set"

View File

@@ -52,5 +52,14 @@ class KissInterp2TestCase extends Test {
function testListDestructure() {
_testListDestructure();
}
function testEarlyReturn() {
_testEarlyReturn();
}
function testBreak() {
_testBreak();
}
function testContinue() {
_testContinue();
}
}

View File

@@ -70,3 +70,15 @@
(function _testListDestructure []
(let [interp (new Interp)]
(assertEval [5 6 7] "(let [[a b c] [5 6 7]] [a b c])")))
(function _testEarlyReturn []
(let [interp (new Interp)]
(assertEval 5 "(let [f ->{(return 5) 6}] (f))")))
(function _testBreak []
(let [interp (new Interp)]
(assertEval [0 1 2 3] "(for i (range 10) (if (< i 4) i (break)))")))
(function _testContinue []
(let [interp (new Interp)]
(assertEval [0 2 4 6 8] "(for i (range 10) (if (= 0 (% i 2)) i (continue)))")))