Skip to content
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

Merged
merged 20 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions impls/jq/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:bionic
FROM ubuntu:24.04
MAINTAINER Joel Martin <[email protected]>

##########################################################
Expand All @@ -9,10 +9,8 @@ MAINTAINER Joel Martin <[email protected]>
RUN apt-get -y update

# Required for running tests
RUN apt-get -y install make python

# Some typical implementation and test requirements
RUN apt-get -y install curl libreadline-dev libedit-dev libpcre3-dev
RUN apt-get -y install make python3
RUN ln -fs /usr/bin/python3 /usr/local/bin/python

RUN mkdir -p /mal
WORKDIR /mal
Expand All @@ -21,12 +19,4 @@ WORKDIR /mal
# Specific implementation requirements
#########################################################

RUN apt-get -y install python3.8 wget
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.8 10

# grab jq 1.6 from github releases
RUN wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64

RUN chmod +x jq-linux64
# a bit ugly, but it'll do?
RUN mv jq-linux64 /usr/bin/jq
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install jq
10 changes: 9 additions & 1 deletion impls/jq/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
all:

.PHONY: clean
clean:
rm -fr .mypy_cache/

check:
flake8 run
pylint run
mypy run

.PHONY: all clean check
29 changes: 27 additions & 2 deletions impls/jq/core.jq
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,31 @@ include "reader";

def core_identify:
{
"+": {
kind: "fn", # native function
inputs: 2,
function: "number_add"
},
"-": {
kind: "fn", # native function
inputs: 2,
function: "number_sub"
},
"*": {
kind: "fn", # native function
inputs: 2,
function: "number_mul"
},
"/": {
kind: "fn", # native function
inputs: 2,
function: "number_div"
},
"eval": {
kind: "fn",
inputs: 1,
function: "eval"
},
"env": {
kind: "fn",
function: "env",
Expand Down Expand Up @@ -369,9 +394,9 @@ def core_interp(arguments; env):
) // (
select(.function == ">=") | null | wrap(arguments[0].value >= arguments[1].value | tostring)
) // (
select(.function == "slurp") | arguments | map(.value) | issue_extern("read") | wrap("string")
select(.function == "slurp") | arguments[0].value | slurp | wrap("string")
) // (
select(.function == "read-string") | arguments | first.value | read_str | read_form.value
select(.function == "read-string") | arguments | first.value | read_form
) // (
select(.function == "atom?") | null | wrap(arguments | first.kind == "atom" | tostring)
) // (
Expand Down
159 changes: 37 additions & 122 deletions impls/jq/env.jq
Original file line number Diff line number Diff line change
Expand Up @@ -42,52 +42,16 @@ def childEnv(binds; exprs):
) | .value | map({(.[0]): .[1]}) | add
};

def pureChildEnv:
{
parent: .,
environment: {},
fallback: null
};

def rootEnv:
{
parent: null,
fallback: null,
environment: {}
};

def inform_function(name):
(.names += [name]) | (.names |= unique);

def inform_function_multi(names):
. as $dot | reduce names[] as $name(
$dot;
inform_function($name)
);

def env_multiset(keys; value):
(if value.kind == "function" then # multiset not allowed on atoms
value | inform_function_multi(keys)
else
value
end) as $value | {
parent: .parent,
environment: (
.environment + (reduce keys[] as $key(.environment; .[$key] |= value))
),
fallback: .fallback
};

def env_multiset(env; keys; value):
env | env_multiset(keys; value);
def env_multiset(fn):
.environment += (reduce fn.names[] as $key(.environment; .[$key] |= fn));

def env_set($key; $value):
(if $value.kind == "function" or $value.kind == "atom" then
# inform the function/atom of its names
($value |
if $value.kind == "atom" then
# check if the one we have is newer
env_req(env; key) as $ours |
($key | env_get(env)) as $ours |
if $ours.last_modified > $value.last_modified then
$ours
else
Expand All @@ -96,14 +60,15 @@ def env_set($key; $value):
end
else
.
end) | inform_function($key)
end) |
.names += [$key] |
.names |= unique

