| 1 | #!/usr/bin/env python2
 | 
| 2 | """
 | 
| 3 | User-defined funcs and procs
 | 
| 4 | """
 | 
| 5 | from __future__ import print_function
 | 
| 6 | 
 | 
| 7 | from _devbuild.gen.id_kind_asdl import Id
 | 
| 8 | from _devbuild.gen.runtime_asdl import cmd_value
 | 
| 9 | from _devbuild.gen.syntax_asdl import (proc_sig, proc_sig_e, Param, ParamGroup,
 | 
| 10 |                                        NamedArg, Func, loc, ArgList, expr,
 | 
| 11 |                                        expr_e, expr_t)
 | 
| 12 | from _devbuild.gen.value_asdl import (value, value_e, value_t, ProcDefaults,
 | 
| 13 |                                       LeftName)
 | 
| 14 | 
 | 
| 15 | from core import error
 | 
| 16 | from core.error import e_die
 | 
| 17 | from core import state
 | 
| 18 | from core import vm
 | 
| 19 | from frontend import lexer
 | 
| 20 | from frontend import typed_args
 | 
| 21 | from mycpp import mylib
 | 
| 22 | from mycpp.mylib import log, NewDict
 | 
| 23 | 
 | 
| 24 | from typing import List, Tuple, Dict, Optional, cast, TYPE_CHECKING
 | 
| 25 | if TYPE_CHECKING:
 | 
| 26 |     from _devbuild.gen.syntax_asdl import command, loc_t
 | 
| 27 |     from osh import cmd_eval
 | 
| 28 |     from ysh import expr_eval
 | 
| 29 | 
 | 
| 30 | _ = log
 | 
| 31 | 
 | 
| 32 | # TODO:
 | 
| 33 | # - use _EvalExpr more?
 | 
| 34 | #   - a single with state.ctx_YshExpr -- I guess that's faster
 | 
| 35 | #   - although EvalExpr() can take param.blame_tok
 | 
| 36 | 
 | 
| 37 | 
 | 
| 38 | def _DisallowMutableDefault(val, blame_loc):
 | 
| 39 |     # type: (value_t, loc_t) -> None
 | 
| 40 |     if val.tag() in (value_e.List, value_e.Dict):
 | 
| 41 |         raise error.TypeErr(val, "Default values can't be mutable", blame_loc)
 | 
| 42 | 
 | 
| 43 | 
 | 
| 44 | def _EvalPosDefaults(expr_ev, pos_params):
 | 
| 45 |     # type: (expr_eval.ExprEvaluator, List[Param]) -> List[value_t]
 | 
| 46 |     """Shared between func and proc: Eval defaults for positional params"""
 | 
| 47 | 
 | 
| 48 |     no_val = None  # type: value_t
 | 
| 49 |     pos_defaults = [no_val] * len(pos_params)
 | 
| 50 |     for i, p in enumerate(pos_params):
 | 
| 51 |         if p.default_val:
 | 
| 52 |             val = expr_ev.EvalExpr(p.default_val, p.blame_tok)
 | 
| 53 |             _DisallowMutableDefault(val, p.blame_tok)
 | 
| 54 |             pos_defaults[i] = val
 | 
| 55 |     return pos_defaults
 | 
| 56 | 
 | 
| 57 | 
 | 
| 58 | def _EvalNamedDefaults(expr_ev, named_params):
 | 
| 59 |     # type: (expr_eval.ExprEvaluator, List[Param]) -> Dict[str, value_t]
 | 
| 60 |     """Shared between func and proc: Eval defaults for named params"""
 | 
| 61 | 
 | 
| 62 |     named_defaults = NewDict()  # type: Dict[str, value_t]
 | 
| 63 |     for i, p in enumerate(named_params):
 | 
| 64 |         if p.default_val:
 | 
| 65 |             val = expr_ev.EvalExpr(p.default_val, p.blame_tok)
 | 
| 66 |             _DisallowMutableDefault(val, p.blame_tok)
 | 
