-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jq: update interpreter, merge eval-ast, fix some env errors #685
Conversation
Original issue describing the change and converting the first set of implementations: kanaka#592 Tracking issue for other implementations: kanaka#657 All normal tests pass, but REGRESS and self-hosting fail. Steps: display the results from jq without python simplify/improve quasiquote simplify replenv construction Cosmetic: Update the interpreter from latest Debian/Ubuntu. move first core functions from steps4-A to core.jq simplify interprocess communication between run and utils.jq merge run and rts.py, simplify it
Hi @alimpfard, I know it's been a few years since you did the mal implementation in jq. We are working on refactoring all the implementations with some important simplifications to the process/guide (and often taking the opportunity to upgrade to newer versions of the target language). Any chance you would be willing to refresh your memory on this code base to help us with those changes? @asarhaddon has made a first pass in this PR do do that refactoring and gotten the basic tests to work but there is a failure during regression testing (running tests for earlier steps using stepA). Neither of us are jq experts and could use some assistance if you're willing. I think mostly it would be helping to understand and/or cleanup some of the environment related code in step3 and step6. |
Sure! I'll take a look later today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for self-hosting...well...I'll let the diff speak for its own mysteries:
diff --git a/impls/mal/env.mal b/impls/mal/env.mal
index dc0ee2ef..48216eb6 100644
--- a/impls/mal/env.mal
+++ b/impls/mal/env.mal
@@ -29,12 +29,6 @@
(def! env-as-map (fn* [env]
(dissoc @env :outer)))
-(def! env-get-or-nil (fn* [env k]
- (let* [ks (str k)
- e (env-find-str env ks)]
- (if e
- (get @e ks)))))
-
;; Private helper for env-get and env-get-or-nil.
(def! env-find-str (fn* [env ks]
(if env
@@ -43,6 +37,12 @@
env
(env-find-str (get data :outer) ks))))))
+(def! env-get-or-nil (fn* [env k]
+ (let* [ks (str k)
+ e (env-find-str env ks)]
+ (if e
+ (get @e ks)))))
+
(def! env-get (fn* [env k]
(let* [ks (str k)
e (env-find-str env ks)]
diff --git a/impls/mal/step7_quote.mal b/impls/mal/step7_quote.mal
index b1c6fb8f..a4c9efda 100644
--- a/impls/mal/step7_quote.mal
+++ b/impls/mal/step7_quote.mal
@@ -11,16 +11,6 @@
;; eval
-(def! qq-loop (fn* [elt acc]
- (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
- (if (= 2 (count elt))
- (list 'concat (nth elt 1) acc)
- (throw "splice-unquote expects 1 argument"))
- (list 'cons (QUASIQUOTE elt) acc))))
-(def! qq-foldr (fn* [xs]
- (if (empty? xs)
- ()
- (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! QUASIQUOTE (fn* [ast]
(cond
(vector? ast) (list 'vec (qq-foldr ast))
@@ -31,6 +21,16 @@
(nth ast 1)
(throw "unquote expects 1 argument"))
"else" (qq-foldr ast))))
+(def! qq-loop (fn* [elt acc]
+ (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
+ (if (= 2 (count elt))
+ (list 'concat (nth elt 1) acc)
+ (throw "splice-unquote expects 1 argument"))
+ (list 'cons (QUASIQUOTE elt) acc))))
+(def! qq-foldr (fn* [xs]
+ (if (empty? xs)
+ ()
+ (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! LET (fn* [env binds form]
(if (empty? binds)
diff --git a/impls/mal/step8_macros.mal b/impls/mal/step8_macros.mal
index e1c88392..4df91755 100644
--- a/impls/mal/step8_macros.mal
+++ b/impls/mal/step8_macros.mal
@@ -11,16 +11,6 @@
;; eval
-(def! qq-loop (fn* [elt acc]
- (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
- (if (= 2 (count elt))
- (list 'concat (nth elt 1) acc)
- (throw "splice-unquote expects 1 argument"))
- (list 'cons (QUASIQUOTE elt) acc))))
-(def! qq-foldr (fn* [xs]
- (if (empty? xs)
- ()
- (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! QUASIQUOTE (fn* [ast]
(cond
(vector? ast) (list 'vec (qq-foldr ast))
@@ -31,6 +21,16 @@
(nth ast 1)
(throw "unquote expects 1 argument"))
"else" (qq-foldr ast))))
+(def! qq-loop (fn* [elt acc]
+ (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
+ (if (= 2 (count elt))
+ (list 'concat (nth elt 1) acc)
+ (throw "splice-unquote expects 1 argument"))
+ (list 'cons (QUASIQUOTE elt) acc))))
+(def! qq-foldr (fn* [xs]
+ (if (empty? xs)
+ ()
+ (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! LET (fn* [env binds form]
(if (empty? binds)
diff --git a/impls/mal/step9_try.mal b/impls/mal/step9_try.mal
index 54403bfd..6e8351cb 100644
--- a/impls/mal/step9_try.mal
+++ b/impls/mal/step9_try.mal
@@ -11,16 +11,6 @@
;; eval
-(def! qq-loop (fn* [elt acc]
- (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
- (if (= 2 (count elt))
- (list 'concat (nth elt 1) acc)
- (throw "splice-unquote expects 1 argument"))
- (list 'cons (QUASIQUOTE elt) acc))))
-(def! qq-foldr (fn* [xs]
- (if (empty? xs)
- ()
- (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! QUASIQUOTE (fn* [ast]
(cond
(vector? ast) (list 'vec (qq-foldr ast))
@@ -31,6 +21,16 @@
(nth ast 1)
(throw "unquote expects 1 argument"))
"else" (qq-foldr ast))))
+(def! qq-loop (fn* [elt acc]
+ (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
+ (if (= 2 (count elt))
+ (list 'concat (nth elt 1) acc)
+ (throw "splice-unquote expects 1 argument"))
+ (list 'cons (QUASIQUOTE elt) acc))))
+(def! qq-foldr (fn* [xs]
+ (if (empty? xs)
+ ()
+ (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! LET (fn* [env binds form]
(if (empty? binds)
diff --git a/impls/mal/stepA_mal.mal b/impls/mal/stepA_mal.mal
index 43f69409..c816431b 100644
--- a/impls/mal/stepA_mal.mal
+++ b/impls/mal/stepA_mal.mal
@@ -11,16 +11,6 @@
;; eval
-(def! qq-loop (fn* [elt acc]
- (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
- (if (= 2 (count elt))
- (list 'concat (nth elt 1) acc)
- (throw "splice-unquote expects 1 argument"))
- (list 'cons (QUASIQUOTE elt) acc))))
-(def! qq-foldr (fn* [xs]
- (if (empty? xs)
- ()
- (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! QUASIQUOTE (fn* [ast]
(cond
(vector? ast) (list 'vec (qq-foldr ast))
@@ -31,6 +21,16 @@
(nth ast 1)
(throw "unquote expects 1 argument"))
"else" (qq-foldr ast))))
+(def! qq-loop (fn* [elt acc]
+ (if (if (list? elt) (= (first elt) 'splice-unquote)) ; 2nd 'if' means 'and'
+ (if (= 2 (count elt))
+ (list 'concat (nth elt 1) acc)
+ (throw "splice-unquote expects 1 argument"))
+ (list 'cons (QUASIQUOTE elt) acc))))
+(def! qq-foldr (fn* [xs]
+ (if (empty? xs)
+ ()
+ (qq-loop (first xs) (qq-foldr (rest xs))))))
(def! LET (fn* [env binds form]
(if (empty? binds)
impls/jq/step5_tco.jq
Outdated
( | ||
select(.kind == "vector") | | ||
.value | | ||
reduce .[] as $x ({expr:[], env:env}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue with REGRESS is simple enough, this should be $_menv
(env is the initial env, which is why you get "b not found" in the regression test), you use the right value for later steps.
impls/jq/step5_tco.jq
Outdated
. | TCOWrap($_menv; $_orig_retenv; false) # return this unchanged, since it can only be applied to | ||
) // | ||
(select(.kind == "symbol") | | ||
( | ||
select(.kind == "symbol") | | ||
.value | env_get($_menv) | TCOWrap($_menv; null; false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other half of REGRESS failures are not your fault, they were here from before.
This should be
.value | env_get($_menv) | TCOWrap($_menv; null; false) | |
.value | env_get($_menv) | TCOWrap($_menv; $_orig_retenv; false) |
(as the evaluation of a symbol does not affect the resulting env)
I don't quite understand why it's okay for @asarhaddon @kanaka what cleanups are we talking about exactly? I can help out with refactors/cleanups but I'm not familiar with whatever has changed in MAL since I last looked at it 😅 |
Fix by alimpfard.
instead of abusing env_find
for readability
The procedure is short and its name not explicit.
probably once useful for empty lists
Hello. Maintenance would be easyer if the obsolete code was removed and the diff between files reduced. |
I believe most of the env differences between step 5 & 6 are due to the existence of atoms, and how they're supposed to interact with different scoping rules (env vs. $_menv is almost entirely about this), e.g. let* wouldn't "leak" atoms if we passed the old env through. I imagine most of the languages that lack global state will have this difference between step 5 & 6? |
Nice work. That was a big lift! @alimpfard do you think you might be able to add some documentation about the step 4, 5, 6 differences from the typical language pattern? I have an intuitive sense about why that is the case (due to jq's strict immutable nature), but some concrete explanation from the person who wrote it would certainly be appreciated. |
I'll see if I can draft something up (might take a couple days though, a little busy IRL) |
Hello.
This fixes #657 for jq, as well as a few regressions (even step0 fails with the current interpreter version).
I have also done some source cleaning, but env.jq and steps 3 to 6 stil share too much similar and partially broken code.
I have failed to figure how the stuff is supposed to work.
I suggest to first ask help by the original author in removing the code duplications and documenting the design choices,
and only then fix REGRESS=1 and/or self hosting (#662), if stil necessary.