else
$value
end) as $value | {
parent: .parent,
environment: (.environment + (.environment | .[$key] |= $value)), # merge together, as .environment[key] |= value does not work
fallback: .fallback
};
end) as $value |
# merge together, as .environment[$key] |= value does not work
.environment += (.environment | .[$key] |= $value);

def env_dump_keys:
def _dump1:
Expand All @@ -123,6 +88,7 @@ def env_dump_keys:
end | unique
end;

# Helper for env_get.
def env_find(env):
if env.environment[.] == null then
if env.parent then
Expand All @@ -135,41 +101,14 @@ def env_find(env):
end;

def env_get(env):
. as $key | $key | env_find(env).environment[$key] as $value |
if $value == null then
jqmal_error("'\($key)' not found")
else
if $value.kind == "atom" then
$value.identity as $id |
$key | env_find(env.parent).environment[$key] as $possibly_newer |
if $possibly_newer.identity == $id and $possibly_newer.last_modified > $value.last_modified then
$possibly_newer
else
$value
end
else
$value
end
end;

def env_get(env; key):
key | env_get(env);

def env_req(env; key):
key as $key | key | env_find(env).environment[$key] as $value |
if $value == null then
null
else
if $value.kind == "atom" then
$value.identity as $id |
$key | env_find(env.parent).environment[$key] as $possibly_newer |
if $possibly_newer.identity == $id and $possibly_newer.last_modified > $value.last_modified then
$possibly_newer
else
$value
end
else
$value
# key -> value or null
. as $key | env_find(env).environment[$key] |
if . != null and .kind == "atom" then
($key | env_find(env.parent).environment[$key]) as $possibly_newer |
if $possibly_newer.identity == .identity
and $possibly_newer.last_modified > .last_modified
then
$possibly_newer
end
end;

Expand All @@ -185,26 +124,6 @@ def env_set(env; $key; $value):
fallback: env.fallback
};

def env_setfallback(env; fallback):
{
parent: env.parent,
fallback: fallback,
environment: env.environment
};

def addEnv(env):
{
expr: .,
env: env
};

def addToEnv(env; name; expr):
{
expr: expr,
env: env_set(env; name; expr)
};


def wrapEnv(atoms):
{
replEnv: .,
Expand All @@ -227,37 +146,33 @@ def unwrapReplEnv:
def unwrapCurrentEnv:
.currentEnv;

def env_set6(env; key; value):
if env.isReplEnv then
env_set(env.currentEnv; key; value) | wrapEnv(env.atoms)
else
env_set(env.currentEnv; key; value) | wrapEnv(env.replEnv; env.atoms)
end;

def env_set_(env; key; value):
if env.currentEnv != null then
env_set6(env; key; value)
# Moving the common env_set before the if breaks something. ?
if env.isReplEnv then
env_set(env.currentEnv; key; value) | wrapEnv(env.atoms)
else
env_set(env.currentEnv; key; value) | wrapEnv(env.replEnv; env.atoms)
end
else
env_set(env; key; value)
end;

def addToEnv(envexp; name):
envexp.expr as $value
| envexp.env as $rawEnv
| (if $rawEnv.isReplEnv then
env_set_($rawEnv.currentEnv; name; $value) | wrapEnv($rawEnv.atoms)
else
env_set_($rawEnv.currentEnv; name; $value) | wrapEnv($rawEnv.replEnv; $rawEnv.atoms)
end) as $newEnv
| {
expr: $value,
env: $newEnv
};
def addToEnv(name):
# { expr, env } -> { same expr, new env }
.expr as $value |
.env |= (
. as $rawEnv |
if .isReplEnv then
env_set_(.currentEnv; name; $value) | wrapEnv($rawEnv.atoms)
else
env_set_(.currentEnv; name; $value) | wrapEnv($rawEnv.replEnv; $rawEnv.atoms)
end);

def _env_remove_references(refs):
if . != null then
if .environment == null then
_debug("This one broke the rules, officer: \(.)")
debug("This one broke the rules, officer: \(.)")
else
{
environment: (.environment | to_entries | map(select(.key as $key | refs | contains([$key]) | not)) | from_entries),
Expand All @@ -278,4 +193,4 @@ def env_remove_references(refs):
else
_env_remove_references(refs)
end
end;
end;
Loading