Skip to content

Commit

Permalink
[oil-language] Implement @rest in procs and ..spread in funcs.
Browse files Browse the repository at this point in the history
Need to change the names.
  • Loading branch information
Andy Chu committed Oct 1, 2019
1 parent ad8aa58 commit d604949
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 19 deletions.
2 changes: 1 addition & 1 deletion frontend/syntax.asdl
Expand Up @@ -321,7 +321,7 @@ module syntax
-- 'open' is for proc p { }; closed is for proc p [] { }
proc_sig =
Open
| Closed(param* params, token? block_arg)
| Closed(param* params, token? rest, token? block)

param = (token name, type_expr? type, expr? default)

Expand Down
13 changes: 7 additions & 6 deletions oil_lang/expr_to_ast.py
Expand Up @@ -684,8 +684,8 @@ def _Argument(self, p_node, do_named, arglist):
return

if n == 2:
# Note: We allow multiple splats, just like Julia. They are concatenated
# as in lists and dicts.
# Note: We allow multiple spreads, just like Julia. They are
# concatenated as in lists and dicts.
if children[0].tok.id == Id.Expr_Ellipsis:
spread_expr = self.Expr(children[1])
if do_named:
Expand Down Expand Up @@ -776,6 +776,7 @@ def _ProcParams(self, p_node):
n = len(children)

params = [] # type: List[param]
rest = None
block = None

i = 0
Expand All @@ -788,15 +789,15 @@ def _ProcParams(self, p_node):
else:
if p.tok.id == Id.Expr_At: # @args
i += 1
splat = children[i].tok
rest = children[i].tok
elif p.tok.id == Id.Arith_Amp: # &block
i += 1
block = children[i].tok
else:
raise AssertionError(p.tok)
i += 2

return proc_sig.Closed(params, block)
return proc_sig.Closed(params, rest, block)

def _FuncParam(self, pnode):
# type: (PNode) -> param
Expand Down Expand Up @@ -836,7 +837,7 @@ def _FuncParams(self, p_node):
if ISNONTERMINAL(p.typ):
params.append(self._FuncParam(p))
elif p.tok.id == Id.Expr_Ellipsis:
splat = children[i].tok
splat = children[i+1].tok
i += 1

return params, splat
Expand Down Expand Up @@ -881,7 +882,7 @@ def Func(self, pnode, out):
if ISNONTERMINAL(typ2):
assert typ2 == grammar_nt.func_params, children[pos] # f(x, y)
# every other one is a comma
out.pos_params , out.pos_splat = self._FuncParams(children[2])
out.pos_params, out.pos_splat = self._FuncParams(children[2])
pos += 1

id_ = children[pos].tok.id
Expand Down
20 changes: 20 additions & 0 deletions osh/cmd_exec.py
Expand Up @@ -1750,6 +1750,19 @@ def _RunOilProc(self, node, argv):
scope_e.LocalOnly)
except IndexError:
e_die("No value provided for param %s", name.val)

n_params = len(sig.params)
if sig.rest:
leftover = value.MaybeStrArray(argv[n_params:])
self.mem.SetVar(
lvalue.Named(sig.rest.val), leftover, (), scope_e.LocalOnly)
else:
n_args = len(argv)
if n_params != n_args:
raise TypeError(
"proc %r expected %d arguments, but got %d" %
(node.name.val, n_params, n_args))

else: # proc is-open { }
# if sig.tag == proc_sig_e.Open:
#raise NotImplementedError('open')
Expand Down Expand Up @@ -1808,6 +1821,13 @@ def RunOilFunc(self, func, args, kwargs):
raise TypeError('Missing positional argument %r', param.name)
self.mem.SetVar(lvalue.Named(param.name.val), val, (), scope_e.LocalOnly)

if func.node.pos_splat:
splat_name = func.node.pos_splat.val

# NOTE: This is a heterogeneous TUPLE, not list.
leftover = value.Obj(args[n_params:])
self.mem.SetVar(lvalue.Named(splat_name), leftover, (), scope_e.LocalOnly)

# Bind named arguments
for i, param in enumerate(func.node.named_params):
name = param.name
Expand Down
46 changes: 35 additions & 11 deletions spec/oil-func-proc.test.sh
Expand Up @@ -38,6 +38,17 @@ echo $add(3,4,5)
12
## END

#### Pass too many positional params (without spread)
shopt -s oil:basic
func add(x, y) {
return x + y
}
var f = add(1,2,3)
echo $f
## STDOUT:
a
## END

#### Passing named arg
func f(; x=42) {
echo $x
Expand All @@ -52,12 +63,22 @@ pass f(x=99)
#### Func with named param with no default
shopt -s oil:basic
func add(x Int, y Int ; verbose Bool) {
#if (verbose) {
# echo 'verbose'
#}
if (verbose) {
echo 'verbose'
}
return x + y
}
echo $add(3, 2)
var a = add(3, 2, verbose=true)
echo $a

# crashes
var a = add(3, 2)
echo "shouldn't get here"

# doens't work eyt
#echo $add(3, 2, verbose=true)

## status: 1
## STDOUT:
verbose
5
Expand All @@ -71,9 +92,10 @@ func printf(fmt, ...args) {
pp args
}
pass printf('foo', 'a', 42, null)

## STDOUT:
(str) 'foo'
(list) ['a', 42, null]
(tuple) ('a', 42, None)
## END

#### return expression then return builtin
Expand Down Expand Up @@ -106,7 +128,7 @@ echo status=$?
status=42
## END

#### closed proc with no args
#### closed proc with no args, passed too many
proc f [] {
return 42
}
Expand All @@ -117,6 +139,7 @@ echo status=$?
f a b
echo status=$?

## status: 1
## STDOUT:
status=42
## END
Expand Down Expand Up @@ -145,19 +168,20 @@ status=42
# func(**opt) # Assumes keyword args match?
# parse :grep_opts :opt @ARGV

shopt -s oil:basic

proc f [@names] {
echo names= $names
return 42
echo names: @names
}
# this gets called with 3 args then?
f a b c
echo status=$?
## STDOUT:
names=
names:
a
b
c
status=42
status=0
## END

#### proc with block arg
Expand All @@ -166,7 +190,7 @@ status=42
proc f [x, y, &block] {
echo hi
}
f a b c
f a b
## STDOUT:
hi
## END
Expand Down
2 changes: 1 addition & 1 deletion test/spec.sh
Expand Up @@ -844,7 +844,7 @@ oil-regex() {
}

oil-func-proc() {
sh-spec spec/oil-func-proc.test.sh --cd-tmp --osh-failures-allowed 4 \
sh-spec spec/oil-func-proc.test.sh --cd-tmp --osh-failures-allowed 1 \
$OSH_LIST "$@"
}

Expand Down

0 comments on commit d604949

Please sign in to comment.