| 67 |             named_defaults[p.name] = val
 | 
| 68 |     return named_defaults
 | 
| 69 | 
 | 
| 70 | 
 | 
| 71 | def EvalFuncDefaults(
 | 
| 72 |         expr_ev,  # type: expr_eval.ExprEvaluator
 | 
| 73 |         func,  # type: Func
 | 
| 74 | ):
 | 
| 75 |     # type: (...) -> Tuple[List[value_t], Dict[str, value_t]]
 | 
| 76 |     """Evaluate default args for funcs, at time of DEFINITION, not call."""
 | 
| 77 | 
 | 
| 78 |     if func.positional:
 | 
| 79 |         pos_defaults = _EvalPosDefaults(expr_ev, func.positional.params)
 | 
| 80 |     else:
 | 
| 81 |         pos_defaults = None
 | 
| 82 | 
 | 
| 83 |     if func.named:
 | 
| 84 |         named_defaults = _EvalNamedDefaults(expr_ev, func.named.params)
 | 
| 85 |     else:
 | 
| 86 |         named_defaults = None
 | 
| 87 | 
 | 
| 88 |     return pos_defaults, named_defaults
 | 
| 89 | 
 | 
| 90 | 
 | 
| 91 | def EvalProcDefaults(expr_ev, sig):
 | 
| 92 |     # type: (expr_eval.ExprEvaluator, proc_sig.Closed) -> ProcDefaults
 | 
| 93 |     """Evaluate default args for procs, at time of DEFINITION, not call."""
 | 
| 94 | 
 | 
| 95 |     no_val = None  # type: value_t
 | 
| 96 | 
 | 
| 97 |     if sig.word:
 | 
| 98 |         word_defaults = [no_val] * len(sig.word.params)
 | 
| 99 |         for i, p in enumerate(sig.word.params):
 | 
| 100 |             if p.default_val:
 | 
| 101 |                 val = expr_ev.EvalExpr(p.default_val, p.blame_tok)
 | 
| 102 |                 if val.tag() != value_e.Str:
 | 
| 103 |                     raise error.TypeErr(
 | 
| 104 |                         val, 'Default val for word param must be Str',
 | 
| 105 |                         p.blame_tok)
 | 
| 106 | 
 | 
| 107 |                 word_defaults[i] = val
 | 
| 108 |     else:
 | 
| 109 |         word_defaults = None
 | 
| 110 | 
 | 
| 111 |     if sig.positional:
 | 
| 112 |         pos_defaults = _EvalPosDefaults(expr_ev, sig.positional.params)
 | 
| 113 |     else:
 | 
| 114 |         pos_defaults = None  # in case there's a block param
 | 
| 115 | 
 | 
| 116 |     if sig.named:
 | 
| 117 |         named_defaults = _EvalNamedDefaults(expr_ev, sig.named.params)
 | 
| 118 |     else:
 | 
| 119 |         named_defaults = None
 | 
| 120 | 
 | 
| 121 |     # cd /tmp (; ; myblock)
 | 
| 122 |     if sig.block_param:
 | 
| 123 |         exp = sig.block_param.default_val
 | 
| 124 |         if exp:
 | 
| 125 |             block_default = expr_ev.EvalExpr(exp, sig.block_param.blame_tok)
 | 
| 126 |             # It can only be ^() or null
 | 
| 127 |             if block_default.tag() not in (value_e.Null, value_e.Command):
 | 
| 128 |                 raise error.TypeErr(
 | 
| 129 |                     block_default,
 | 
| 130 |                     "Default value for block should be Command or Null",
 | 
| 131 |                     sig.block_param.blame_tok)
 | 
| 132 |         else:
 | 
| 133 |             block_default = None  # no default, different than value.Null
 | 
| 134 |     else:
 | 
| 135 |         block_default = None
 | 
| 136 | 
 | 
| 137 |     return ProcDefaults(word_defaults, pos_defaults, named_defaults,
 | 
| 138 |                         block_default)
 | 
| 139 | 
 | 
