From be730e7cf113ad56a72b2634103a2e65ecd7ea5d Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Wed, 2 Oct 2024 08:43:52 -0600 Subject: [PATCH 1/5] Replace ctx with a pure YSH definition --- builtin/pure_ysh.py | 100 ----------------------------------- core/shell.py | 1 - spec/ysh-builtin-ctx.test.sh | 16 ++++++ stdlib/ysh/ctx.ysh | 84 +++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 101 deletions(-) create mode 100644 stdlib/ysh/ctx.ysh diff --git a/builtin/pure_ysh.py b/builtin/pure_ysh.py index 4e2bcccf7b..8cb90f49fc 100644 --- a/builtin/pure_ysh.py +++ b/builtin/pure_ysh.py @@ -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 5b51fe3fb2..951076bbf7 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/spec/ysh-builtin-ctx.test.sh b/spec/ysh-builtin-ctx.test.sh index 6d26065293..99df3a0b75 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 +source $LIB_YSH/ctx.ysh + var mydict = {} ctx push (mydict) { ctx set (key1="value1") @@ -16,6 +18,8 @@ json write (mydict) ## END #### ctx emit +source $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 +source $LIB_YSH/ctx.ysh + var a = {} var b = {} ctx push (a) { @@ -78,6 +84,8 @@ json write (b) ## END #### error in context +source $LIB_YSH/ctx.ysh + var a = {} try { ctx push (a) { @@ -91,6 +99,8 @@ status=100 ## END #### no context, set +source $LIB_YSH/ctx.ysh + ctx set (bad=true) echo status=$_status ## status: 3 @@ -98,6 +108,8 @@ echo status=$_status ## END #### no context, emit +source $LIB_YSH/ctx.ysh + ctx emit bad (true) echo status=$_status ## status: 3 @@ -105,6 +117,8 @@ echo status=$_status ## END #### mini-parseArgs +source $LIB_YSH/ctx.ysh + proc parser (; place ; ; block_def) { var p = {} ctx push (p; ; block_def) @@ -159,6 +173,8 @@ json write (spec) ## END #### ctx with value.Place, not List/Dict (error location bug fix) +source $LIB_YSH/ctx.ysh + ctx push (&p) { true diff --git a/stdlib/ysh/ctx.ysh b/stdlib/ysh/ctx.ysh new file mode 100644 index 0000000000..5110ef88c4 --- /dev/null +++ b/stdlib/ysh/ctx.ysh @@ -0,0 +1,84 @@ +proc ctx_invoke (verb, ...pos; self, ...args; ...named; block=null) { + case (verb) { + push { + if (len(pos) !== 0) { + error "Unexpected word arguments" (code=2) + } + if (len(args) !== 1) { + error "Expected a single Dict argument" (code=2) + } + if (len(keys(named)) !== 0) { + error "Unexpected named arguments" (code=2) + } + if (block === null) { + error "Expected a block argument" (code=2) + } + + var context = args[0] + if (type(context) !== 'Dict') { + error "Expected context to be a Dict" (code=3) + } + + call self.contextStack->append(context) + try { call io->eval(block) } + call self.contextStack->pop() + return $[_error.code] + } + + set { + if (len(pos) !== 0) { + error "Unexpected word arguments" (code=2) + } + if (len(args) !== 0) { + error "Unexpected positional arguments" (code=2) + } + if (block !== null) { + error "Unexpected block argument" (code=2) + } + if (len(self.contextStack) === 0) { + error "Not in a context. Did you forget to ctx push?" (code=3) + } + + # Cannot use var here because "context" was already declared in push + setvar context = self.contextStack[-1] + for k, v in (named) { + setvar context[k] = v + } + } + + emit { + if (len(pos) !== 1) { + error "Expected a field to emit into" (code=2) + } + if (len(args) !== 1) { + error "Expected an item to push" (code=2) + } + if (len(keys(named)) !== 0) { + error "Unexpected named arguments" (code=2) + } + if (block !== null) { + error "Unexpected block argument" (code=2) + } + if (len(self.contextStack) === 0) { + error "Not in a context. Did you forget to ctx push?" (code=3) + } + + var field = pos[0] + var item = args[0] + setvar context = self.contextStack[-1] + + if (field not in context) { + setvar context[field] = [] + } + + call context[field]->append(item) + } + + (else) { + error "Expected a verb (push, set, emit)" (code=2) + } + } +} + +var ctx_methods = Object(null, {__invoke__: ctx_invoke}) +var ctx = Object(ctx_methods, {contextStack: []}) From 5a1f6c076f31c673f56facdeb0711acf035bc37c Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Wed, 2 Oct 2024 08:55:22 -0600 Subject: [PATCH 2/5] Source ctx.ysh in args.ysh --- stdlib/ysh/args.ysh | 2 ++ 1 file changed, 2 insertions(+) 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` From 10fb084f74ce008565efd5eab44593c39536e0fb Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Wed, 2 Oct 2024 08:57:02 -0600 Subject: [PATCH 3/5] Fix lint errors --- builtin/pure_ysh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/pure_ysh.py b/builtin/pure_ysh.py index 8cb90f49fc..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 From e235d7d9fc9c680939c0e07ae8889b8c12b4f59b Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Mon, 7 Oct 2024 20:06:55 -0600 Subject: [PATCH 4/5] Remove ctx from builtins list --- frontend/builtin_def.py | 1 - 1 file changed, 1 deletion(-) 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', From 83b468cfe0a38a40033f895029a69f7a12081c0b Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Mon, 7 Oct 2024 20:07:07 -0600 Subject: [PATCH 5/5] Try using modules for the pure ysh ctx builtin --- spec/ysh-builtin-ctx.test.sh | 17 +++--- stdlib/ysh/ctx.ysh | 102 +++++++++-------------------------- 2 files changed, 33 insertions(+), 86 deletions(-) diff --git a/spec/ysh-builtin-ctx.test.sh b/spec/ysh-builtin-ctx.test.sh index 99df3a0b75..b18b74f227 100644 --- a/spec/ysh-builtin-ctx.test.sh +++ b/spec/ysh-builtin-ctx.test.sh @@ -2,7 +2,7 @@ ## oils_failures_allowed: 0 #### ctx push and set -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh var mydict = {} ctx push (mydict) { @@ -18,7 +18,7 @@ json write (mydict) ## END #### ctx emit -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh var p = {} ctx push (p) { @@ -62,7 +62,7 @@ json write (p) ## END #### nested ctx -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh var a = {} var b = {} @@ -84,7 +84,7 @@ json write (b) ## END #### error in context -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh var a = {} try { @@ -99,7 +99,7 @@ status=100 ## END #### no context, set -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh ctx set (bad=true) echo status=$_status @@ -108,7 +108,7 @@ echo status=$_status ## END #### no context, emit -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh ctx emit bad (true) echo status=$_status @@ -117,7 +117,7 @@ echo status=$_status ## END #### mini-parseArgs -source $LIB_YSH/ctx.ysh +use $LIB_YSH/ctx.ysh proc parser (; place ; ; block_def) { var p = {} @@ -173,8 +173,7 @@ json write (spec) ## END #### ctx with value.Place, not List/Dict (error location bug fix) -source $LIB_YSH/ctx.ysh - +use $LIB_YSH/ctx.ysh ctx push (&p) { true diff --git a/stdlib/ysh/ctx.ysh b/stdlib/ysh/ctx.ysh index 5110ef88c4..51ba49625c 100644 --- a/stdlib/ysh/ctx.ysh +++ b/stdlib/ysh/ctx.ysh @@ -1,84 +1,32 @@ -proc ctx_invoke (verb, ...pos; self, ...args; ...named; block=null) { - case (verb) { - push { - if (len(pos) !== 0) { - error "Unexpected word arguments" (code=2) - } - if (len(args) !== 1) { - error "Expected a single Dict argument" (code=2) - } - if (len(keys(named)) !== 0) { - error "Unexpected named arguments" (code=2) - } - if (block === null) { - error "Expected a block argument" (code=2) - } +var contextStack = [] - var context = args[0] - if (type(context) !== 'Dict') { - error "Expected context to be a Dict" (code=3) - } - - call self.contextStack->append(context) - try { call io->eval(block) } - call self.contextStack->pop() - return $[_error.code] - } - - set { - if (len(pos) !== 0) { - error "Unexpected word arguments" (code=2) - } - if (len(args) !== 0) { - error "Unexpected positional arguments" (code=2) - } - if (block !== null) { - error "Unexpected block argument" (code=2) - } - if (len(self.contextStack) === 0) { - error "Not in a context. Did you forget to ctx push?" (code=3) - } - - # Cannot use var here because "context" was already declared in push - setvar context = self.contextStack[-1] - for k, v in (named) { - setvar context[k] = v - } - } - - emit { - if (len(pos) !== 1) { - error "Expected a field to emit into" (code=2) - } - if (len(args) !== 1) { - error "Expected an item to push" (code=2) - } - if (len(keys(named)) !== 0) { - error "Unexpected named arguments" (code=2) - } - if (block !== null) { - error "Unexpected block argument" (code=2) - } - if (len(self.contextStack) === 0) { - error "Not in a context. Did you forget to ctx push?" (code=3) - } - - var field = pos[0] - var item = args[0] - setvar context = self.contextStack[-1] +proc push (; context;; block) { + if (type(context) !== 'Dict') { + error "Expected context to be a Dict" (code=3) + } - if (field not in context) { - setvar context[field] = [] - } + call contextStack->append(context) + try { + use ///ysh/ctx.ysh + call io->eval(block, vars={ctx}) + } + call contextStack->pop() + return $[_error.code] +} - call context[field]->append(item) - } +proc set (;; ...named) { + var context = contextStack[-1] + for k, v in (named) { + setvar context[k] = v + } +} - (else) { - error "Expected a verb (push, set, emit)" (code=2) - } +proc emit (field; item) { + var context = contextStack[-1] + if (field not in context) { + setvar context[field] = [] } + call context[field]->append(item) } -var ctx_methods = Object(null, {__invoke__: ctx_invoke}) -var ctx = Object(ctx_methods, {contextStack: []}) +var __provide__ = :| push set emit |