Skip to content

Commit

Permalink
[hay] Don't mutate globals in Hay evaluation
Browse files Browse the repository at this point in the history
This goes for both 'hay eval :result { }' and eval_hay().

Add example of how to annotate commands / procs with top-level hay
nodes.  Still need to figure out how to serialize both hay nodes and
procs.  Procs probably need signatures too.
  • Loading branch information
Andy C committed Jun 11, 2022
1 parent f9193a5 commit d7c4068
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 64 deletions.
2 changes: 1 addition & 1 deletion core/shell.py
Expand Up @@ -461,7 +461,7 @@ def Main(lang, arg_r, environ, login_shell, loader, line_input):
AddOil(builtins, mem, search_path, cmd_ev, errfmt, procs, arena)

parse_config = funcs.ParseHay(fd_state, parse_ctx, errfmt)
eval_to_dict = funcs.EvalHay(hay_state, mutable_opts, cmd_ev)
eval_to_dict = funcs.EvalHay(hay_state, mutable_opts, mem, cmd_ev)
block_as_str = funcs.BlockAsStr(arena)
hay_func = funcs.HayFunc(hay_state)
funcs_builtin.Init3(mem, parse_config, eval_to_dict, block_as_str, hay_func)
Expand Down
8 changes: 5 additions & 3 deletions core/state.py
Expand Up @@ -304,11 +304,11 @@ class ctx_HayEval(object):
- you should be able to define them, but not call the user ...
"""
def __init__(self, hay_state, mutable_opts):
# type: (Hay, MutableOpts) -> None
#log('pairs %s', pairs)
def __init__(self, hay_state, mutable_opts, mem):
# type: (Hay, MutableOpts, Mem) -> None
self.hay_state = hay_state
self.mutable_opts = mutable_opts
self.mem = mem

if mutable_opts.Get(option_i._running_hay):
# This blames the right 'hay' location
Expand All @@ -319,6 +319,7 @@ def __init__(self, hay_state, mutable_opts):
mutable_opts.Push(option_i._running_hay, True)

self.hay_state.PushEval()
self.mem.PushTemp()

def __enter__(self):
# type: () -> None
Expand All @@ -327,6 +328,7 @@ def __enter__(self):
def __exit__(self, type, value, traceback):
# type: (Any, Any, Any) -> None

self.mem.PopTemp()
self.hay_state.PopEval()

self.mutable_opts.Pop(option_i._running_hay)
Expand Down
7 changes: 4 additions & 3 deletions oil_lang/funcs.py
Expand Up @@ -73,10 +73,11 @@ def Call(self, path):
class EvalHay(object):
""" eval_to_dict() """

def __init__(self, hay_state, mutable_opts, cmd_ev):
# type: (state.Hay, state.MutableOpts, cmd_eval.CommandEvaluator) -> None
def __init__(self, hay_state, mutable_opts, mem, cmd_ev):
# type: (state.Hay, state.MutableOpts, state.Mem, cmd_eval.CommandEvaluator) -> None
self.hay_state = hay_state
self.mutable_opts = mutable_opts
self.mem = mem
self.cmd_ev = cmd_ev

if mylib.PYTHON:
Expand All @@ -92,7 +93,7 @@ def Call(self, block):
UP_block = block
block = cast(value__Block, UP_block)

with state.ctx_HayEval(self.hay_state, self.mutable_opts):
with state.ctx_HayEval(self.hay_state, self.mutable_opts, self.mem):
unused = self.cmd_ev.EvalBlock(block.body)

return self.hay_state.Result()
Expand Down
2 changes: 1 addition & 1 deletion osh/builtin_pure.py
Expand Up @@ -929,7 +929,7 @@ def Run(self, cmd_val):
if not block: # 'package foo' is OK
e_usage('eval expected a block')

with state.ctx_HayEval(self.hay_state, self.mutable_opts):
with state.ctx_HayEval(self.hay_state, self.mutable_opts, self.mem):
# Note: we want all haynode invocations in the block to appear as
# our 'children', recursively
unused = self.cmd_ev.EvalBlock(block)
Expand Down
78 changes: 78 additions & 0 deletions spec/hay-isolation.test.sh
Expand Up @@ -56,3 +56,81 @@ status=127
_status 127
## END


#### builtins and externals not available in hay eval
shopt --set parse_brace
shopt --unset errexit

hay define Package

try {
hay eval :result {
Package foo {
/bin/ls
}
}
}
echo "status $_status"

try {
hay eval :result {
cd /tmp
}
}
echo "status $_status"

## STDOUT:
status 127
status 127
## END

#### procs in hay eval
shopt --set parse_brace parse_at parse_proc

hay define Package

proc outside {
echo outside
Package OUT
}

hay eval :result {
outside

proc inside {
echo inside
}

inside
}

const args = result['children'][0]['args']
write --sep ' ' -- $len(result['children']) @args

## STDOUT:
outside
inside
1 OUT
## END


#### variables mutated within hay eval don't persist
shopt --set parse_brace

hay define Package

setvar x = 42

hay eval :result {
Package foo

setvar x = 1
}

echo "x = $x"

## STDOUT:
x = 42
## END


56 changes: 0 additions & 56 deletions spec/hay.test.sh
Expand Up @@ -141,62 +141,6 @@ status=0
1 three
## END

#### builtins an externals not available in hay eval
shopt --set parse_brace
shopt --unset errexit

hay define Package

try {
hay eval :result {
Package foo {
/bin/ls
}
}
}
echo "status $_status"

try {
hay eval :result {
cd /tmp
}
}
echo "status $_status"

## STDOUT:
status 127
status 127
## END

#### procs in hay eval
shopt --set parse_brace parse_at parse_proc

hay define Package

proc outside {
echo outside
Package OUT
}

hay eval :result {
outside

proc inside {
echo inside
}

inside
}

const args = result['children'][0]['args']
write --sep ' ' -- $len(result['children']) @args

## STDOUT:
outside
inside
1 OUT
## END

#### hay eval attr node, and JSON
shopt --set parse_brace parse_equals

Expand Down
35 changes: 35 additions & 0 deletions spec/testdata/config/annotate-commands.oil
@@ -0,0 +1,35 @@
# A way that programs could annotate procs and external commands

hay define Sig

Sig grep {
pure = true
}

Sig foo {
pure = false
}

proc foo {
echo 'where is my sig?'
}

case ${1:-} {

# Is this a good pattern?
# You have to run it twice, which is not great.

(''|sigs)
json write (_hay())
;;

('procs')
pp proc
;;

# TODO: would be nice to validate this
(*)
@ARGV
;;
}

0 comments on commit d7c4068

Please sign in to comment.