| 140 | 
 | 
| 141 | def _EvalPosArgs(expr_ev, exprs, pos_args):
 | 
| 142 |     # type: (expr_eval.ExprEvaluator, List[expr_t], List[value_t]) -> None
 | 
| 143 |     """Shared between func and proc: evaluate positional args."""
 | 
| 144 | 
 | 
| 145 |     for e in exprs:
 | 
| 146 |         UP_e = e
 | 
| 147 |         if e.tag() == expr_e.Spread:
 | 
| 148 |             e = cast(expr.Spread, UP_e)
 | 
| 149 |             val = expr_ev._EvalExpr(e.child)
 | 
| 150 |             if val.tag() != value_e.List:
 | 
| 151 |                 raise error.TypeErr(val, 'Spread expected a List', e.left)
 | 
| 152 |             pos_args.extend(cast(value.List, val).items)
 | 
| 153 |         else:
 | 
| 154 |             pos_args.append(expr_ev._EvalExpr(e))
 | 
| 155 | 
 | 
| 156 | 
 | 
| 157 | def _EvalNamedArgs(expr_ev, named_exprs):
 | 
| 158 |     # type: (expr_eval.ExprEvaluator, List[NamedArg]) -> Dict[str, value_t]
 | 
| 159 |     """Shared between func and proc: evaluate named args."""
 | 
| 160 | 
 | 
| 161 |     named_args = NewDict()  # type: Dict[str, value_t]
 | 
| 162 |     for n in named_exprs:
 | 
| 163 |         val_expr = n.value
 | 
| 164 |         UP_val_expr = val_expr
 | 
| 165 |         if val_expr.tag() == expr_e.Spread:
 | 
| 166 |             val_expr = cast(expr.Spread, UP_val_expr)
 | 
| 167 |             val = expr_ev._EvalExpr(val_expr.child)
 | 
| 168 |             if val.tag() != value_e.Dict:
 | 
| 169 |                 raise error.TypeErr(val, 'Spread expected a Dict',
 | 
| 170 |                                     val_expr.left)
 | 
| 171 |             named_args.update(cast(value.Dict, val).d)
 | 
| 172 |         else:
 | 
| 173 |             val = expr_ev.EvalExpr(n.value, n.name)
 | 
| 174 |             name = lexer.TokenVal(n.name)
 | 
| 175 |             named_args[name] = val
 | 
| 176 | 
 | 
| 177 |     return named_args
 | 
| 178 | 
 | 
| 179 | 
 | 
| 180 | def _EvalArgList(
 | 
| 181 |         expr_ev,  # type: expr_eval.ExprEvaluator
 | 
| 182 |         args,  # type: ArgList
 | 
| 183 |         me=None  # type: Optional[value_t]
 | 
| 184 | ):
 | 
| 185 |     # type: (...) -> Tuple[List[value_t], Optional[Dict[str, value_t]]]
 | 
| 186 |     """Evaluate arg list for funcs.
 | 
| 187 | 
 | 
| 188 |     This is a PRIVATE METHOD on ExprEvaluator, but it's in THIS FILE, because I
 | 
| 189 |     want it to be next to EvalTypedArgsToProc, which is similar.
 | 
| 190 | 
 | 
| 191 |     It's not valid to call this without the EvalExpr() wrapper:
 | 
| 192 | 
 | 
| 193 |       with state.ctx_YshExpr(...)  # required to call this
 | 
| 194 |         ...
 | 
| 195 |     """
 | 
| 196 |     pos_args = []  # type: List[value_t]
 | 
| 197 | 
 | 
| 198 |     if me:  # self/this argument
 | 
| 199 |         pos_args.append(me)
 | 
| 200 | 
 | 
| 201 |     _EvalPosArgs(expr_ev, args.pos_args, pos_args)
 | 
| 202 | 
 | 
| 203 |     named_args = None  # type: Dict[str, value_t]
 | 
| 204 |     if args.named_args is not None:
 | 
