Skip to content

Commit

Permalink
[refactor] Move assignment builtins to their own file.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Chu committed Jul 15, 2019
1 parent 8f07fbe commit 1402d03
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 177 deletions.
9 changes: 5 additions & 4 deletions bin/oil.py
Expand Up @@ -73,6 +73,7 @@ def _tlog(msg):
from oil_lang import expr_eval

from osh import builtin
from osh import builtin_assign
from osh import builtin_bracket
from osh import builtin_comp
from osh import builtin_printf
Expand Down Expand Up @@ -417,7 +418,7 @@ def ShellMain(lang, argv0, argv, login_shell):

dir_stack = state.DirStack()

declare_typeset = builtin.DeclareTypeset(mem, funcs)
declare_typeset = builtin_assign.DeclareTypeset(mem, funcs)

builtins = { # Lookup
builtin_e.ECHO: builtin.Echo,
Expand All @@ -442,12 +443,12 @@ def ShellMain(lang, argv0, argv, login_shell):

builtin_e.SET: builtin.Set(exec_opts, mem),
builtin_e.SHOPT: builtin.Shopt(exec_opts),
builtin_e.UNSET: builtin.Unset(mem, funcs, errfmt),

builtin_e.SHIFT: builtin.Shift(mem),
builtin_e.EXPORT: builtin.Export(mem),
builtin_e.EXPORT: builtin_assign.Export(mem),
builtin_e.DECLARE: declare_typeset,
builtin_e.TYPESET: declare_typeset,
builtin_e.UNSET: builtin_assign.Unset(mem, funcs, errfmt),
builtin_e.SHIFT: builtin_assign.Shift(mem),

builtin_e.ALIAS: builtin.Alias(aliases, errfmt),
builtin_e.UNALIAS: builtin.UnAlias(aliases, errfmt),
Expand Down
3 changes: 2 additions & 1 deletion core/test_lib.py
Expand Up @@ -26,6 +26,7 @@
from frontend import parse_lib
from frontend import reader
from osh import builtin
from osh import builtin_assign
from osh import builtin_comp
from osh import cmd_exec
from osh import expr_eval
Expand Down Expand Up @@ -139,7 +140,7 @@ def InitExecutor(parse_ctx=None, comp_lookup=None, arena=None, mem=None,
readline = None # simulate not having it
builtins = { # Lookup
builtin_e.ECHO: builtin.Echo,
builtin_e.SHIFT: builtin.Shift(mem),
builtin_e.SHIFT: builtin_assign.Shift(mem),

builtin_e.HISTORY: builtin.History(readline),

Expand Down
172 changes: 1 addition & 171 deletions osh/builtin.py
Expand Up @@ -31,7 +31,7 @@

from _devbuild.gen import osh_help # generated file
from _devbuild.gen.runtime_asdl import (
lvalue, value, value_e, scope_e, span_e, var_flags_e, builtin_e, arg_vector
value_e, scope_e, span_e, builtin_e, arg_vector
)
from asdl import pretty
from core import ui
Expand Down Expand Up @@ -572,26 +572,6 @@ def __call__(self, arg_vec):
return status


class Shift(object):
def __init__(self, mem):
self.mem = mem

def __call__(self, arg_vec):
num_args = len(arg_vec.strs) - 1
if num_args == 0:
n = 1
elif num_args == 1:
arg = arg_vec.strs[1]
try:
n = int(arg)
except ValueError:
raise args.UsageError("Invalid shift argument %r" % arg)
else:
raise args.UsageError('got too many arguments')

return self.mem.Shift(n)


CD_SPEC = _Register('cd')
CD_SPEC.ShortFlag('-L')
CD_SPEC.ShortFlag('-P')
Expand Down Expand Up @@ -800,46 +780,6 @@ def __call__(self, arg_vec):
return 0


EXPORT_SPEC = _Register('export')
EXPORT_SPEC.ShortFlag('-n')


class Export(object):
def __init__(self, mem):
self.mem = mem

def __call__(self, arg_vec):
arg, arg_index = EXPORT_SPEC.ParseVec(arg_vec)
positional = arg_vec.strs[arg_index:]
if arg.n:
for name in positional:
if not match.IsValidVarName(name):
# TODO: span_id=
raise args.UsageError('export: Invalid variable name %r' % name)

# NOTE: bash doesn't care if it wasn't found.
self.mem.ClearFlag(name, var_flags_e.Exported, scope_e.Dynamic)
else:
for arg in positional:
parts = arg.split('=', 1)
if len(parts) == 1:
name = parts[0]
val = None # Creates an empty variable
else:
name, s = parts
val = value.Str(s)

if not match.IsValidVarName(name):
# TODO: span_id=
raise args.UsageError('export: Invalid variable name %r' % name)

#log('%s %s', name, val)
self.mem.SetVar(
lvalue.Named(name), val, (var_flags_e.Exported,), scope_e.Dynamic)

return 0


def AddOptionsToArgSpec(spec):
"""Shared between 'set' builtin and the shell's own arg parser."""
for short_flag, opt_name in state.SET_OPTIONS:
Expand Down Expand Up @@ -985,62 +925,6 @@ def __call__(self, arg_vec):
return 0


UNSET_SPEC = _Register('unset')
UNSET_SPEC.ShortFlag('-v')
UNSET_SPEC.ShortFlag('-f')


# TODO:
# - Parse lvalue expression: unset 'a[ i - 1 ]'. Static or dynamic parsing?
# - It would make more sense to treat no args as an error (bash doesn't.)
# - Should we have strict builtins? Or just make it stricter?

class Unset(object):
def __init__(self, mem, funcs, errfmt):
self.mem = mem
self.funcs = funcs
self.errfmt = errfmt

def _UnsetVar(self, name, spid):
if not match.IsValidVarName(name):
raise args.UsageError(
'got invalid variable name %r' % name, span_id=spid)

ok, found = self.mem.Unset(lvalue.Named(name), scope_e.Dynamic)
if not ok:
self.errfmt.Print("Can't unset readonly variable %r", name,
span_id=spid)
return ok, found

def __call__(self, arg_vec):
arg, offset = UNSET_SPEC.ParseVec(arg_vec)
n = len(arg_vec.strs)

for i in xrange(offset, n):
name = arg_vec.strs[i]
spid = arg_vec.spids[i]

if arg.f:
if name in self.funcs:
del self.funcs[name]
elif arg.v:
ok, _ = self._UnsetVar(name, spid)
if not ok:
return 1
else:
# Try to delete var first, then func.
ok, found = self._UnsetVar(name, spid)
if not ok:
return 1

#log('%s: %s', name, found)
if not found:
if name in self.funcs:
del self.funcs[name]

return 0


def _ResolveNames(names, funcs, aliases, search_path):
results = []
for name in names:
Expand Down Expand Up @@ -1187,60 +1071,6 @@ def __call__(self, arg_vec):
return status


DECLARE_SPEC = _Register('declare')
DECLARE_SPEC.ShortFlag('-f')
DECLARE_SPEC.ShortFlag('-F')
DECLARE_SPEC.ShortFlag('-p')


class DeclareTypeset(object):
def __init__(self, mem, funcs):
self.mem = mem
self.funcs = funcs

def __call__(self, arg_vec):
arg, arg_index = DECLARE_SPEC.ParseVec(arg_vec)
names = arg_vec.strs[arg_index:]

status = 0

# NOTE: in bash, -f shows the function body, while -F shows the name. In
# osh, they're identical and behave like -F.

if arg.f or arg.F: # Lookup and print functions.
if names:
for name in names:
if name in self.funcs:
print(name)
# TODO: Could print LST, or render LST. Bash does this. 'trap' too.
#print(funcs[name])
else:
status = 1
elif arg.F:
for func_name in sorted(self.funcs):
print('declare -f %s' % (func_name))
else:
raise args.UsageError('declare/typeset -f without args')

elif arg.p: # Lookup and print variables.
if names:
for name in names:
val = self.mem.GetVar(name)
if val.tag != value_e.Undef:
# TODO: Print flags.

print(name)
else:
status = 1
else:
raise args.UsageError('declare/typeset -p without args')

else:
raise args.UsageError("doesn't understand %s" % arg_vec.strs[1:])

return status


ALIAS_SPEC = _Register('alias')


Expand Down

0 comments on commit 1402d03

Please sign in to comment.