def visit_Subscript(self, node): from ast import Expr, Call, Name, Load, Lambda, arguments, arg m = match("E[ _expr | _x in _set]", node) if m: x_s = m["_x"].id # name of dummy vaqriable expr = m["_expr"] sset = m["_set"] res = Call( func=Name(id="Ex", ctx=Load()), args=[ Lambda( args=arguments( args=[arg(arg=x_s, annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=expr, ), sset, ], keywords=[], starargs=None, kwargs=None, ) return res m = match("Sum[ _expr | _x in _set]", node) if m: x_s = m["_x"].id # name of dummy vaqriable expr = m["_expr"] sset = m["_set"] res = Call( func=Name(id="Sum", ctx=Load()), args=[ Lambda( args=arguments( args=[arg(arg=x_s, annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=expr, ), sset, ], keywords=[], starargs=None, kwargs=None, ) return res return node
def _make_coverage_dummy_expr(macronode): """Force expression `macronode` to be reported as covered by coverage tools. This facilitates "deleting" expression nodes by `return None` from a macro. Since an expression slot in the AST cannot be empty, we inject a dummy node that evaluates to `None`. `macronode` is the macro invocation node to copy source location info from. """ # TODO: inject the macro name for human-readability # We inject a lambda and an immediate call to it, because a constant `None`, # if it appears alone in an `ast.Expr`, is optimized away by CPython. # We must set location info manually, because we run after `expand`. non = copy_location(Constant(value=None), macronode) lam = copy_location( Lambda(args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=non), macronode) call = copy_location(Call(func=lam, args=[], keywords=[]), macronode) return Done(call)
def visit_BoolOp(self, node: BoolOp) -> Union[UnaryOp, Call]: self.generic_visit(node) if isinstance(node.op, And): runtime = '__lazybooland__' elif isinstance(node.op, Or): runtime = '__lazyboolor__' else: return node lhs, rhs = node.values delegate = Call( func=Name(id=runtime, ctx=Load()), args=[ lhs, # Make the rhs a deferred computation by wrapping with a lambda Lambda(args=arguments(args=[], kwonlyargs=[], kw_defaults=[], defaults=[]), body=rhs) ], keywords=[]) copy_location(delegate, node) fix_missing_locations(delegate) return delegate
def rewrite_with_to_binds(body, monad): new_body = [] # Construct a transformer for this specific monad's mreturn rdb = RewriteDoBody(monad) # This is the body of the lambda we're about to construct last_part = body[-1].value # Rewrite mreturn rdb.visit(last_part) # Iterate in reverse, making each line the into a lambda whose body is the # rest of the lines (which are each lambdas), and whose names are the # bind assignments. for b in reversed(body[:-1]): rdb.visit(b) if isinstance(b, Assign): name = b.targets[0].id value = b.value else: # If there was no assignment to the bind, just use a random name, eek name = '__DO_NOT_NAME_A_VARIABLE_THIS_STRING__' value = b.value # last part = value.bind(lambda name: last_part) last_part = Call(func=Attribute(value=value, attr='bind', ctx=Load()), args=[ Lambda(args=arguments(args=[ Name(id=name, ctx=Param()), ], vararg=None, kwarg=None, defaults=[]), body=last_part), ], keywords=[], starargs=None, kwargs=None) return last_part
def visit_ClassDef(self, node): """Process property defaults for Scenic classes.""" if node.name in self.constructors: # Scenic class definition newBody = [] for child in node.body: child = self.visit(child) if isinstance(child, AnnAssign): # default value for property origValue = child.annotation target = child.target # extract any attributes for this property metaAttrs = [] if isinstance(target, Subscript): sl = target.slice if not isinstance(sl, Index): self.parseError(sl, 'malformed attributes for property default') sl = sl.value if isinstance(sl, Name): metaAttrs.append(sl.id) elif isinstance(sl, Tuple): for elt in sl.elts: if not isinstance(elt, Name): self.parseError(elt, 'malformed attributes for property default') metaAttrs.append(elt.id) else: self.parseError(sl, 'malformed attributes for property default') newTarget = Name(target.value.id, Store()) copy_location(newTarget, target) target = newTarget # find dependencies of the default value properties = AttributeFinder.find('self', origValue) # create default value object args = [ Set([Str(prop) for prop in properties]), Set([Str(attr) for attr in metaAttrs]), Lambda(self.self_args, origValue) ] value = Call(Name(createDefault, Load()), args, []) copy_location(value, origValue) newChild = AnnAssign( target=target, annotation=value, value=None, simple=True) child = copy_location(newChild, child) newBody.append(child) node.body = newBody return node else: # ordinary Python class # it's impossible at the moment to define a Python class in a Scenic file, # but we'll leave this check here for future-proofing for base in node.bases: name = None if isinstance(base, Call): name = base.func.id elif isinstance(base, Name): name = base.id if name is not None and name in self.constructors: self.parseError(node, f'Python class {node.name} derives from PRS class {name}') return self.generic_visit(node)
def visit_ClassDef(self, node): """Process property defaults for Scenic classes.""" if node.name in self.constructors: # constructor definition newBody = [] for child in node.body: child = self.visit(child) if isinstance(child, AnnAssign): # default value for property origValue = child.annotation target = child.target # extract any attributes for this property metaAttrs = [] if isinstance(target, Subscript): sl = target.slice if not isinstance(sl, Index): self.parseError(sl, 'malformed attributes for property default') sl = sl.value if isinstance(sl, Name): metaAttrs.append(sl.id) elif isinstance(sl, Tuple): for elt in sl.elts: if not isinstance(elt, Name): self.parseError(elt, 'malformed attributes for property default') metaAttrs.append(elt.id) else: self.parseError(sl, 'malformed attributes for property default') newTarget = Name(target.value.id, Store()) copy_location(newTarget, target) target = newTarget # find dependencies of the default value properties = AttributeFinder.find('self', origValue) # create default value object args = [ Set([Str(prop) for prop in properties]), Set([Str(attr) for attr in metaAttrs]), Lambda(selfArg, origValue) ] value = Call(Name(createDefault, Load()), args, []) copy_location(value, origValue) newChild = AnnAssign( target=target, annotation=value, value=None, simple=True) child = copy_location(newChild, child) newBody.append(child) node.body = newBody return node else: # ordinary Python class # catch some mistakes where 'class' was used instead of 'constructor' for base in node.bases: name = None if isinstance(base, Call): name = base.func.id elif isinstance(base, Name): name = base.id if name is not None and name in self.constructors: self.parseError(node, f'must use "{constructorStatement}" to subclass objects') return self.generic_visit(node)
def visit_Subscript(self, node): from ast import Expr, Call, Name, Load, Lambda, arguments, arg m = match('E[ _expr | _x in _set]', node) if m: x_s = m['_x'].id # name of dummy vaqriable expr = m['_expr'] sset = m['_set'] res = Call(func=Name(id='Ex', ctx=Load()), args=[ Lambda(args=arguments( args=[arg(arg=x_s, annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=expr), sset ], keywords=[], starargs=None, kwargs=None) return res m = match('Sum[ _expr | _x in _set]', node) if m: x_s = m['_x'].id # name of dummy vaqriable expr = m['_expr'] sset = m['_set'] res = Call(func=Name(id='Sum', ctx=Load()), args=[ Lambda(args=arguments( args=[arg(arg=x_s, annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=expr), sset ], keywords=[], starargs=None, kwargs=None) return res return node
def visit_Call(self, node): """Wrap require statements with lambdas and unpack any argument packages.""" func = node.func if isinstance( func, Name) and func.id == requireStatement: # Require statement # Soft reqs have 2 arguments, including the probability, which is given as the # first argument by the token translator; so we allow an extra argument here and # validate it later on (in case the user wrongly gives 2 arguments to require). if not (1 <= len(node.args) <= 2): raise self.parseError(node, 'require takes exactly one argument') if len(node.keywords) != 0: raise self.parseError(node, 'require takes no keyword arguments') cond = node.args[-1] if isinstance(cond, Starred): raise self.parseError( node, 'argument unpacking cannot be used with require') req = self.visit(cond) line = self.lineMap[node.lineno] reqID = Num(len(self.requirements)) # save ID number self.requirements.append( req) # save condition for later inspection when pruning closure = Lambda(noArgs, req) # enclose requirement in a lambda lineNum = Num(line) # save line number for error messages copy_location(closure, req) copy_location(lineNum, req) newArgs = [reqID, closure, lineNum] if len(node.args) == 2: # get probability for soft requirements prob = node.args[0] if not isinstance(prob, Num): raise self.parseError( node, 'malformed requirement ' '(should be a single expression)') newArgs.append(prob) return copy_location(Call(func, newArgs, []), node) else: # Ordinary function call newArgs = [] # Translate arguments, unpacking any argument packages for arg in node.args: if isinstance(arg, BinOp) and isinstance(arg.op, packageNode): newArgs.extend(self.unpack(arg, 2, node)) else: newArgs.append(self.visit(arg)) newKeywords = [self.visit(kwarg) for kwarg in node.keywords] return copy_location(Call(func, newArgs, newKeywords), node)
def _visit_lam(self: 'ASTTagger', node: ast.Lambda): args = node.args new = self.symtable.enter_new() arguments = args.args + args.kwonlyargs if args.vararg: arguments.append(args.vararg) if args.kwarg: arguments.append(args.kwarg) for arg in arguments: # lambda might be able to annotated in the future? annotation = arg.annotation if annotation: self.visit(annotation) new.entered.add(arg.arg) new_tagger = ASTTagger(new) node.body = new_tagger.visit(node.body) return Tag(node, new)
def make_lambda(expr): """ Take an expression and return a thunk node evaluating to that expression """ return copy_loc( expr, Lambda( args=arguments( posonlyargs=[], args=[], varargs=[], kwonlyargs=[], kw_defaults=[], defaults=[], ), body=expr, ), )
def inner(e): return Lambda(args=arguments(args=[arg(a) for a in args]), body=e)
def create_let_lamb(call, body): return Lambda( args=arguments(args=[arg(arg=a.id) for a in call.args], ), body=body, )