| 205 |         named_args = _EvalNamedArgs(expr_ev, args.named_args)
 | 
| 206 | 
 | 
| 207 |     return pos_args, named_args
 | 
| 208 | 
 | 
| 209 | 
 | 
| 210 | def EvalTypedArgsToProc(
 | 
| 211 |         expr_ev,  # type: expr_eval.ExprEvaluator
 | 
| 212 |         mutable_opts,  # type: state.MutableOpts
 | 
| 213 |         node,  # type: command.Simple
 | 
| 214 |         cmd_val,  # type: cmd_value.Argv
 | 
| 215 | ):
 | 
| 216 |     # type: (...) -> None
 | 
| 217 |     """Evaluate word, typed, named, and block args for a proc."""
 | 
| 218 |     cmd_val.typed_args = node.typed_args
 | 
| 219 | 
 | 
| 220 |     # We only got here if the call looks like
 | 
| 221 |     #    p (x)
 | 
| 222 |     #    p { echo hi }
 | 
| 223 |     #    p () { echo hi }
 | 
| 224 |     # So allocate this unconditionally
 | 
| 225 |     cmd_val.pos_args = []
 | 
| 226 | 
 | 
| 227 |     ty = node.typed_args
 | 
| 228 |     if ty:
 | 
| 229 |         if ty.left.id == Id.Op_LBracket:  # assert [42 === x]
 | 
| 230 |             # Defer evaluation by wrapping in value.Expr
 | 
| 231 | 
 | 
| 232 |             for exp in ty.pos_args:
 | 
| 233 |                 cmd_val.pos_args.append(value.Expr(exp))
 | 
| 234 |             # TODO: ...spread is illegal
 | 
| 235 | 
 | 
| 236 |             n1 = ty.named_args
 | 
| 237 |             if n1 is not None:
 | 
| 238 |                 cmd_val.named_args = NewDict()
 | 
| 239 |                 for named_arg in n1:
 | 
| 240 |                     name = lexer.TokenVal(named_arg.name)
 | 
| 241 |                     cmd_val.named_args[name] = value.Expr(named_arg.value)
 | 
| 242 |                 # TODO: ...spread is illegal
 | 
| 243 | 
 | 
| 244 |         else:  # json write (x)
 | 
| 245 |             with state.ctx_YshExpr(mutable_opts):  # What EvalExpr() does
 | 
| 246 |                 _EvalPosArgs(expr_ev, ty.pos_args, cmd_val.pos_args)
 | 
| 247 | 
 | 
| 248 |                 if ty.named_args is not None:
 | 
| 249 |                     cmd_val.named_args = _EvalNamedArgs(expr_ev, ty.named_args)
 | 
| 250 | 
 | 
| 251 |         if ty.block_expr and node.block:
 | 
| 252 |             e_die("Can't accept both block expression and block literal",
 | 
| 253 |                   node.block.brace_group.left)
 | 
| 254 | 
 | 
| 255 |         # p ( ; ; block) is an expression to be evaluated
 | 
| 256 |         if ty.block_expr:
 | 
| 257 |             # fallback location is (
 | 
| 258 |             cmd_val.block_arg = expr_ev.EvalExpr(ty.block_expr, ty.left)
 | 
| 259 | 
 | 
| 260 |     # p { echo hi } is an unevaluated block
 | 
| 261 |     if node.block:
 | 
| 262 |         # TODO: conslidate value.Block (holds LiteralBlock) and value.Command
 | 
| 263 |         cmd_val.block_arg = value.Block(node.block)
 | 
| 264 | 
 | 
| 265 |         # Add location info so the cmd_val looks the same for both:
 | 
| 266 |         #   cd /tmp (; ; ^(echo hi))
 | 
| 267 |         #   cd /tmp { echo hi }
 | 
| 268 |         if not cmd_val.typed_args:
 | 
| 269 |             cmd_val.typed_args = ArgList.CreateNull()
 | 
| 270 | 
 | 
| 271 |             # Also add locations for error message: ls { echo invalid }
 | 
