def create_internal_property( self, name: str, expr: Optional[AbstractExpression], type: Optional[CompiledType]) -> Optional[PropertyDef]: """ Create an internal property for this env spec. If ``expr`` is None, do not create a property and return None. Otherwise, unsugar it. :param name: Lower-case name to use to create this property name. Since the property is internal, the name is decorated. """ assert self.ast_node is not None if expr is None: return None expr = unsugar(expr) p = PropertyDef(expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('{}_{}'.format( name, next(self.PROPERTY_COUNT))), public=False, type=type, ignore_warn_on_node=True) p._indexing_name = '_{}'.format(p.original_name.lower) p._original_name = names.Name.from_lower(p._indexing_name) p.location = getattr(expr, 'location') or self.location self.ast_node.add_field(p) return p
def create_internal_properties(self, env_spec: EnvSpec) -> None: self.transitive_parent_prop = env_spec.create_internal_property( 'Env_Trans_Parent', unsugar(self.transitive_parent), T.Bool ) self.names_prop = env_spec.create_internal_property( 'Env_Names', self.names, T.Symbol.array )
def create_internal_property(self, name, expr, type): """ Create an internal property for this env spec. If ``expr`` is None, do not create a property and return None. Otherwise, unsugar it. :param str name: Lower-case name to use to create this property name. Since the property is internal, the name is decorated. """ if expr is None: return None expr = unsugar(expr) p = PropertyDef(expr, AbstractNodeData.PREFIX_INTERNAL, name=names.Name('_{}_{}'.format( name, next(self.PROPERTY_COUNT))), public=False, type=type, ignore_warn_on_node=True) p.location = getattr(expr, 'location') or self.location self.ast_node.add_field(p) return p
def create_internal_properties(self, env_spec: EnvSpec) -> None: self.transitive_parent_prop = env_spec.create_internal_property( 'Env_Trans_Parent', unsugar(self.transitive_parent), T.Bool)
def emit_expr(expr, **ctx): from langkit.expressions import ( Literal, Let, FieldAccess, AbstractVariable, SelfVariable, EntityVariable, LogicTrue, LogicFalse, unsugar, Map, All, Any, GetSymbol, Match, Eq, BinaryBooleanOperator, Then, OrderingTest, Quantifier, If, IsNull, Cast, DynamicVariable, IsA, Not, SymbolLiteral, No, Cond, New, CollectionSingleton, Concat, EnumLiteral, EnvGet, ArrayLiteral, Arithmetic, PropertyError, CharacterLiteral, Predicate, StructUpdate, BigIntLiteral, RefCategories, Bind, Try, Block, Contains, PropertyDef ) def is_a(*names): return any(expr.__class__.__name__ == n for n in names) then_underscore_var = ctx.get('then_underscore_var') overload_coll_name = ctx.get('overload_coll_name') walker = ctx.get('walker') def emit_lambda(expr, vars): vars_str = ", ".join(var_name(var) for var in vars) return "({}) => {}".format(vars_str, ee(expr)) del vars_str def emit_method_call(receiver, name, args=[], force_parens=True, as_multiline=False): return "{}.{}{}".format( receiver, name, emit_paren(", ".join(args), as_multiline) if force_parens or args else "" ) def emit_let(expr): if len(expr.vars) == 0: with walker.returned_expr(): return walker.emit_comments() + ee(expr.expr) vars_defs = "" for i, (var, abs_expr) in enumerate(zip(expr.vars, expr.var_exprs)): with walker.var_assignment(i): vars_defs += "{}val {} = {};$hl".format( walker.emit_comments(), var_name(var), ee(abs_expr) ) vars_defs += walker.emit_comments() with walker.returned_expr(): return "{{$i$hl{}$hl{}{}$hl$d}}".format( vars_defs, walker.emit_comments(), ee(expr.expr) ) def ee(expr, **extra_ctx): full_ctx = dict(ctx, **extra_ctx) return emit_expr(expr, **full_ctx) def ee_pexpr(expr, **extra_ctx): # We don't want to carry an arg_expr data left in the context from a # previous call, it needs to be specified in each call to ee_pexpr. ctx.pop('arg_expr', None) full_ctx = dict(ctx, **extra_ctx) return emit_paren_expr(expr, **full_ctx) expr = unsugar(expr) if isinstance(expr, Literal): return str(expr.literal).lower() elif isinstance(expr, SymbolLiteral): return json.dumps(expr.name) elif isinstance(expr, PropertyError): return "raise PropertyError({})".format( repr(expr.message) if expr.message else "" ) elif isinstance(expr, IsA): return "{} is {}".format( ee_pexpr(expr.expr), "{}".format(" | ".join(type_name(t) for t in expr.astnodes)) ) elif isinstance(expr, LogicTrue): return "%true" elif isinstance(expr, LogicFalse): return "%false" elif is_a("bind"): bind = "bind {} = {};$hl".format( ee(expr.expr_0), ee(expr.expr_1) ) return "{{$i$hl{}$hl{}$hl$d}}".format( bind, ee(expr.expr_2) ) elif isinstance(expr, Let): if isinstance(expr, Block): return emit_let(expr) else: with walker.call('Let'): with walker.arg(0): return walker.emit_comments() + emit_let(expr) elif isinstance(expr, Map): op_name = expr.kind args = [] vars = [expr.element_var] if expr.requires_index: vars.append(expr.index_var) if op_name in ["map", "mapcat"]: args.append(emit_lambda(expr.expr, vars)) elif op_name == "filter": args.append(emit_lambda(expr.filter_expr, vars)) elif op_name == "filter_map": args.append(emit_lambda(expr.expr, vars)) args.append(emit_lambda(expr.filter_expr, vars)) op_name = "filtermap" elif op_name == "take_while": args.append(emit_lambda(expr.take_while_expr, vars)) if overload_coll_name: op_name = overload_coll_name del ctx['overload_coll_name'] coll = ee(expr.collection) return emit_method_call(coll, op_name, args) elif isinstance(expr, Quantifier): return emit_method_call( ee(expr.collection), expr.kind, [emit_lambda(expr.expr, [expr.element_var])] ) elif isinstance(expr, Contains): return emit_method_call(ee(expr.collection), "contains", [ee(expr.item)]) elif isinstance(expr, If): with walker.call('If'): with walker.arg(0): coms = walker.emit_comments() cond_strn = ee_pexpr(expr.cond) res = "{}if {} then {} else {}".format( coms, cond_strn, ee_pexpr(expr._then, arg_expr=1), ee_pexpr(expr.else_then, arg_expr=2) ) return res elif isinstance(expr, Cond): with walker.call('Cond'): branches = expr.branches res = "" for i, b in enumerate(branches): # condition with walker.arg(i * 2): coms = walker.emit_comments() cond_strn = ee_pexpr(b[0]) expr_strn = ee_pexpr(b[1], arg_expr=i * 2 + 1) res += "{}{} {} then {}$hl".format( coms, "if" if i == 0 else "elif", cond_strn, expr_strn ) with walker.arg(len(branches) * 2): coms = walker.emit_comments() else_strn = ee_pexpr(expr.else_expr) res += "{}else {}".format(coms, else_strn) return res elif isinstance(expr, IsNull): return "{}.is_null".format(ee(expr.expr)) elif isinstance(expr, Cast): return "{}.as[{}]{}".format( ee(expr.expr), type_name(expr.dest_type), "!" if expr.do_raise else "", ) elif isinstance(expr, All): return ee(expr.equation_array, overload_coll_name="logic_all") elif isinstance(expr, Any): return ee(expr.equation_array, overload_coll_name="logic_any") elif isinstance(expr, Match): with walker.method_call("match"): res = "" with walker.self_arg(): coms = walker.emit_comments() matched_expr_strn = ee(expr.matched_expr) res += "{}match {} {{$i".format(coms, matched_expr_strn) for i, (typ, var, e) in enumerate(expr.matchers): with walker.arg(i): coms = walker.emit_comments() if coms and i > 0: coms = "$hl" + coms res += "$hl{}case {}{} => {}".format( coms, var_name(var), (" " + sf(": ${type_name(typ)}")) if typ else "", ee(e) ) res += "$d$hl}" return res elif isinstance(expr, Eq): return "{} = {}".format(ee(expr.lhs), ee(expr.rhs)) elif isinstance(expr, BinaryBooleanOperator): with walker.boolean_binop(expr.kind): def emit_bool_op_rec(expr, depth): if depth == 2: lhs = emit_paren_expr(expr.lhs, arg_expr=0, **ctx) else: lhs = emit_bool_op_rec(expr.lhs, depth - 1) return "{} {} {}".format( lhs, expr.kind, emit_paren_expr(expr.rhs, arg_expr=depth - 1, **ctx) ) return emit_bool_op_rec(expr, walker.arg_count()) elif isinstance(expr, Not): return "not {}".format(emit_paren_expr(expr.expr, **ctx)) elif isinstance(expr, Then): if expr.var_expr.source_name is None: assert expr.underscore_then # Match is like a function call in the Python DSL, but is a regular # expression in the new syntax, so we don't want to use the ? # syntax on it. if not isinstance(expr.then_expr, Match): return "{}?{}".format( ee(expr.expr), ee(expr.then_expr, then_underscore_var=expr.var_expr) ) return emit_method_call( ee_pexpr(expr.expr), "do", keep([ "({}) => {}".format(var_name(expr.var_expr), ee(expr.then_expr)), "default_val={}".format(ee_pexpr(expr.default_val)) if expr.default_val else None ]) ) elif isinstance(expr, OrderingTest): return "{} {} {}".format( ee_pexpr(expr.lhs), expr.OPERATOR_IMAGE[expr.operator], ee_pexpr(expr.rhs) ) elif isinstance(expr, GetSymbol): return "{}.symbol".format(ee(expr.node_expr)) elif is_a("as_entity", "as_bare_entity", "children", "env_parent", "rebindings_parent", "parents", "parent", "root", "env_node", "rebindings_new_env", "rebindings_old_env"): # Field like expressions exprs = expr.sub_expressions return emit_method_call(ee(exprs[0]), type(expr).__name__, map(ee, exprs[1:]), False) elif is_a("append_rebinding", "concat_rebindings", "env_node", "get_value", "solve", "is_referenced_from", "env_group", "length", "can_reach", "as_int", "unique", "env_orphan", "is_visible_from", "as_array", "rebind_env"): # Method like expressions exprs = expr.sub_expressions return emit_method_call(ee(exprs[0]), type(expr).__name__, map(ee, exprs[1:])) elif isinstance(expr, EnumLiteral): return expr.value.dsl_name elif isinstance(expr, Try): return "try $sl$i{}$sl$d {}".format( ee_pexpr(expr.try_expr), "or {}".format(ee_pexpr(expr.else_expr)) if expr.else_expr is not None else "" ) elif isinstance(expr, Arithmetic): return "{} {} {}".format(ee_pexpr(expr.l), expr.op, ee_pexpr(expr.r)) elif isinstance(expr, EnvGet): args = [ee(expr.symbol)] if expr.sequential_from: args.append("from={}".format(ee(expr.sequential_from))) if expr.categories: args.append('categories={}'.format(ee(expr.categories))) return emit_method_call( ee(expr.env), "get_first" if expr.only_first else "get", args ) elif is_a("at"): # Recognize find if (isinstance(expr.expr_0, Map) and expr.expr_0.kind == 'filter' and ee(expr.expr_1) == "0"): return ee(expr.expr_0, overload_coll_name="find") return "{}?({})".format(ee(expr.expr_0), ee(expr.expr_1)) elif is_a("at_or_raise"): return "{}({})".format(ee(expr.expr_0), ee(expr.expr_1)) elif isinstance(expr, FieldAccess): args = [] has_any_commented_arg = False is_property = isinstance(expr.constructed_expr.node_data, PropertyDef) if expr.arguments: with walker.method_call(expr.field): field_coms = walker.emit_comments() receiver_str = ee(expr.receiver) for i in range(walker.arg_count()): kw = walker.arg_keyword(i) with walker.arg(i): arg_coms = walker.emit_comments() if kw is None: args.append(arg_coms + ee(expr.arguments.args[i])) else: args.append(arg_coms + "{}={}".format( kw, ee(expr.arguments.kwargs[kw]) )) has_any_commented_arg |= arg_coms != "" else: field_coms = "" receiver_str = ee(expr.receiver) return field_coms + emit_method_call( receiver_str, expr.field, args, as_multiline=has_any_commented_arg, force_parens=is_property ) elif isinstance(expr, Concat): return "{} & {}".format(ee_pexpr(expr.array_1), ee_pexpr(expr.array_2)) elif isinstance(expr, EntityVariable): return "self" elif isinstance(expr, SelfVariable): return "node" elif isinstance(expr, DynamicVariable): return expr.argument_name.lower elif isinstance(expr, AbstractVariable): if then_underscore_var: if id(then_underscore_var) == id(expr): return "" return var_name(expr) elif isinstance(expr, No): return "null".format(type_name(expr.expr_type)) # TODO: Emit valid null values for other types, eg. [] for arrays. elif isinstance(expr, CollectionSingleton): if then_underscore_var: return emit_method_call(ee(expr.expr), "singleton") else: return "[{}]".format(ee(expr.expr)) elif isinstance(expr, New): return "{}{}".format( type_name(expr.struct_type), emit_paren(", ".join( "{}={}".format(unparsed_name(k), ee(v)) for k, v in expr.field_values.items() )) ) elif isinstance(expr, StructUpdate): return '{}.update({})'.format( ee(expr.expr), ', '.join('{}={}'.format(name, ee(field_expr)) for name, field_expr in sorted(expr.assocs.items())) ) elif isinstance(expr, ArrayLiteral): if not len(expr.elements): return '[]' elif isinstance(expr.elements[0], CharacterLiteral): return repr(u"".join(e.literal for e in expr.elements))[1:] return "[{}]".format(", ".join(ee(el) for el in expr.elements)) elif isinstance(expr, CharacterLiteral): # Get rid of the 'u' unicode prefix return repr(expr.literal)[1:] elif isinstance(expr, BigIntLiteral): return 'BigInt({})'.format(str(expr.expr) if isinstance(expr.expr, (int, long)) else ee(expr.expr)) elif isinstance(expr, RefCategories): return 'RefCats({})'.format(', '.join( '{}={}'.format(name, ee(value)) for name, value in list(sorted(expr.cat_map.items())) + [("others", expr.default)] )) elif isinstance(expr, Predicate): return "%predicate({})".format(", ".join(keep([ fqn(expr.pred_property), ee(expr.exprs[0]), ] + [ee(e) for e in expr.exprs[1:]]))) elif is_a("domain"): return "%domain({}, {})".format(ee(expr.expr_0), ee(expr.expr_1)) elif isinstance(expr, Bind): return "%eq({})".format(", ".join(keep([ ee(expr.from_expr), ee(expr.to_expr), "eq_prop={}".format(fqn(expr.eq_prop)) if expr.eq_prop else "" "conv_prop={}".format(fqn(expr.conv_prop)) if expr.conv_prop else "" ]))) else: # raise NotImplementedError(type(expr)) return repr(expr)