diff --git a/builtin/pure_ysh.py b/builtin/pure_ysh.py index 4e2bcccf7b..a4e2f4b51e 100644 --- a/builtin/pure_ysh.py +++ b/builtin/pure_ysh.py @@ -4,7 +4,7 @@ from __future__ import print_function from _devbuild.gen.runtime_asdl import cmd_value -from _devbuild.gen.syntax_asdl import command_t, loc, loc_t +from _devbuild.gen.syntax_asdl import loc from _devbuild.gen.value_asdl import value, value_e, value_t from core import error from core import state @@ -14,7 +14,7 @@ from mycpp import mylib from mycpp.mylib import tagswitch, NewDict -from typing import TYPE_CHECKING, cast, Any, Dict, List +from typing import TYPE_CHECKING, cast, Dict, List if TYPE_CHECKING: from display import ui @@ -63,106 +63,6 @@ def Run(self, cmd_val): return 0 -class ctx_Context(object): - """For ctx push (context) { ... }""" - - def __init__(self, mem, context): - # type: (state.Mem, Dict[str, value_t]) -> None - self.mem = mem - self.mem.PushContextStack(context) - - def __enter__(self): - # type: () -> None - pass - - def __exit__(self, type, value, traceback): - # type: (Any, Any, Any) -> None - self.mem.PopContextStack() - - -class Ctx(vm._Builtin): - - def __init__(self, mem, cmd_ev): - # type: (state.Mem, CommandEvaluator) -> None - self.mem = mem - self.cmd_ev = cmd_ev # To run blocks - - def _GetContext(self): - # type: () -> Dict[str, value_t] - ctx = self.mem.GetContext() - if ctx is None: - raise error.Expr( - "Could not find a context. Did you forget to 'ctx push'?", - loc.Missing) - return ctx - - def _Push(self, context, block): - # type: (Dict[str, value_t], command_t) -> int - with ctx_Context(self.mem, context): - return self.cmd_ev.EvalCommand(block) - - def _Set(self, updates): - # type: (Dict[str, value_t]) -> int - ctx = self._GetContext() - ctx.update(updates) - return 0 - - def _Emit(self, field, item, blame): - # type: (str, value_t, loc_t) -> int - ctx = self._GetContext() - - if field not in ctx: - ctx[field] = value.List([]) - - UP_arr = ctx[field] - if UP_arr.tag() != value_e.List: - raise error.TypeErr( - UP_arr, - "Expected the context item '%s' to be a List" % (field), blame) - - arr = cast(value.List, UP_arr) - arr.items.append(item) - - return 0 - - def Run(self, cmd_val): - # type: (cmd_value.Argv) -> int - rd = typed_args.ReaderForProc(cmd_val) - _, arg_r = flag_util.ParseCmdVal('ctx', - cmd_val, - accept_typed_args=True) - - verb, verb_loc = arg_r.ReadRequired2( - 'Expected a verb (push, set, emit)') - - if verb == "push": - context = rd.PosDict() - block = rd.RequiredBlock() - rd.Done() - arg_r.AtEnd() - - return self._Push(context, block) - - elif verb == "set": - updates = rd.RestNamed() - rd.Done() - arg_r.AtEnd() - - return self._Set(updates) - - elif verb == "emit": - field, field_loc = arg_r.ReadRequired2( - "A target field is required") - item = rd.PosValue() - rd.Done() - arg_r.AtEnd() - - return self._Emit(field, item, field_loc) - - else: - raise error.Usage("Unknown verb '%s'" % verb, verb_loc) - - class PushRegisters(vm._Builtin): def __init__(self, mem, cmd_ev): diff --git a/core/shell.py b/core/shell.py index d6cf69f66e..7dabd1f1cb 100644 --- a/core/shell.py +++ b/core/shell.py @@ -614,7 +614,6 @@ def Main( b[builtin_i.trap] = trap_osh.Trap(trap_state, parse_ctx, tracer, errfmt) b[builtin_i.shvar] = pure_ysh.Shvar(mem, search_path, cmd_ev) - b[builtin_i.ctx] = pure_ysh.Ctx(mem, cmd_ev) b[builtin_i.push_registers] = pure_ysh.PushRegisters(mem, cmd_ev) # Hay diff --git a/frontend/builtin_def.py b/frontend/builtin_def.py index 8ee6e38551..2db17eea3a 100644 --- a/frontend/builtin_def.py +++ b/frontend/builtin_def.py @@ -60,7 +60,6 @@ 'fork', 'forkwait', 'redir', 'fopen', # fopen is for backward compat 'shvar', - 'ctx', 'invoke', 'runproc', diff --git a/spec/ysh-builtin-ctx.test.sh b/spec/ysh-builtin-ctx.test.sh index 6d26065293..b18b74f227 100644 --- a/spec/ysh-builtin-ctx.test.sh +++ b/spec/ysh-builtin-ctx.test.sh @@ -2,6 +2,8 @@ ## oils_failures_allowed: 0 #### ctx push and set +use $LIB_YSH/ctx.ysh + var mydict = {} ctx push (mydict) { ctx set (key1="value1") @@ -16,6 +18,8 @@ json write (mydict) ## END #### ctx emit +use $LIB_YSH/ctx.ysh + var p = {} ctx push (p) { ctx emit flag ({short_name: '-v'}) @@ -58,6 +62,8 @@ json write (p) ## END #### nested ctx +use $LIB_YSH/ctx.ysh + var a = {} var b = {} ctx push (a) { @@ -78,6 +84,8 @@ json write (b) ## END #### error in context +use $LIB_YSH/ctx.ysh + var a = {} try { ctx push (a) { @@ -91,6 +99,8 @@ status=100 ## END #### no context, set +use $LIB_YSH/ctx.ysh + ctx set (bad=true) echo status=$_status ## status: 3 @@ -98,6 +108,8 @@ echo status=$_status ## END #### no context, emit +use $LIB_YSH/ctx.ysh + ctx emit bad (true) echo status=$_status ## status: 3 @@ -105,6 +117,8 @@ echo status=$_status ## END #### mini-parseArgs +use $LIB_YSH/ctx.ysh + proc parser (; place ; ; block_def) { var p = {} ctx push (p; ; block_def) @@ -159,6 +173,7 @@ json write (spec) ## END #### ctx with value.Place, not List/Dict (error location bug fix) +use $LIB_YSH/ctx.ysh ctx push (&p) { true diff --git a/stdlib/ysh/args.ysh b/stdlib/ysh/args.ysh index 9143d15fff..5375fdb242 100644 --- a/stdlib/ysh/args.ysh +++ b/stdlib/ysh/args.ysh @@ -26,6 +26,8 @@ # # echo "Verbose $[args.verbose]" +source $LIB_YSH/ctx.ysh + # TODO: See list # - It would be nice to keep `flag` and `arg` private, injecting them into the # proc namespace only within `Args` diff --git a/stdlib/ysh/ctx.ysh b/stdlib/ysh/ctx.ysh new file mode 100644 index 0000000000..51ba49625c --- /dev/null +++ b/stdlib/ysh/ctx.ysh @@ -0,0 +1,32 @@ +var contextStack = [] + +proc push (; context;; block) { + if (type(context) !== 'Dict') { + error "Expected context to be a Dict" (code=3) + } + + call contextStack->append(context) + try { + use ///ysh/ctx.ysh + call io->eval(block, vars={ctx}) + } + call contextStack->pop() + return $[_error.code] +} + +proc set (;; ...named) { + var context = contextStack[-1] + for k, v in (named) { + setvar context[k] = v + } +} + +proc emit (field; item) { + var context = contextStack[-1] + if (field not in context) { + setvar context[field] = [] + } + call context[field]->append(item) +} + +var __provide__ = :| push set emit |