| 272 |             cmd_val.typed_args.left = node.block.brace_group.left
 | 
| 273 |             cmd_val.typed_args.right = node.block.brace_group.right
 | 
| 274 | 
 | 
| 275 | 
 | 
| 276 | def _BindWords(
 | 
| 277 |         proc_name,  # type: str
 | 
| 278 |         group,  # type: ParamGroup
 | 
| 279 |         defaults,  # type: List[value_t]
 | 
| 280 |         cmd_val,  # type: cmd_value.Argv
 | 
| 281 |         mem,  # type: state.Mem
 | 
| 282 |         blame_loc,  # type: loc_t
 | 
| 283 | ):
 | 
| 284 |     # type: (...) -> None
 | 
| 285 | 
 | 
| 286 |     argv = cmd_val.argv[1:]
 | 
| 287 |     num_args = len(argv)
 | 
| 288 |     for i, p in enumerate(group.params):
 | 
| 289 |         if i < num_args:
 | 
| 290 |             val = value.Str(argv[i])  # type: value_t
 | 
| 291 |         else:  # default args were evaluated on definition
 | 
| 292 |             val = defaults[i]
 | 
| 293 |             if val is None:
 | 
| 294 |                 raise error.Expr(
 | 
| 295 |                     "proc %r wasn't passed word param %r" %
 | 
| 296 |                     (proc_name, p.name), blame_loc)
 | 
| 297 | 
 | 
| 298 |         mem.SetLocalName(LeftName(p.name, p.blame_tok), val)
 | 
| 299 | 
 | 
| 300 |     # ...rest
 | 
| 301 | 
 | 
| 302 |     num_params = len(group.params)
 | 
| 303 |     rest = group.rest_of
 | 
| 304 |     if rest:
 | 
| 305 |         lval = LeftName(rest.name, rest.blame_tok)
 | 
| 306 | 
 | 
| 307 |         items = [value.Str(s)
 | 
| 308 |                  for s in argv[num_params:]]  # type: List[value_t]
 | 
| 309 |         rest_val = value.List(items)
 | 
| 310 |         mem.SetLocalName(lval, rest_val)
 | 
| 311 |     else:
 | 
| 312 |         if num_args > num_params:
 | 
| 313 |             if len(cmd_val.arg_locs):
 | 
| 314 |                 # point to the first extra one
 | 
| 315 |                 extra_loc = cmd_val.arg_locs[num_params + 1]  # type: loc_t
 | 
| 316 |             else:
 | 
| 317 |                 extra_loc = loc.Missing
 | 
| 318 | 
 | 
| 319 |             # Too many arguments.
 | 
| 320 |             raise error.Expr(
 | 
| 321 |                 "proc %r takes %d words, but got %d" %
 | 
| 322 |                 (proc_name, num_params, num_args), extra_loc)
 | 
| 323 | 
 | 
| 324 | 
 | 
| 325 | def _BindTyped(
 | 
| 326 |         code_name,  # type: str
 | 
| 327 |         group,  # type: Optional[ParamGroup]
 | 
| 328 |         defaults,  # type: List[value_t]
 | 
| 329 |         pos_args,  # type: Optional[List[value_t]]
 | 
| 330 |         mem,  # type: state.Mem
 | 
| 331 |         blame_loc,  # type: loc_t
 | 
| 332 | ):
 | 
| 333 |     # type: (...) -> None
 | 
| 334 | 
 | 
| 335 |     if pos_args is None:
 | 
| 336 |         pos_args = []
 | 
| 337 | 
 | 
| 338 |     num_args = len(pos_args)
 | 
| 339 |     num_params = 0
 | 
| 340 | 
 | 
| 341 |     i = 0
 | 
| 342 | 
 | 
| 343 |     if group:
 | 
| 344 |         for p in group.params:
 | 
| 345 |             if i < num_args:
 | 
| 346 |                 val = pos_args[i]
 | 
| 347 |             else:
 | 
| 348 |                 val = defaults[i]
 | 
