2020-01-05 20:49:41 +03:00
include "utils";
2020-01-05 22:59:12 +03:00
def childEnv(binds; exprs):
2020-01-05 20:49:41 +03:00
{
parent: .,
2020-01-06 10:30:26 +03:00
fallback: null,
2020-01-05 22:59:12 +03:00
environment: [binds, exprs] | transpose | (
. as $dot | reduce .[] as $item (
{ value: [], seen: false, name: null, idx: 0 };
if $item[1] != null then
if .seen then
{
value: (.value[1:-1] + (.value|last[1].value += [$item[1]])),
seen: true,
name: .name
}
else
if $item[0] == "&" then
$dot[.idx+1][0] as $name | {
value: (.value + [[$name, {kind:"list", value: [$item[1]]}]]),
seen: true,
name: $name
}
else
{
value: (.value + [$item]),
seen: false,
name: null
}
end
end | (.idx |= .idx + 1)
2020-01-05 23:22:44 +03:00
else
if $item[0] == "&" then
$dot[.idx+1][0] as $name | {
value: (.value + [[$name, {kind:"list", value: []}]]),
seen: true,
name: $name
}
else . end
end
2020-01-05 22:59:12 +03:00
)
) | .value | map({(.[0]): .[1]}) | add
2020-01-05 20:49:41 +03:00
};
def pureChildEnv:
{
parent: .,
2020-01-06 10:30:26 +03:00
environment: {},
2020-01-10 09:01:00 +03:00
fallback: null
2020-01-05 20:49:41 +03:00
};
def rootEnv:
{
parent: null,
2020-01-06 10:30:26 +03:00
fallback: null,
2020-01-10 09:01:00 +03:00
environment: {}
2020-01-05 20:49:41 +03:00
};
2020-01-06 02:40:03 +03:00
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):
2020-01-09 19:07:24 +03:00
(if value.kind == "function" then # multiset not allowed on atoms
2020-01-06 02:40:03 +03:00
value | inform_function_multi(keys)
else
value
end) as $value | {
2020-01-05 20:49:41 +03:00
parent: .parent,
2020-01-06 02:40:03 +03:00
environment: (
.environment + (reduce keys[] as $key(.environment; .[$key] |= value))
2020-01-08 17:36:57 +03:00
),
2020-01-10 09:01:00 +03:00
fallback: .fallback
2020-01-05 20:49:41 +03:00
};
2020-01-06 02:40:03 +03:00
def env_multiset(env; keys; value):
env | env_multiset(keys; value);
def env_set($key; $value):
2020-01-06 23:55:22 +03:00
(if $value.kind == "function" or $value.kind == "atom" then
2020-01-09 19:07:24 +03:00
# 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 |
if $ours.last_modified > $value.last_modified then
$ours
else
# update modification timestamp
$value | .last_modified |= now
end
else
.
end) | inform_function($key)
2020-01-06 02:40:03 +03:00
else
$value
end) as $value | {
parent: .parent,
2020-01-08 17:36:57 +03:00
environment: (.environment + (.environment | .[$key] |= $value)), # merge together, as .environment[key] |= value does not work
2020-01-10 09:01:00 +03:00
fallback: .fallback
2020-01-06 02:40:03 +03:00
};
2020-01-09 19:07:24 +03:00
def env_dump_keys:
2020-01-08 17:36:57 +03:00
def _dump1:
2020-01-06 23:55:22 +03:00
.environment // {} | keys;
2020-01-08 17:36:57 +03:00
if . == null then [] else
if .parent == null then
2020-01-09 19:07:24 +03:00
(
_dump1 +
(.fallback | env_dump_keys)
)
2020-01-08 17:36:57 +03:00
else
2020-01-09 19:07:24 +03:00
(
_dump1 +
(.parent | env_dump_keys) +
(.fallback | env_dump_keys)
)
end | unique
end;
def env_find(env):
if env.environment[.] == null then
if env.parent then
env_find(env.parent) // if env.fallback then env_find(env.fallback) else null end
else
null
2020-01-08 17:36:57 +03:00
end
2020-01-09 19:07:24 +03:00
else
env
2020-01-06 10:30:26 +03:00
end;
2020-01-09 19:07:24 +03:00
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
end
end;
2020-01-08 17:36:57 +03:00
2020-01-06 02:40:03 +03:00
def env_set(env; $key; $value):
2020-01-16 07:51:46 +03:00
(if $value.kind == "function" then
2020-01-06 23:55:22 +03:00
# inform the function/atom of its names
2020-01-16 07:02:22 +03:00
$value | (.names += [$key]) | (.names |= unique)
2020-01-06 02:40:03 +03:00
else
$value
end) as $value | {
2020-01-05 20:49:41 +03:00
parent: env.parent,
2020-01-08 17:36:57 +03:00
environment: ((env.environment // jqmal_error("Environment empty in \(env | keys)")) + (env.environment | .[$key] |= $value)), # merge together, as env.environment[key] |= value does not work
2020-01-10 09:01:00 +03:00
fallback: env.fallback
2020-01-05 20:49:41 +03:00
};
2020-01-06 10:30:26 +03:00
def env_setfallback(env; fallback):
{
parent: env.parent,
fallback: fallback,
2020-01-10 09:01:00 +03:00
environment: env.environment
2020-01-06 10:30:26 +03:00
};
2020-01-07 23:26:31 +03:00
2020-01-05 20:49:41 +03:00
def addEnv(env):
{
expr: .,
env: env
};
def addToEnv(env; name; expr):
{
expr: expr,
env: env_set(env; name; expr)
};
2020-01-06 19:26:48 +03:00
2020-01-10 09:01:00 +03:00
def wrapEnv(atoms):
2020-01-06 19:26:48 +03:00
{
replEnv: .,
currentEnv: .,
2020-01-10 09:01:00 +03:00
atoms: atoms,
2020-01-06 19:26:48 +03:00
isReplEnv: true
};
2020-01-10 09:01:00 +03:00
def wrapEnv(replEnv; atoms):
2020-01-05 20:49:41 +03:00
{
2020-01-06 19:26:48 +03:00
replEnv: replEnv,
currentEnv: .,
2020-01-10 09:01:00 +03:00
atoms: atoms, # id -> value
2020-01-06 19:26:48 +03:00
isReplEnv: (replEnv == .) # should we allow separate copies?
};
def unwrapReplEnv:
.replEnv;
def unwrapCurrentEnv:
.currentEnv;
def env_set6(env; key; value):
if env.isReplEnv then
2020-01-10 09:01:00 +03:00
env_set(env.currentEnv; key; value) | wrapEnv(env.atoms)
2020-01-06 19:26:48 +03:00
else
2020-01-10 09:01:00 +03:00
env_set(env.currentEnv; key; value) | wrapEnv(env.replEnv; env.atoms)
2020-01-06 19:26:48 +03:00
end;
2020-01-06 23:55:22 +03:00
def env_set_(env; key; value):
if env.currentEnv != null then
env_set6(env; key; value)
else
env_set(env; key; value)
end;
2020-01-14 01:25:39 +03:00
def addToEnv(envexp; name):
2020-01-06 19:26:48 +03:00
envexp.expr as $value
| envexp.env as $rawEnv
| (if $rawEnv.isReplEnv then
2020-01-10 09:01:00 +03:00
env_set_($rawEnv.currentEnv; name; $value) | wrapEnv($rawEnv.atoms)
2020-01-06 19:26:48 +03:00
else
2020-01-10 09:01:00 +03:00
env_set_($rawEnv.currentEnv; name; $value) | wrapEnv($rawEnv.replEnv; $rawEnv.atoms)
2020-01-06 19:26:48 +03:00
end) as $newEnv
| {
expr: $value,
env: $newEnv
};
2020-01-08 17:36:57 +03:00
def _env_remove_references(refs):
if . != null then
2020-01-16 07:51:46 +03:00
if .environment == null then
_debug("This one broke the rules, officer: \(.)")
else
{
environment: (.environment | to_entries | map(select(.key as $key | refs | contains([$key]) | not)) | from_entries),
parent: (.parent | _env_remove_references(refs)),
fallback: (.fallback | _env_remove_references(refs))
}
end
2020-01-08 17:36:57 +03:00
else . end;
def env_remove_references(refs):
. as $env
2020-01-16 07:51:46 +03:00
| if (refs|length == 0) then
# optimisation: most functions are purely lexical
$env
else
if has("replEnv") then
.currentEnv |= _env_remove_references(refs)
else
_env_remove_references(refs)
end
end;