| 349 |                 if val is None:
 | 
| 350 |                     raise error.Expr(
 | 
| 351 |                         "%r wasn't passed typed param %r" %
 | 
| 352 |                         (code_name, p.name), blame_loc)
 | 
| 353 | 
 | 
| 354 |             mem.SetLocalName(LeftName(p.name, p.blame_tok), val)
 | 
| 355 |             i += 1
 | 
| 356 |         num_params += len(group.params)
 | 
| 357 | 
 | 
| 358 |     # ...rest
 | 
| 359 | 
 | 
| 360 |     if group:
 | 
| 361 |         rest = group.rest_of
 | 
| 362 |         if rest:
 | 
| 363 |             lval = LeftName(rest.name, rest.blame_tok)
 | 
| 364 | 
 | 
| 365 |             rest_val = value.List(pos_args[num_params:])
 | 
| 366 |             mem.SetLocalName(lval, rest_val)
 | 
| 367 |         else:
 | 
| 368 |             if num_args > num_params:
 | 
| 369 |                 # Too many arguments.
 | 
| 370 |                 raise error.Expr(
 | 
| 371 |                     "%r takes %d typed args, but got %d" %
 | 
| 372 |                     (code_name, num_params, num_args), blame_loc)
 | 
| 373 | 
 | 
| 374 | 
 | 
| 375 | def _BindNamed(
 | 
| 376 |         code_name,  # type: str
 | 
| 377 |         group,  # type: ParamGroup
 | 
| 378 |         defaults,  # type: Dict[str, value_t]
 | 
| 379 |         named_args,  # type: Optional[Dict[str, value_t]]
 | 
| 380 |         mem,  # type: state.Mem
 | 
| 381 |         blame_loc,  # type: loc_t
 | 
| 382 | ):
 | 
| 383 |     # type: (...) -> None
 | 
| 384 | 
 | 
| 385 |     if named_args is None:
 | 
| 386 |         named_args = NewDict()
 | 
| 387 | 
 | 
| 388 |     for p in group.params:
 | 
| 389 |         val = named_args.get(p.name)
 | 
| 390 |         if val is None:
 | 
| 391 |             val = defaults.get(p.name)
 | 
| 392 |         if val is None:
 | 
| 393 |             raise error.Expr(
 | 
| 394 |                 "%r wasn't passed named param %r" % (code_name, p.name),
 | 
| 395 |                 blame_loc)
 | 
| 396 | 
 | 
| 397 |         mem.SetLocalName(LeftName(p.name, p.blame_tok), val)
 | 
| 398 |         # Remove bound args
 | 
| 399 |         mylib.dict_erase(named_args, p.name)
 | 
| 400 | 
 | 
| 401 |     # ...rest
 | 
| 402 |     rest = group.rest_of
 | 
| 403 |     if rest:
 | 
| 404 |         lval = LeftName(rest.name, rest.blame_tok)
 | 
| 405 |         mem.SetLocalName(lval, value.Dict(named_args))
 | 
| 406 |     else:
 | 
| 407 |         num_args = len(named_args)
 | 
| 408 |         num_params = len(group.params)
 | 
| 409 |         if num_args > num_params:
 | 
| 410 |             # Too many arguments.
 | 
| 411 |             raise error.Expr(
 | 
| 412 |                 "%r takes %d named args, but got %d" %
 | 
| 413 |                 (code_name, num_params, num_args), blame_loc)
 | 
| 414 | 
 | 
| 415 | 
 | 
| 416 | def _BindFuncArgs(func, rd, mem):
 | 
| 417 |     # type: (value.Func, typed_args.Reader, state.Mem) -> None
 | 
| 418 | 
 | 
| 419 |     node = func.parsed
 | 
| 420 |     blame_loc = rd.LeftParenToken()
 | 
| 421 | 
 | 
| 422 |     ### Handle positional args
 | 
| 423 | 
 | 
| 424 |     if node.positional:
 | 
| 425 |         _BindTyped(func.name, node.positional, func.pos_defaults, rd.pos_args,
 | 
| 426 |                    mem, blame_loc)
 | 
| 427 |     else:
 | 
| 428 |         if rd.pos_args is not None:
 | 
| 429 |             num_pos = len(rd.pos_args)
 | 
| 430 |             if num_pos != 0:
 | 
| 431 |                 raise error.Expr(
 | 
| 432 |                     "Func %r takes no positional args, but got %d" %
 | 
| 433 |                     (func.name, num_pos), blame_loc)
 | 
| 434 | 
 | 
| 435 |     semi = rd.arg_list.semi_tok
 | 
| 436 |     if semi is not None:
 | 
| 437 |         blame_loc = semi
 | 
| 438 | 
 | 
| 439 |     ### Handle named args
 | 
| 440 | 
 | 
| 441 |     if node.named:
 | 
| 442 |         _BindNamed(func.name, node.named, func.named_defaults, rd.named_args,
 | 
| 443 |                    mem, blame_loc)
 | 
| 444 |     else:
 | 
| 445 |         if rd.named_args is not None:
 | 
| 446 |             num_named = len(rd.named_args)
 | 
| 447 |             if num_named != 0:
 | 
| 448 |                 raise error.Expr(
 | 
| 449 |                     "Func %r takes no named args, but got %d" %
 | 
| 450 |                     (func.name, num_named), blame_loc)
 | 
| 451 | 
 | 
| 452 | 
 | 
| 453 | def BindProcArgs(proc, cmd_val, mem):
 | 
| 454 |     # type: (value.Proc, cmd_value.Argv, state.Mem) -> None
 | 
| 455 | 
 | 
| 456 |     UP_sig = proc.sig
 | 
| 457 |     if UP_sig.tag() != proc_sig_e.Closed:  # proc is-closed ()
 | 
| 458 |         return
 | 
| 459 | 
 | 
| 460 |     sig = cast(proc_sig.Closed, UP_sig)
 | 
| 461 | 
 | 
| 462 |     # Note: we don't call _BindX() when there is no corresponding param group.
 | 
| 463 |     # This saves a few allocations, because most procs won't have all 3 types
 | 
| 464 |     # of args.
 | 
| 465 | 
 | 
| 466 |     blame_loc = loc.Missing  # type: loc_t
 | 
| 467 | 
 | 
| 468 |     ### Handle word args
 | 
| 469 | 
 | 
| 470 |     if len(cmd_val.arg_locs) > 0:
 | 
| 471 |         blame_loc = cmd_val.arg_locs[0]
 | 
| 472 | 
 | 
| 473 |     if sig.word:
 | 
| 474 |         _BindWords(proc.name, sig.word, proc.defaults.for_word, cmd_val, mem,
 | 
| 475 |                    blame_loc)
 | 
| 476 |     else:
 | 
| 477 |         num_word = len(cmd_val.argv)
 | 
| 478 |         if num_word != 1:
 | 
| 479 |             raise error.Expr(
 | 
| 480 |                 "Proc %r takes no word args, but got %d" %
 | 
| 481 |                 (proc.name, num_word - 1), blame_loc)
 | 
| 482 | 
 | 
| 483 |     ### Handle typed positional args.  This includes a block arg, if any.
 | 
| 484 | 
 | 
| 485 |     if cmd_val.typed_args:  # blame ( of call site
 | 
| 486 |         blame_loc = cmd_val.typed_args.left
 | 
| 487 | 
 | 
| 488 |     if sig.positional:  # or sig.block_param:
 | 
| 489 |         _BindTyped(proc.name, sig.positional, proc.defaults.for_typed,
 | 
| 490 |                    cmd_val.pos_args, mem, blame_loc)
 | 
| 491 |     else:
 | 
| 492 |         if cmd_val.pos_args is not None:
 | 
| 493 |             num_pos = len(cmd_val.pos_args)
 | 
| 494 |             if num_pos != 0:
 | 
| 495 |                 raise error.Expr(
 | 
| 496 |                     "Proc %r takes no typed args, but got %d" %
 | 
| 497 |                     (proc.name, num_pos), blame_loc)
 | 
| 498 | 
 | 
| 499 |     ### Handle typed named args
 | 
| 500 | 
 | 
| 501 |     if cmd_val.typed_args:  # blame ; of call site if possible
 | 
| 502 |         semi = cmd_val.typed_args.semi_tok
 | 
| 503 |         if semi is not None:
 | 
| 504 |             blame_loc = semi
 | 
| 505 | 
 | 
| 506 |     if sig.named:
 | 
| 507 |         _BindNamed(proc.name, sig.named, proc.defaults.for_named,
 | 
| 508 |                    cmd_val.named_args, mem, blame_loc)
 | 
| 509 |     else:
 | 
| 510 |         if cmd_val.named_args is not None:
 | 
| 511 |             num_named = len(cmd_val.named_args)
 | 
| 512 |             if num_named != 0:
 | 
| 513 |                 raise error.Expr(
 | 
| 514 |                     "Proc %r takes no named args, but got %d" %
 | 
| 515 |                     (proc.name, num_named), blame_loc)
 | 
| 516 | 
 | 
| 517 |     # Maybe blame second ; of call site.  Because value_t doesn't generally
 | 
| 518 |     # have location info, as opposed to expr_t.
 | 
| 519 |     if cmd_val.typed_args:
 | 
| 520 |         semi = cmd_val.typed_args.semi_tok2
 | 
| 521 |         if semi is not None:
 | 
| 522 |             blame_loc = semi
 | 
| 523 | 
 | 
| 524 |     ### Handle block arg
 | 
| 525 | 
 | 
| 526 |     block_param = sig.block_param
 | 
| 527 |     block_arg = cmd_val.block_arg
 | 
| 528 | 
 | 
| 529 |     if block_param:
 | 
| 530 |         if block_arg is None:
 | 
| 531 |             block_arg = proc.defaults.for_block
 | 
| 532 |         if block_arg is None:
 | 
| 533 |             raise error.Expr(
 | 
| 534 |                 "%r wasn't passed block param %r" %
 | 
| 535 |                 (proc.name, block_param.name), blame_loc)
 | 
| 536 | 
 | 
| 537 |         mem.SetLocalName(LeftName(block_param.name, block_param.blame_tok),
 | 
| 538 |                          block_arg)
 | 
| 539 | 
 | 
| 540 |     else:
 | 
| 541 |         if block_arg is not None:
 | 
| 542 |             raise error.Expr(
 | 
| 543 |                 "Proc %r doesn't accept a block argument" % proc.name,
 | 
| 544 |                 blame_loc)
 | 
| 545 | 
 | 
| 546 | 
 | 
| 547 | def CallUserFunc(
 | 
| 548 |         func,  # type: value.Func
 | 
| 549 |         rd,  # type: typed_args.Reader
 | 
| 550 |         mem,  # type: state.Mem
 | 
| 551 |         cmd_ev,  # type: cmd_eval.CommandEvaluator
 | 
| 552 | ):
 | 
| 553 |     # type: (...) -> value_t
 | 
| 554 | 
 | 
| 555 |     # Push a new stack frame
 | 
| 556 |     with state.ctx_FuncCall(mem, func):
 | 
| 557 |         _BindFuncArgs(func, rd, mem)
 | 
| 558 | 
 | 
| 559 |         try:
 | 
| 560 |             cmd_ev._Execute(func.parsed.body)
 | 
| 561 | 
 | 
| 562 |             return value.Null  # implicit return
 | 
| 563 |         except vm.ValueControlFlow as e:
 | 
| 564 |             return e.value
 | 
| 565 |         except vm.IntControlFlow as e:
 | 
| 566 |             raise AssertionError('IntControlFlow in func')
 | 
| 567 | 
 | 
| 568 |     raise AssertionError('unreachable')
 | 
| 569 | 
 | 
| 570 | 
 | 
| 571 | # vim: sw=4
 |