public static LNode ContractsOnMethod(LNode fn, IMacroContext context) { LNode oldFn = fn; if (fn.ArgCount >= 4) { var rw = new CodeContractRewriter(fn.Args[0], fn.Args[1], context); fn = ProcessArgContractAttributes(fn, 2, rw); if (fn.Args[0].HasAttrs) { fn = fn.WithArgChanged(0, fn.Args[0].WithAttrs(rw.Process(fn.Args[0].Attrs, null))); } if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[3]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(3, body); return(fn); } } return(null); }
[LexicalMacro("notnull T method(notnull T arg) {...}; T method([requires(expr)] T arg) {...}; " + "[requires(expr)] T method(...) {...}; [ensures(expr)] T method(...) {...}; " + "[ensuresOnThrow(expr)] T method(...) {...}; [ensuresOnThrow<Exception>(expr)] T method(...) {...}", "Generates Contract checks in a method.\n\n" + "- [requires(expr)] and [assert(expr)] specify an expression that must be true at the beginning of the method; assert conditions are checked in debug builds only, while \"requires\" conditions are checked in all builds. The condition can include an underscore `_` that refers to the argument that the attribute is attached to, if any.\n" + "- [ensures(expr)] and [ensuresAssert(expr)] specify an expression that must be true if-and-when the method returns normally. assert conditions are checked in debug builds only. The condition can include an underscore `_` that refers to the return value of the method.\n" + "- [ensuresFinally(expr)] specifies an expression that must be true when the method exits, whether by exception or by a normal return. This is implemented by wrapping the method in a try-finally block.\n" + "- [ensuresOnThrow(expr)] and [ensuresOnThrow<ExceptionType>(expr)] specify a condition that must be true if the method throws an exception. When #haveContractRewriter is false, underscore `_` refers to the thrown exception object; this is not available in the MS Code Contracts Rewriter.\n" + "- notnull is equivalent to [requires(_ != null)] if applied to an argument, and [ensures(_ != null)] if applied to the method as a whole.\n" + "\nAll contract attributes (except notnull) can specify multiple expressions separated by commas, to produce multiple checks, each with its own error message.", "#fn", "#cons", Mode = MacroMode.Passive | MacroMode.PriorityInternalFallback)] public static LNode ContractsOnMethod(LNode fn, IMacroContext context) { LNode fnArgs, oldFn = fn; if (fn.ArgCount >= 4) { var rw = new CodeContractRewriter(fn.Args[0], fn.Args[1], context); fn = ProcessArgContractAttributes(fn, 2, rw); if (fn.Args[0].HasAttrs) { fn = fn.WithArgChanged(0, fn.Args[0].WithAttrs(rw.Process(fn.Args[0].Attrs, null))); } if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[3]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(3, body); return(fn); } } return(null); }
public static LNode ForwardProperty(LNode prop, IMessageSink sink) { LNode name, fwd, body; if (prop.ArgCount != 3) return null; LNode target = GetForwardingTarget(fwd = prop.Args[2], name = prop.Args[1]); if (target != null) { body = F.Braces(new RVList<LNode>( F.Call(S.get, F.Braces(F.Call(S.Return, target))), F.Call(S.set, F.Braces(F.Call(S.Assign, target, F.Id(S.value)))))); return prop.WithArgChanged(2, body); } else if ((body = fwd).Calls(S.Braces)) { var body2 = body.WithArgs(stmt => { if (stmt.Calls(S.get, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) return stmt.WithArgs(new RVList<LNode>(F.Braces(F.Call(S.Return, target)))); if (stmt.Calls(S.set, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) return stmt.WithArgs(new RVList<LNode>(F.Braces(F.Call(S.Assign, target, F.Id(S.value))))); return stmt; }); if (body2 != body) return prop.WithArgChanged(2, body2); } return null; }
public static LNode ForwardProperty(LNode prop, IMacroContext context) { LNode name, fwd, body; if (prop.ArgCount != 4) { return(null); } LNode target = GetForwardingTarget(fwd = prop.Args[3], name = prop.Args[1]); if (target != null) { body = F.Braces(F.Call(S.get, F.Braces(F.Call(S.Return, target)))); return(prop.WithArgChanged(3, body)); } else if ((body = fwd).Calls(S.Braces)) { var body2 = body.WithArgs(stmt => { if (stmt.Calls(S.get, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) { return(stmt.WithArgs(new VList <LNode>(F.Braces(F.Call(S.Return, target))))); } if (stmt.Calls(S.set, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) { return(stmt.WithArgs(new VList <LNode>(F.Braces(F.Call(S.Assign, target, F.Id(S.value)))))); } return(stmt); }); if (body2 != body) { return(prop.WithArgChanged(3, body2)); } } return(null); }
static LNode ProcessArgContractAttributes(LNode fn, int argsIndex, CodeContractRewriter rw, bool isLambda = false) { LNode fnArgs = fn.Args[argsIndex]; if (fnArgs.CallsMin(isLambda ? S.Tuple : S.AltList, 1)) { return(fn.WithArgChanged(argsIndex, fnArgs.WithArgs(arg => { if (arg.HasAttrs) { return arg.WithAttrs(rw.Process(arg.Attrs, GetVarName(arg))); } return arg; }))); } else if (isLambda) { var arg = fnArgs; // just one argument if (arg.HasAttrs) { fn = fn.WithArgChanged(argsIndex, arg.WithAttrs(rw.Process(arg.Attrs, GetVarName(arg)))); } } return(fn); }
public static LNode of(LNode node, IMessageSink sink) { LNode kind; if (node.ArgCount == 2 && (kind = node.Args[0]).IsId) { if (kind.IsIdNamed(_array)) { return(node.WithArgChanged(0, kind.WithName(S.Array))); } if (kind.IsIdNamed(_opt)) { return(node.WithArgChanged(0, kind.WithName(S.QuestionMark))); } if (kind.IsIdNamed(_ptr)) { return(node.WithArgChanged(0, kind.WithName(S._Pointer))); } } else if (node.ArgCount == 3 && (kind = node.Args[0]).IsIdNamed(_array) && node.Args[1].IsLiteral) { return(node.WithArgs(kind.WithName(S.GetArrayKeyword((int)node.Args[1].Value)), node.Args[2])); } return(null); }
[LexicalMacro("([notnull] (x => ...)); ([notnull] x) => ...; ([requires(expr)] x) => ...; " + "([ensures(expr)] (x => ...)); ([ensuresOnThrow(expr)] (x => ...)); ", "Generates Contract checks in a lambda function. See the documentation of " + "ContractsOnMethod for more information about the contract attributes.", "=>", Mode = MacroMode.Passive | MacroMode.PriorityInternalFallback)] public static LNode ContractsOnLambda(LNode fn, IMacroContext context) { LNode oldFn = fn; if (fn.ArgCount == 2) { var rw = new CodeContractRewriter(LNode.Missing, Id_lambda_function, context); fn = ProcessArgContractAttributes(fn, 0, rw, isLambda: true); if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[1]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(1, body); return(fn); } } return(null); }
protected virtual LNode MakeSuperExpr(LNode lhs, ref LNode primary, RVList <LNode> rhs) { if (primary == null) { return(lhs); // an error should have been printed already } if (lhs == primary) { if (primary.BaseStyle == NodeStyle.Operator) { primary = F.Call(primary, rhs); } else { primary = lhs.WithArgs(lhs.Args.AddRange(rhs)); } MarkSpecial(primary); return(primary); } else { Debug.Assert(lhs != null && lhs.IsCall && lhs.ArgCount > 0); Debug.Assert(lhs.BaseStyle != NodeStyle.Special); int c = lhs.ArgCount - 1; LNode ce = MakeSuperExpr(lhs.Args[c], ref primary, rhs); return(lhs.WithArgChanged(c, ce)); } }
public static LNode ForwardProperty(LNode prop, IMacroContext context) { LNode name, fwd, body; if (prop.ArgCount != 4) return null; LNode target = GetForwardingTarget(name = prop.Args[1], fwd = prop.Args[3]); if (target != null) { body = F.Braces(F.Call(S.get, F.Braces(F.Call(S.Return, target))).SetBaseStyle(NodeStyle.Special)); return prop.WithArgChanged(3, body); } else if ((body = fwd).Calls(S.Braces)) { var body2 = body.WithArgs(stmt => { if (stmt.Calls(S.get, 1) && (target = GetForwardingTarget(name, stmt.Args[0])) != null) return stmt.WithArgs(new VList<LNode>(F.Braces(F.Call(S.Return, target)))); if (stmt.Calls(S.set, 1) && (target = GetForwardingTarget(name, stmt.Args[0])) != null) return stmt.WithArgs(new VList<LNode>(F.Braces(F.Call(S.Assign, target, F.Id(S.value))))); return stmt; }); if (body2 != body) return prop.WithArgChanged(3, body2); } return null; }
public static LNode ForwardMethod(LNode fn, IMessageSink sink) { LNode args, fwd, body; if (fn.ArgCount != 4 || !(fwd = fn.Args[3]).Calls(S.Forward, 1) || !(args = fn.Args[2]).Calls(S.List)) return null; RVList<LNode> formalArgs = args.Args; RVList<LNode> argList = RVList<LNode>.Empty; foreach (var formalArg in formalArgs) { if (!formalArg.Calls(S.Var, 2)) return Reject(sink, formalArg, "'==>' expected a variable declaration here"); LNode argName = formalArg.Args[1]; if (argName.Calls(S.Assign, 2)) argName = argName.Args[0]; LNode @ref = formalArg.AttrNamed(S.Ref) ?? formalArg.AttrNamed(S.Out); if (@ref != null) argName = argName.PlusAttr(@ref); argList.Add(argName); } LNode target = GetForwardingTarget(fwd, fn.Args[1]); LNode call = F.Call(target, argList); bool isVoidFn = fn.Args[0].IsIdNamed(S.Void); body = F.Braces(isVoidFn ? call : F.Call(S.Return, call)); return fn.WithArgChanged(3, body); }
// Creates a temporary for an LValue (left side of `=`, or `ref` parameter) // e.g. f(x).Foo becomes f(x_N).Foo, and `var x_N = x` is added to `stmtSequence`, // where N is a unique integer for the temporary variable. LNode MaybeCreateTemporaryForLValue(LNode expr, ref VList <LNode> stmtSequence) { { LNode _, lhs; if (expr.Calls(CodeSymbols.Dot, 2) && (lhs = expr.Args[0]) != null || expr.CallsMin(CodeSymbols.Of, 1) && (lhs = expr.Args[0]) != null) { return(expr.WithArgChanged(0, MaybeCreateTemporaryForLValue(lhs, ref stmtSequence))); } else if ((_ = expr) != null && !_.IsCall) { return(expr); } else { var args = expr.Args.ToWList(); int i = 0; if (expr.CallsMin(S.IndexBracks, 1) || expr.CallsMin(S.NullIndexBracks, 1)) { // Consider foo[i]. We cannot always store `foo` in a temporary, as // this may change the code's behavior in case `foo` is a struct. i = 1; } for (; i < args.Count; i++) { if (!args[i].IsLiteral && !args[i].Attrs.Contains(_trivia_isTmpVar)) { LNode tmpVarName; stmtSequence.Add(TempVarDecl(Context, args[i], out tmpVarName)); args[i] = tmpVarName.PlusAttr(_trivia_isTmpVar); } } return(expr.WithArgs(args.ToVList())); } } }
public static LNode ContractsOnLambda(LNode fn, IMacroContext context) { LNode oldFn = fn; if (fn.ArgCount == 2) { var rw = new CodeContractRewriter(LNode.Missing, Id_lambda_function, context); fn = ProcessArgContractAttributes(fn, 0, rw, isLambda: true); if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[1]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(1, body); return(fn); } } return(null); }
public static LNode IfUnless(LNode node, IMacroContext context) { // #if(cond1, {...}, #elsif(cond2, {...}), #else({...})); // #unless(cond1, {...}, #else({...})); var args = node.Args; bool isUnless = node.Calls(__unless) && args.Count >= 2; if (isUnless) { node = node.WithArgChanged(0, F.Call(S.Not, args[0])); } if (args.Count >= 3) { LNode clause = args[2]; bool isElseIf = clause.Calls(__elsif) || clause.Calls(__elseif); if (clause.Calls(S.Else, 2) && clause[0].Calls("if", 1)) { // Although it's possible to accept "else if (foo)", "else if foo {...}" // would be parsed quite wrongly as "else (if foo {...})"! So issue a // warning to discourage the bad habit of writing "else if". context.Warning(clause[0].Target, "'else if' should be one word (elsif or elseif)"); isElseIf = true; clause = clause.WithArgChanged(0, clause[0][0]); } if (isElseIf) { var first3 = args.WithoutLast(args.Count - 3); // node: #if(cond1, {...}, #elsif(cond2, {...}), #elsif(cond3, {...}), #else({...})) // ^^^^^^^clause^^^^^^^ // ^^^^^^^^^^^^first3^^^^^^^^^^^^^^^^ // returns: #if(cond1, {...}, #if(cond2, {...}, #elsif(cond3, {...}), #else({...}))) LNode @else = clause.WithTarget(S.If); if (args.Count > 3) { @else = @else.WithArgs(@else.Args.AddRange(args.Slice(3))); } return(node.WithArgs(first3.WithoutLast(1).Add(@else))); } if (clause.Calls(S.Else, 1) && args.Count == 3) { return(node.WithArgChanged(2, clause[0])); } } return(isUnless ? node : null); }
public static LNode priorityTestHi(LNode node, IMessageSink sink) { if (node.ArgCount >= 1 && !node[0].IsIdNamed("hi")) { return(node.WithArgChanged(0, LNode.Id("hi"))); } return(null); }
public static LNode ContractsOnMethod(LNode fn, IMacroContext context) { // Performance note: one should keep in mind that this macro usually // has no effect. It looks for contracts and usually finds none, so // we should try to search quickly. Luckily, LNode methods like // n.WithArgChanged(i,N) do not allocate a new node if the new value // equals the old value (if n[i] == N). LNode oldFn = fn; if (fn.ArgCount >= 4) { var rw = new CodeContractRewriter(fn.Args[0], fn.Args[1], context); // If this thing has an argument list, scan it fn = ProcessArgContractAttributes(fn, 2, rw); // Scan attributes on return type, then attributes on the whole method if (fn.Args[0].HasAttrs) { fn = fn.WithArgChanged(0, fn.Args[0].WithAttrs(rw.Process(fn.Args[0].Attrs, null))); } if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); // this is the common case } else { var body = fn.Args[3]; if (!body.Calls(S.Braces) // Add braces in case of void LambdaMethod() => expr; ) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(3, body); return(fn); } } return(null); }
public static LNode TupleType(LNode node, IMacroContext context) { var stem = node.Args[0, F.Missing]; if (stem.IsId && (stem.Name == S.AltList || stem.Name == S.Tuple)) { var tupleMakers = MaybeInitTupleMakers(context.ScopedProperties); var bareType = tupleMakers.TryGet(node.Args.Count - 1, new Pair<LNode, LNode>()).A; if (bareType == null) bareType = ((Pair<LNode, LNode>)context.ScopedProperties[DefaultTupleMaker]).A; if (bareType != null) return node.WithArgChanged(0, bareType); } return null; }
LNode ESEInForLoop(LNode stmt, VList <LNode> attrs, VList <LNode> init, LNode cond, VList <LNode> inc, LNode block) { // TODO: handle multi-int and multi-inc var preInit = VList <LNode> .Empty; var init_apos = init.SmartSelect(init1 => { init1 = EliminateSequenceExpressionsInExecStmt(init1); if (init1.CallsMin(__numrunSequence, 1)) { preInit.AddRange(init1.Args.WithoutLast(1)); return(init1.Args.Last); } return(init1); }); var cond_apos = BubbleUpBlocks(cond); var inc_apos = inc.SmartSelectMany(inc1 => { inc1 = BubbleUpBlocks(inc1); return(inc1.AsList(__numrunSequence)); }); block = EliminateSequenceExpressionsInChildStmt(block); if (init_apos != init || cond_apos != cond || inc_apos != inc) { init = init_apos; if (inc_apos != inc) { var blockStmts = block.AsList(S.Braces).AddRange(inc_apos); block = blockStmts.AsLNode(S.Braces); inc = LNode.List(); } if (cond_apos.CallsMin(__numrunSequence, 1)) { var preCond = cond_apos.Args.WithoutLast(1); cond = cond_apos.Args.Last; stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement))); } else { stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block)); } if (preInit.Count != 0) { stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement); } return(stmt); } else { return(stmt.WithArgChanged(3, block)); } }
static LNode ConvertToNormalDot(LNode prefix, LNode suffix) { // Essentially, we must increase the precedence of ?. to convert it to a normal dot. // This often requires multiple stages, as in: // (a.b) ?. (c().d<x>) ==> (a.b ?. c().d)<x> ==> (a.b ?. c)().d<x> ==> a.b.c().d<x> // (a.b) ?. @'of(c().d, x) ==> @'of((a.b) ?. c().d, x) ==> @'of((a.b ?. c)().d, x) ==> @'of(a.b.c().d, x) // The cases to be handled are... // x ?. y <=> x ?. y ==> x.y (1) // x ?. "foo" <=> x ?. "foo" ==> x."Foo" (1) // x ?. ++y <=> x ?. @`++`(y) ==> x.(++y) (1) // x ?. y<a, b> <=> x ?. @'of(y, a, b) ==> #of(x.y, a, b) (2) // x ?. y[a, b] <=> x ?. @`[]`(y, a, b)==> @`[]`(x.y, a, b) (2) // x ?. y.z <=> x ?. @.(y, z) ==> #.(x?.y, z) (2) // x ?. y::z <=> x ?. @`::`(y, z) ==> #::(x?.y, z) (2) // x ?. y:::z <=> x ?. @`:::`(y, z) ==> #:::(x?.y, z) (2) // x ?. y->z <=> x ?. @`->`(y, z) ==> #->(x?.y, z) (2) // x ?. y(->z) <=> x ?. @'cast(y, z) ==> @'cast(x?.y, z) (2) // x ?. y++ <=> x ?. @`suf++`(y) ==> @`suf++`(x?.y) (2) // x ?. y ?. z <=> x ?. @`?.`(y, z) ==> @`?.`(x.y, z) // x ?. y(a, b) <=> x ?. y(a, b) ==> x.y(a, b) (3: default case) // The following groups are handled essentially the same way: // 1. Ids, Literals and prefix operators (+ - ++ -- ! ~ new) // 2. @'of, @`[]`, @`.`, @`::`, @`:::`, @`=:`, @`->`, @'cast, @`suf++`, @`suf--` // 3. All other calls var c = suffix.ArgCount; var name = suffix.Name; if (suffix.IsCall) { if (c == 1 && PrefixOps.Contains(name)) // (1) { return(F.Dot(prefix, suffix)); } else if (c >= 1 && OtherOps.Contains(name)) // (2) { var inner = ConvertToNormalDot(prefix, suffix.Args[0]); return(suffix.WithArgChanged(0, inner)); } else // (3) { var inner = ConvertToNormalDot(prefix, suffix.Target); return(suffix.WithTarget(inner)); } } else // (1) { return(F.Dot(prefix, suffix)); } }
public static LNode Constructor(LNode cons, IMacroContext context) { if (cons.ArgCount >= 3 && cons.Args[1].IsIdNamed(S.This)) { var anc = context.Ancestors; LNode space = anc.TryGet(anc.Count - 3, LNode.Missing), typeName; Symbol type = EcsValidators.SpaceStatementKind(space); if (type != null && anc[anc.Count - 2] == space.Args[2]) { typeName = space.Args[0]; return(cons.WithArgChanged(1, F.Id(KeyNameComponentOf(typeName)))); } } return(null); }
public static LNode ForwardMethod(LNode fn, IMacroContext context) { LNode args, fwd, body; if (fn.ArgCount != 4 || !(fwd = fn.Args[3]).Calls(S.Forward, 1) || !(args = fn.Args[2]).Calls(S.AltList)) return null; VList<LNode> argList = GetArgNamesFromFormalArgList(args, formalArg => context.Write(Severity.Error, formalArg, "'==>': Expected a variable declaration here")); LNode target = GetForwardingTarget(fn.Args[1], fwd); LNode call = F.Call(target, argList); bool isVoidFn = fn.Args[0].IsIdNamed(S.Void); body = F.Braces(isVoidFn ? call : F.Call(S.Return, call)); return fn.WithArgChanged(3, body); }
static LNode GetForwardingTarget(LNode fwd, LNode methodName) { if (fwd.Calls(S.Forward, 1)) { LNode target = fwd.Args[0]; if (target.Calls(S.Dot, 2) && (target.Args[1].IsIdNamed(_hash) || target.Args[1].IsIdNamed(__))) { return(target.WithArgChanged(1, target.Args[1].WithName( EcsNodePrinter.KeyNameComponentOf(methodName)))); } return(target); } else { return(null); } }
protected override LNode DoneAttaching(LNode node, LNode parent, int indexInParent) { // Constructors are funky in EC# because EC# generalizes constructors so // you can write, for example, `this() { F(); base(); G(); }`. // Plain C# constructors like `this() : base() { G(); }`, `base(x)` // are actually stored like `this() { base(); G(); }`, where the colon // is the beginning of the Range of the constructor body and the opening // brace is the range of the Target of the method body. This makes it // difficult to get the trivia attached the way we want. For example, this // constructor: // // Foo(int x) // : base(x) // { // Bar(); // } // // Gets trivia attached like this: // // #cons(``, Foo, #(int x), @`%newline` (@`%newline` `'{}`)( // [`%appendStatement`] base(x), Bar())); // // This code changes the trivia to something more reasonable: // // #cons(``, Foo, #(int x), @`%newline` { base(x), Bar() }); // LNode baseCall; if (node.CallsMin(S.Braces, 1) && parent != null && parent.Calls(S.Constructor) && (baseCall = node.Args[0]).Range.StartIndex < node.Target.Range.StartIndex) { if (RemoveLeadingNewline(ref node) != null) { node = node.WithArgChanged(0, baseCall.WithoutAttrNamed(S.TriviaAppendStatement)); } LNode target = node.Target, newline_trivia; if ((newline_trivia = RemoveLeadingNewline(ref target)) != null) { node = node.WithTarget(target).PlusAttrBefore(newline_trivia); } } return(node); }
public static LNode TupleType(LNode node, IMessageSink sink) { var stem = node.Args[0, F._Missing]; if (stem.IsId && (stem.Name == S.List || stem.Name == S.Tuple)) { MaybeInitTupleMakers(); var bareType = TupleMakers.TryGet(node.Args.Count - 1, new Pair <LNode, LNode>()).A; if (bareType == null) { bareType = DefaultTupleMaker.A; } if (bareType != null) { return(node.WithArgChanged(0, bareType)); } } return(null); }
public static LNode ForwardMethod(LNode fn, IMacroContext context) { LNode args, fwd, body; if (fn.ArgCount != 4 || !(fwd = fn.Args[3]).Calls(S.Forward, 1) || !(args = fn.Args[2]).Calls(S.AltList)) { return(null); } VList <LNode> argList = GetArgNamesFromFormalArgList(args, formalArg => context.Write(Severity.Error, formalArg, "'==>': Expected a variable declaration here")); LNode target = GetForwardingTarget(fwd, fn.Args[1]); LNode call = F.Call(target, argList); bool isVoidFn = fn.Args[0].IsIdNamed(S.Void); body = F.Braces(isVoidFn ? call : F.Call(S.Return, call)); return(fn.WithArgChanged(3, body)); }
public static LNode TupleType(LNode node, IMacroContext context) { var stem = node.Args[0, F.Missing]; if (stem.IsId && (stem.Name == S.AltList || stem.Name == S.Tuple)) { var tupleMakers = MaybeInitTupleMakers(context.ScopedProperties); var bareType = tupleMakers.TryGet(node.Args.Count - 1, new Pair <LNode, LNode>()).A; if (bareType == null) { bareType = ((Pair <LNode, LNode>)context.ScopedProperties[DefaultTupleMaker]).A; } if (bareType != null) { return(node.WithArgChanged(0, bareType)); } } return(null); }
public void PrependStmtsToGetterOrSetter(ref LNode braces, int getterIndex, LNode getter) { if (!PrependStmts.IsEmpty) { if (getter.ArgCount == 0) { Context.Write(Severity.Error, getter, "`{0}`: contracts cannot be applied to autoproperties. " + "A body is required, but you can use [field] on the property to add a body to `get` and `set` automatically.", getter); return; } else if (getter.ArgCount == 1) { var body = getter.Args[0]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, PrependStmts)); getter = getter.WithArgs(body); braces = braces.WithArgChanged(getterIndex, getter); } } }
public static LNode ForwardMethod(LNode fn, IMessageSink sink) { LNode args, fwd, body; if (fn.ArgCount != 4 || !(fwd = fn.Args[3]).Calls(S.Forward, 1) || !(args = fn.Args[2]).Calls(S.List)) { return(null); } RVList <LNode> formalArgs = args.Args; RVList <LNode> argList = RVList <LNode> .Empty; foreach (var formalArg in formalArgs) { if (!formalArg.Calls(S.Var, 2)) { return(Reject(sink, formalArg, "'==>' expected a variable declaration here")); } LNode argName = formalArg.Args[1]; if (argName.Calls(S.Assign, 2)) { argName = argName.Args[0]; } LNode @ref = formalArg.AttrNamed(S.Ref) ?? formalArg.AttrNamed(S.Out); if (@ref != null) { argName = argName.PlusAttr(@ref); } argList.Add(argName); } LNode target = GetForwardingTarget(fwd, fn.Args[1]); LNode call = F.Call(target, argList); bool isVoidFn = fn.Args[0].IsIdNamed(S.Void); body = F.Braces(isVoidFn ? call : F.Call(S.Return, call)); return(fn.WithArgChanged(3, body)); }
static LNode ConvertToNormalDot(LNode prefix, LNode suffix) { // Essentially, we must increase the precedence of ?. to convert it to a normal dot. // This often requires multiple stages, as in: // (a.b) ?. (c().d<x>) ==> (a.b ?. c().d)<x> ==> (a.b ?. c)().d<x> ==> a.b.c().d<x> // (a.b) ?. #of(c().d, x) ==> #of((a.b) ?. c().d, x) ==> #of((a.b ?. c)().d, x) ==> #of(a.b.c().d, x) // The cases to be handled are... // x ?. y <=> x ?. y ==> x.y (1) // x ?. "foo" <=> x ?. "foo" ==> x."Foo" (1) // x ?. ++y <=> x ?. @`++`(y) ==> x.(++y) (1) // x ?. y<a, b> <=> x ?. #of(y, a, b) ==> #of(x.y, a, b) (2) // x ?. y[a, b] <=> x ?. @`[]`(y, a, b)==> @`[]`(x.y, a, b) (2) // x ?. y.z <=> x ?. @.(y, z) ==> #.(x?.y, z) (2) // x ?. y::z <=> x ?. @`::`(y, z) ==> #::(x?.y, z) (2) // x ?. y:::z <=> x ?. @`:::`(y, z) ==> #:::(x?.y, z) (2) // x ?. y->z <=> x ?. @`->`(y, z) ==> #->(x?.y, z) (2) // x ?. y(->z) <=> x ?. #cast(y, z) ==> #cast(x?.y, z) (2) // x ?. y++ <=> x ?. @`suf++`(y) ==> @`suf++`(x?.y) (2) // x ?. y ?. z <=> x ?. @`?.`(y, z) ==> @`?.`(x.y, z) // x ?. y(a, b) <=> x ?. y(a, b) ==> x.y(a, b) (3: default case) // The following groups are handled essentially the same way: // 1. Ids, Literals and prefix operators (+ - ++ -- ! ~ new) // 2. #of, @`[]`, @`.`, @`::`, @`:::`, @`=:`, @`->`, #cast, @`suf++`, @`suf--` // 3. All other calls var c = suffix.ArgCount; var name = suffix.Name; if (suffix.IsCall) { if (c == 1 && PrefixOps.Contains(name)) // (1) return F.Dot(prefix, suffix); else if (c >= 1 && OtherOps.Contains(name)) { // (2) var inner = ConvertToNormalDot(prefix, suffix.Args[0]); return suffix.WithArgChanged(0, inner); } else { // (3) var inner = ConvertToNormalDot(prefix, suffix.Target); return suffix.WithTarget(inner); } } else // (1) return F.Dot(prefix, suffix); }
public static LNode ContractsOnLambda(LNode fn, IMacroContext context) { // Performance note: one should keep in mind that this macro usually // has no effect. It looks for contracts and usually finds none. LNode oldFn = fn; if (fn.ArgCount == 2) { var rw = new CodeContractRewriter(LNode.Missing, Id_lambda_function, context); // If this thing has an argument list, scan it fn = ProcessArgContractAttributes(fn, 0, rw, isLambda: true); // Scan attributes on the lambda as a whole if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); // this is the common case } else { var body = fn.Args[1]; if (!body.Calls(S.Braces) // Add braces in case of void LambdaMethod() => expr; ) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(1, body); return(fn); } } return(null); }
[LexicalMacro("notnull T Prop {...}; T this[[requires(expr)] T arg] {...}; " + "T Prop { [requires(expr)] set; }; [ensures(expr)] T Prop {...}; " + "[ensuresOnThrow(expr)] T Prop {...}; [ensuresOnThrow<Exception>(expr)] T Prop {...}", "Generates contract checks in a property. You can apply contract attributes to " + "the property itself, to the getter, to the setter, or all three. When the [requires] " + "or [assert] attributes are applied to the property itself, they are treated as if " + "they were applied to the getter; but when the [ensures], [ensuresAssert], notnull, " + "and [ensuresOnThrow] attributes are applied to the property itself, they are treated " + "as if they were applied to both the getter and the setter separately.", "#property", Mode = MacroMode.Passive | MacroMode.PriorityInternalFallback)] public static LNode ContractsOnProperty(LNode prop, IMacroContext context) { LNode propArgs, oldProp = prop; if (prop.ArgCount == 4) { LNode braces = prop[3]; var oldBraces = braces; var rw = new CodeContractRewriter(prop.Args[0], prop.Args[1], context); prop = ProcessArgContractAttributes(prop, 2, rw); VList <LNode> cAttrs = LNode.List(); prop = prop.WithArgChanged(0, GrabContractAttrs(prop.Args[0], ref cAttrs, ContractAppliesTo.Getter)); prop = GrabContractAttrs(prop, ref cAttrs); LNode getter = null, setter = null; int getterIndex = -1, setterIndex = -1; VList <LNode> getterAttrs = LNode.List(), setterAttrs = LNode.List(); bool isLambdaProperty = !braces.Calls(S.Braces); if (isLambdaProperty) { if (cAttrs.Count == 0) { return(null); } getterAttrs = cAttrs; getter = LNode.Call(CodeSymbols.get, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(braces)))).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special); braces = LNode.Call(CodeSymbols.Braces, LNode.List(getter)).SetStyle(NodeStyle.Statement); getterIndex = 0; } else { for (int i = 0; i < braces.Args.Count; i++) { var part = braces.Args[i]; if (part.Calls(S.get)) { getter = part; getterIndex = i; } if (part.Calls(S.set)) { setter = part; setterIndex = i; } } if (cAttrs.Count != 0) { getterAttrs = cAttrs.Where(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Getter) != 0); setterAttrs = cAttrs.Where(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Setter) != 0); } } var sharedPrependStmts = rw.PrependStmts; if (getter != null) { getter = GrabContractAttrs(getter, ref getterAttrs); rw.Process(getterAttrs, null); rw.PrependStmtsToGetterOrSetter(ref braces, getterIndex, getter); } if (setter != null) { rw.PrependStmts = sharedPrependStmts; setter = GrabContractAttrs(setter, ref setterAttrs); rw.Process(setterAttrs, LNode.Id(CodeSymbols.value), true); rw.PrependStmtsToGetterOrSetter(ref braces, setterIndex, setter); } if (braces == oldBraces) { return(null); } else { return(prop.WithArgChanged(3, braces)); } } return(null); }
public static LNode BackingField(LNode prop, IMessageSink sink) { LNode propType, propName, propArgs, body; if (prop.ArgCount != 4 || !(body = prop.Args[3]).Calls(S.Braces)) { return(null); } // Look for an attribute of the form [field], [field name] or [field Type name] LNode fieldAttr = null, fieldName; bool autoType = false; int i; for (i = 0; i < prop.Attrs.Count; i++) { LNode attr = prop.Attrs[i]; if (attr.IsIdNamed(_field)) { fieldAttr = attr; break; } else if (attr.Calls(S.Var, 2)) { LNode fieldVarAttr = null; attr = attr.WithoutAttrNamed(__field, out fieldVarAttr); if (fieldVarAttr != null && fieldVarAttr.IsId || (autoType = attr.Args[0].IsIdNamed(_field))) { fieldAttr = attr; break; } } } if (fieldAttr == null) { return(null); } // Extract the type and name of the backing field, if specified LNode field = fieldAttr; propType = prop.Args[0]; propName = prop.Args[1]; propArgs = prop.Args[2]; if (field.IsId) { fieldName = F.Id(ChooseFieldName(Loyc.Ecs.EcsNodePrinter.KeyNameComponentOf(propName))); field = F.Call(S.Var, propType, fieldName).WithAttrs(fieldAttr.Attrs); } else { fieldName = field.Args[1]; if (fieldName.Calls(S.Assign, 2)) { fieldName = fieldName.Args[0]; } } if (autoType) { field = field.WithArgChanged(0, propType); } // Construct the new backing field, fill in the property getter and/or setter if (body.ArgCount == 0) { body = body.WithArgs(LNode.Id(S.get)); } LNode newBody = body.WithArgs(body.Args.SmartSelect(stmt => { var fieldAccessExpr = fieldName; if (propArgs.ArgCount > 0) { // Special case: the property has arguments, // e.g. [field List<T> L] T this[int x] { get; set; } // ==> List<T> L; T this[int x] { get { return L[x]; } set { L[x] = value; } } var argList = GetArgNamesFromFormalArgList(propArgs, formalArg => sink.Write(Severity.Error, formalArg, "'field' macro expected a variable declaration here")); fieldAccessExpr = F.Call(S.IndexBracks, argList.Insert(0, fieldName)); } var attrs = stmt.Attrs; if (stmt.IsIdNamed(S.get)) { stmt = F.Call(stmt.WithoutAttrs(), F.Braces(F.Call(S.Return, fieldAccessExpr))).WithAttrs(attrs); stmt.BaseStyle = NodeStyle.Special; } if (stmt.IsIdNamed(S.set)) { stmt = F.Call(stmt.WithoutAttrs(), F.Braces(F.Call(S.Assign, fieldAccessExpr, F.Id(S.value)))).WithAttrs(attrs); stmt.BaseStyle = NodeStyle.Special; } return(stmt); })); if (newBody == body) { sink.Write(Severity.Warning, fieldAttr, "The body of the property does not contain a 'get;' or 'set;' statement without a body, so no code was generated to get or set the backing field."); } prop = prop.WithAttrs(prop.Attrs.RemoveAt(i)).WithArgChanged(3, newBody); return(F.Call(S.Splice, new VList <LNode>(field, prop))); }
public LNode EliminateSequenceExpressions(LNode stmt, bool isDeclContext) { LNode retType, name, argList, bases, body, initValue; if (EcsValidators.SpaceDefinitionKind(stmt, out name, out bases, out body) != null) { // Space definition: class, struct, etc. return body == null ? stmt : stmt.WithArgChanged(2, EliminateSequenceExpressions(body, true)); } else if (EcsValidators.MethodDefinitionKind(stmt, out retType, out name, out argList, out body, true) != null) { // Method definition return body == null ? stmt : stmt.WithArgChanged(3, EliminateSequenceExpressionsInLambdaExpr(body, retType)); } else if (EcsValidators.IsPropertyDefinition(stmt, out retType, out name, out argList, out body, out initValue)) { // Property definition stmt = stmt.WithArgChanged(3, body.WithArgs(part => { if (part.ArgCount == 1 && part[0].Calls(S.Braces)) part = part.WithArgChanged(0, EliminateSequenceExpressions(part[0], false)); return part; })); if (initValue != null) { var initMethod = EliminateRunSeqFromInitializer(retType, name, ref initValue); if (initMethod != null) { stmt = stmt.WithArgChanged(4, initValue); return LNode.Call((Symbol) "#runSequence", LNode.List(stmt, initMethod)); } } return stmt; } else if (stmt.Calls(CodeSymbols.Braces)) { return stmt.WithArgs(EliminateSequenceExpressions(stmt.Args, isDeclContext)); } else if (!isDeclContext) { return EliminateSequenceExpressionsInExecStmt(stmt); } else if (stmt.CallsMin(S.Var, 2)) { // Eliminate blocks from field member var results = new List<LNode> { stmt }; var vars = stmt.Args; var varType = vars[0]; for (int i = 1; i < vars.Count; i++) { var @var = vars[i]; if (@var.Calls(CodeSymbols.Assign, 2) && (name = @var.Args[0]) != null && (initValue = @var.Args[1]) != null) { var initMethod = EliminateRunSeqFromInitializer(varType, name, ref initValue); if (initMethod != null) { results.Add(initMethod); vars[i] = vars[i].WithArgChanged(1, initValue); } } } if (results.Count > 1) { results[0] = stmt.WithArgs(vars); return LNode.List(results).AsLNode(__numrunSequence); } return stmt; } else return stmt; }
public static LNode ContractsOnProperty(LNode prop, IMacroContext context) { // Performance note: one should keep in mind that this macro usually // has no effect. It looks for contracts and usually finds none. LNode oldProp = prop; if (prop.ArgCount == 4) { LNode braces = prop[3]; var oldBraces = braces; var rw = new CodeContractRewriter(prop.Args[0], prop.Args[1], context); // If this has an argument list (this[...]), process its contract attributes prop = ProcessArgContractAttributes(prop, 2, rw); // Remove contract attributes from the property and store in a list VList <LNode> cAttrs = LNode.List(); prop = prop.WithArgChanged(0, GrabContractAttrs(prop.Args[0], ref cAttrs, ContractAppliesTo.Getter)); prop = GrabContractAttrs(prop, ref cAttrs); // Find the getter and setter LNode getter = null, setter = null; int getterIndex = -1, setterIndex = -1; VList <LNode> getterAttrs = LNode.List(), setterAttrs = LNode.List(); bool isLambdaProperty = !braces.Calls(S.Braces); if (isLambdaProperty) { if (cAttrs.Count == 0) { return(null); // lambda property has no contract attributes } // Transform into a normal property getterAttrs = cAttrs; getter = LNode.Call(CodeSymbols.get, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(braces)))).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special); braces = LNode.Call(CodeSymbols.Braces, LNode.List(getter)).SetStyle(NodeStyle.Statement); getterIndex = 0; } else { for (int i = 0; i < braces.Args.Count; i++) { var part = braces.Args[i]; if (part.Calls(S.get)) { getter = part; getterIndex = i; } if (part.Calls(S.set)) { setter = part; setterIndex = i; } } // Now create separate lists of contract attributes for the getter and the setter if (cAttrs.Count != 0) { getterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Getter) != 0); setterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Setter) != 0); } } // Process the discovered attributes to produce prepended statements var sharedPrependStmts = rw.PrependStmts; if (getter != null) { getter = GrabContractAttrs(getter, ref getterAttrs); rw.Process(getterAttrs, null); rw.PrependStmtsToGetterOrSetter(ref braces, getterIndex, getter); } if (setter != null) { rw.PrependStmts = sharedPrependStmts; setter = GrabContractAttrs(setter, ref setterAttrs); rw.Process(setterAttrs, LNode.Id(CodeSymbols.value), true); rw.PrependStmtsToGetterOrSetter(ref braces, setterIndex, setter); } // Update the property if (braces == oldBraces) { return(null); // this is the common case } else { return(prop.WithArgChanged(3, braces)); } } return(null); }
LNode ESEInForLoop(LNode stmt, VList<LNode> attrs, VList<LNode> init, LNode cond, VList<LNode> inc, LNode block) { // TODO: handle multi-int and multi-inc var preInit = VList<LNode>.Empty; var init_apos = init.SmartSelect(init1 => { init1 = EliminateSequenceExpressionsInExecStmt(init1); if (init1.CallsMin(__numrunSequence, 1)) { preInit.AddRange(init1.Args.WithoutLast(1)); return init1.Args.Last; } return init1; }); var cond_apos = BubbleUpBlocks(cond); var inc_apos = inc.SmartSelectMany(inc1 => { inc1 = BubbleUpBlocks(inc1); return inc1.AsList(__numrunSequence); }); block = EliminateSequenceExpressionsInChildStmt(block); if (init_apos != init || cond_apos != cond || inc_apos != inc) { init = init_apos; if (inc_apos != inc) { var blockStmts = block.AsList(S.Braces).AddRange(inc_apos); block = blockStmts.AsLNode(S.Braces); inc = LNode.List(); } if (cond_apos.CallsMin(__numrunSequence, 1)) { var preCond = cond_apos.Args.WithoutLast(1); cond = cond_apos.Args.Last; stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement))); } else { stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block)); } if (preInit.Count != 0) { stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement); } return stmt; } else { return stmt.WithArgChanged(3, block); } }
public LNode EliminateBlockExprs(LNode stmt, bool isDeclContext) { LNode retType, name, argList, bases, body, initValue; if (EcsValidators.SpaceDefinitionKind(stmt, out name, out bases, out body) != null) { return(body == null ? stmt : stmt.WithArgChanged(2, EliminateBlockExprs(body, true))); } else if (EcsValidators.MethodDefinitionKind(stmt, out retType, out name, out argList, out body, true) != null) { return(body == null ? stmt : stmt.WithArgChanged(3, EliminateBlockExprs(body, false))); } else if (EcsValidators.IsPropertyDefinition(stmt, out retType, out name, out argList, out body, out initValue)) { stmt = stmt.WithArgChanged(3, EliminateBlockExprs(body, false)); if (initValue != null) { var initMethod = EliminateRunSeqFromInitializer(retType, name, ref initValue); if (initMethod != null) { stmt = stmt.WithArgChanged(4, initValue); return(LNode.Call(CodeSymbols.Splice, LNode.List(stmt, initMethod))); } } return(stmt); } else if (!isDeclContext) { return(EliminateBlockExprsInExecStmt(stmt)); } else if (stmt.CallsMin(S.Var, 2)) { var results = new List <LNode> { stmt }; var vars = stmt.Args; var varType = vars[0]; for (int i = 1; i < vars.Count; i++) { { var tmp_1 = vars[i]; if (tmp_1.Calls(CodeSymbols.Assign, 2) && (name = tmp_1.Args[0]) != null && (initValue = tmp_1.Args[1]) != null) { var initMethod = EliminateRunSeqFromInitializer(varType, name, ref initValue); if (initMethod != null) { results.Add(initMethod); vars[i] = vars[i].WithArgChanged(1, initValue); } } } } if (results.Count > 1) { results[0] = stmt.WithArgs(vars); return(LNode.List(results).AsLNode(S.Splice)); } return(stmt); } else { return(stmt); } }
public static LNode BackingField(LNode prop, IMessageSink sink) { LNode type, name, body; if (prop.ArgCount != 3 || !(body = prop.Args[2]).Calls(S.Braces)) { return(null); } LNode fieldAttr = null, fieldVarAttr = null; LNode fieldName; bool autoType = false; int i; for (i = 0; i < prop.Attrs.Count; i++) { LNode attr = prop.Attrs[i]; if (attr.IsIdNamed(_field) || attr.Calls(S.Var, 2) && ((autoType = attr.Args[0].IsIdNamed(_field)) || (fieldVarAttr = attr.AttrNamed(_field)) != null && fieldVarAttr.IsId)) { fieldAttr = attr; break; } } if (fieldAttr == null) { return(null); } LNode field = fieldAttr; type = prop.Args[0]; if (field.IsId) { name = prop.Args[1]; fieldName = F.Id(ChooseFieldName(Ecs.EcsNodePrinter.KeyNameComponentOf(name))); field = F.Call(S.Var, type, fieldName).WithAttrs(fieldAttr.Attrs); } else { fieldName = field.Args[1]; if (fieldName.Calls(S.Assign, 2)) { fieldName = fieldName.Args[0]; } } if (autoType) { field = field.WithArgChanged(0, type); } if (fieldVarAttr != null) { field = field.WithoutAttrNamed(_field); } LNode newBody = body.WithArgs(body.Args.SmartSelect(stmt => { var attrs = stmt.Attrs; if (stmt.IsIdNamed(S.get)) { stmt = F.Call(stmt.WithoutAttrs(), F.Braces(F.Call(S.Return, fieldName))).WithAttrs(attrs); stmt.BaseStyle = NodeStyle.Special; } if (stmt.IsIdNamed(S.set)) { stmt = F.Call(stmt.WithoutAttrs(), F.Braces(F.Call(S.Assign, fieldName, F.Id(S.value)))).WithAttrs(attrs); stmt.BaseStyle = NodeStyle.Special; } return(stmt); })); if (newBody == body) { sink.Write(Severity.Warning, fieldAttr, "The body of the property does not contain a 'get;' or 'set;' statement without a body, so no code was generated to get or set the backing field."); } prop = prop.WithAttrs(prop.Attrs.RemoveAt(i)).WithArgChanged(2, newBody); return(F.Call(S.Splice, new RVList <LNode>(field, prop))); }
LNode EliminateSequenceExpressionsInExecStmt(LNode stmt) { { LNode block, collection, cond, init, initValue, loopVar, name, tmp_11, tmp_12, type; VList<LNode> attrs, incs, inits; if (stmt.Calls(CodeSymbols.Braces)) return stmt.WithArgs(EliminateSequenceExpressions(stmt.Args, false)); else if (stmt.CallsMin(CodeSymbols.If, 1) || stmt.Calls(CodeSymbols.UsingStmt, 2) || stmt.Calls(CodeSymbols.Lock, 2) || stmt.Calls(CodeSymbols.Switch, 2) && stmt.Args[1].Calls(CodeSymbols.Braces)) return ProcessBlockCallStmt(stmt, 1); else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fixed, 2) && (init = stmt.Args[0]) != null && (block = stmt.Args[1]) != null) { init = EliminateSequenceExpressionsInExecStmt(init); block = EliminateSequenceExpressionsInChildStmt(block); if (init.CallsMin(__numrunSequence, 1)) { return LNode.Call(LNode.List(attrs), CodeSymbols.Braces, LNode.List().AddRange(init.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.Fixed, LNode.List(init.Args.Last, block)))).SetStyle(NodeStyle.Statement); } else return stmt.WithArgChanged(1, block); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.While, 2) && (cond = stmt.Args[0]) != null && (block = stmt.Args[1]) != null) { cond = BubbleUpBlocks(cond); block = EliminateSequenceExpressionsInChildStmt(block); if (cond.CallsMin(__numrunSequence, 1)) { return LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList), LNode.Missing, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(cond.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.If, LNode.List(cond.Args.Last, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement))); } else return stmt.WithArgChanged(1, block); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.DoWhile, 2) && (block = stmt.Args[0]) != null && (cond = stmt.Args[1]) != null) { block = EliminateSequenceExpressionsInChildStmt(block); cond = BubbleUpBlocks(cond); if (cond.CallsMin(__numrunSequence, 1)) { var continue_N = F.Id(NextTempName(Context, "continue_")); var bodyStmts = block.AsList(S.Braces); bodyStmts.AddRange(cond.Args.WithoutLast(1)); bodyStmts.Add(LNode.Call(CodeSymbols.Assign, LNode.List(continue_N, cond.Args.Last)).SetStyle(NodeStyle.Operator)); return LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Id(CodeSymbols.Bool), LNode.Call(CodeSymbols.Assign, LNode.List(continue_N, LNode.Literal(true))))))), continue_N, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(bodyStmts)).SetStyle(NodeStyle.Statement))); } else return stmt.WithArgChanged(0, block); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.For, 4) && stmt.Args[0].Calls(CodeSymbols.AltList) && (cond = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (block = stmt.Args[3]) != null) { inits = stmt.Args[0].Args; incs = stmt.Args[2].Args; return ESEInForLoop(stmt, attrs, inits, cond, incs, block); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.ForEach, 3) && (tmp_11 = stmt.Args[0]) != null && tmp_11.Calls(CodeSymbols.Var, 2) && (type = tmp_11.Args[0]) != null && (loopVar = tmp_11.Args[1]) != null && (collection = stmt.Args[1]) != null && (block = stmt.Args[2]) != null) { block = EliminateSequenceExpressionsInChildStmt(block); collection = BubbleUpBlocks(collection); if (collection.CallsMin(__numrunSequence, 1)) { return LNode.Call(LNode.List(attrs), CodeSymbols.Braces, LNode.List().AddRange(collection.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.ForEach, LNode.List(LNode.Call(CodeSymbols.Var, LNode.List(type, loopVar)), collection.Args.Last, block)))).SetStyle(NodeStyle.Statement); } else { return stmt.WithArgChanged(stmt.Args.Count - 1, block); } } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Var, 2) && (type = stmt.Args[0]) != null && (tmp_12 = stmt.Args[1]) != null && tmp_12.Calls(CodeSymbols.Assign, 2) && (name = tmp_12.Args[0]) != null && (initValue = tmp_12.Args[1]) != null) { var initValue_apos = BubbleUpBlocks(initValue); if (initValue_apos != initValue) { { LNode last; VList<LNode> stmts; if (initValue_apos.CallsMin((Symbol) "#runSequence", 1) && (last = initValue_apos.Args[initValue_apos.Args.Count - 1]) != null) { stmts = initValue_apos.Args.WithoutLast(1); return LNode.Call((Symbol) "#runSequence", LNode.List().AddRange(stmts).Add(LNode.Call(LNode.List(attrs), CodeSymbols.Var, LNode.List(type, LNode.Call(CodeSymbols.Assign, LNode.List(name, last)))))); } else return LNode.Call(LNode.List(attrs), CodeSymbols.Var, LNode.List(type, LNode.Call(CodeSymbols.Assign, LNode.List(name, initValue_apos)))); } } } else if (stmt.CallsMin(S.Try, 2)) { return ESEInTryStmt(stmt); } else if (stmt.HasSpecialName && stmt.ArgCount >= 1 && stmt.Args.Last.Calls(S.Braces)) { return ProcessBlockCallStmt(stmt, stmt.ArgCount - 1); } else { // Ordinary expression statement return BubbleUpBlocks(stmt, stmtContext: true); } } return stmt; }
// Creates a temporary for an LValue (left side of `=`, or `ref` parameter) // e.g. f(x).Foo becomes f(x_N).Foo, and `var x_N = x` is added to `stmtSequence`, // where N is a unique integer for the temporary variable. LNode MaybeCreateTemporaryForLValue(LNode expr, ref VList<LNode> stmtSequence) { { LNode _, lhs; if (expr.Calls(CodeSymbols.Dot, 2) && (lhs = expr.Args[0]) != null || expr.CallsMin(CodeSymbols.Of, 1) && (lhs = expr.Args[0]) != null) return expr.WithArgChanged(0, MaybeCreateTemporaryForLValue(lhs, ref stmtSequence)); else if ((_ = expr) != null && !_.IsCall) return expr; else { var args = expr.Args.ToWList(); int i = 0; if (expr.CallsMin(S.IndexBracks, 1) || expr.CallsMin(S.NullIndexBracks, 1)) { // Consider foo[i]. We cannot always store `foo` in a temporary, as // this may change the code's behavior in case `foo` is a struct. i = 1; } for (; i < args.Count; i++) { if (!args[i].IsLiteral && !args[i].Attrs.Contains(_trivia_isTmpVar)) { LNode tmpVarName; stmtSequence.Add(TempVarDecl(Context, args[i], out tmpVarName)); args[i] = tmpVarName.PlusAttr(_trivia_isTmpVar); } } return expr.WithArgs(args.ToVList()); } } }
public static LNode ContractsOnProperty(LNode prop, IMacroContext context) { LNode oldProp = prop; if (prop.ArgCount == 4) { LNode braces = prop[3]; var oldBraces = braces; var rw = new CodeContractRewriter(prop.Args[0], prop.Args[1], context); prop = ProcessArgContractAttributes(prop, 2, rw); VList <LNode> cAttrs = LNode.List(); prop = prop.WithArgChanged(0, GrabContractAttrs(prop.Args[0], ref cAttrs, ContractAppliesTo.Getter)); prop = GrabContractAttrs(prop, ref cAttrs); LNode getter = null, setter = null; int getterIndex = -1, setterIndex = -1; VList <LNode> getterAttrs = LNode.List(), setterAttrs = LNode.List(); bool isLambdaProperty = !braces.Calls(S.Braces); if (isLambdaProperty) { if (cAttrs.Count == 0) { return(null); } getterAttrs = cAttrs; getter = LNode.Call(CodeSymbols.get, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(braces)))).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special); braces = LNode.Call(CodeSymbols.Braces, LNode.List(getter)).SetStyle(NodeStyle.Statement); getterIndex = 0; } else { for (int i = 0; i < braces.Args.Count; i++) { var part = braces.Args[i]; if (part.Calls(S.get)) { getter = part; getterIndex = i; } if (part.Calls(S.set)) { setter = part; setterIndex = i; } } if (cAttrs.Count != 0) { getterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Getter) != 0); setterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Setter) != 0); } } var sharedPrependStmts = rw.PrependStmts; if (getter != null) { getter = GrabContractAttrs(getter, ref getterAttrs); rw.Process(getterAttrs, null); rw.PrependStmtsToGetterOrSetter(ref braces, getterIndex, getter); } if (setter != null) { rw.PrependStmts = sharedPrependStmts; setter = GrabContractAttrs(setter, ref setterAttrs); rw.Process(setterAttrs, LNode.Id(CodeSymbols.value), true); rw.PrependStmtsToGetterOrSetter(ref braces, setterIndex, setter); } if (braces == oldBraces) { return(null); } else { return(prop.WithArgChanged(3, braces)); } } return(null); }
private static bool DetectSetOrCreateMember(LNode arg, out Symbol relevantAttribute, out Symbol fieldName, out Symbol paramName, out LNode newArg, out LNode propOrFieldDecl) { relevantAttribute = null; fieldName = null; paramName = null; newArg = null; propOrFieldDecl = null; LNode _, type, name, defaultValue, propArgs; if (EcsValidators.IsPropertyDefinition(arg, out type, out name, out propArgs, out _, out defaultValue) && propArgs.ArgCount == 0) { // #property(Type, Name<T>, {...}) relevantAttribute = S.Property; fieldName = EcsNodePrinter.KeyNameComponentOf(name); paramName = ChooseArgName(fieldName); if (defaultValue != null) // initializer is Args[4] { newArg = LNode.Call(S.Var, LNode.List(type, F.Assign(paramName, defaultValue)), arg); propOrFieldDecl = arg.WithArgs(arg.Args.Initial(4)); } else { newArg = LNode.Call(S.Var, LNode.List(type, F.Id(paramName)), arg); propOrFieldDecl = arg; } DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl); return(true); } else if (IsVar(arg, out type, out paramName, out defaultValue)) { int a_i = 0; foreach (var attr in arg.Attrs) { if (attr.IsId) { var a = attr.Name; if (a == _set || FieldCreationAttributes.Contains(a)) { relevantAttribute = a; fieldName = paramName; paramName = ChooseArgName(fieldName); if (a == _set) { newArg = F.Var(type, paramName, defaultValue).WithAttrs(arg.Attrs.Without(attr)); } else { // in case of something like "[A] public params T arg = value", // assume that "= value" represents a default value, not a field // initializer. Most attributes stay on the argument. newArg = arg.WithArgChanged(1, defaultValue != null ? F.Assign(paramName, defaultValue) : F.Id(paramName)); propOrFieldDecl = LNode.Call(S.Var, LNode.List(type, F.Id(fieldName)), arg); DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl); } break; } } a_i++; } return(newArg != null); } return(false); }
private static bool DetectSetOrCreateMember(LNode arg, out Symbol relevantAttribute, out Symbol fieldName, out Symbol paramName, out LNode newArg, out LNode propOrFieldDecl) { relevantAttribute = null; fieldName = null; paramName = null; newArg = null; propOrFieldDecl = null; LNode _, type, name, defaultValue, propArgs; if (EcsValidators.IsPropertyDefinition(arg, out type, out name, out propArgs, out _, out defaultValue) && propArgs.ArgCount == 0) { // #property(Type, Name<T>, {...}) relevantAttribute = S.Property; fieldName = EcsNodePrinter.KeyNameComponentOf(name); paramName = ChooseArgName(fieldName); if (defaultValue != null) { // initializer is Args[4] newArg = LNode.Call(S.Var, LNode.List(type, F.Assign(paramName, defaultValue)), arg); propOrFieldDecl = arg.WithArgs(arg.Args.First(4)); } else { newArg = LNode.Call(S.Var, LNode.List(type, F.Id(paramName)), arg); propOrFieldDecl = arg; } DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl); return true; } else if (IsVar(arg, out type, out paramName, out defaultValue)) { int a_i = 0; foreach (var attr in arg.Attrs) { if (attr.IsId) { var a = attr.Name; if (a == _set || FieldCreationAttributes.Contains(a)) { relevantAttribute = a; fieldName = paramName; paramName = ChooseArgName(fieldName); if (a == _set) { newArg = F.Var(type, paramName, defaultValue).WithAttrs(arg.Attrs.Without(attr)); } else { // in case of something like "[A] public params T arg = value", // assume that "= value" represents a default value, not a field // initializer. Most attributes stay on the argument. newArg = arg.WithArgChanged(1, defaultValue != null ? F.Assign(paramName, defaultValue) : F.Id(paramName)); propOrFieldDecl = LNode.Call(S.Var, LNode.List(type, F.Id(fieldName)), arg); DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl); } break; } } a_i++; } return newArg != null; } return false; }
protected override LNode DoneAttaching(LNode node, LNode parent, int indexInParent) { // Constructors are funky in EC# because EC# generalizes constructors so // you can write, for example, `this() { F(); base(); G(); }`. // Plain C# constructors like `this() : base() { G(); }`, `base(x)` // are actually stored like `this() { base(); G(); }`, where the colon // is the beginning of the Range of the constructor body and the opening // brace is the range of the Target of the method body. This makes it // difficult to get the trivia attached the way we want. For example, this // constructor: // // Foo(int x) // : base(x) // { // Bar(); // } // // Gets trivia attached like this: // // #cons(@``, Foo, #(int x), [#trivia_newline] ([#trivia_newline] @`'{}`)( // [#trivia_appendStatement] base(x), Bar())); // // This code changes the trivia to something more reasonable: // // #cons(@``, Foo, #(int x), [#trivia_newline] { base(x), Bar() }); // LNode baseCall; if (node.CallsMin(S.Braces, 1) && parent != null && parent.Calls(S.Constructor) && (baseCall = node.Args[0]).Range.StartIndex < node.Target.Range.StartIndex) { if (RemoveLeadingNewline(ref node) != null) node = node.WithArgChanged(0, baseCall.WithoutAttrNamed(S.TriviaAppendStatement)); LNode target = node.Target, newline_trivia; if ((newline_trivia = RemoveLeadingNewline(ref target)) != null) { node = node.WithTarget(target).PlusAttrBefore(newline_trivia); } } return node; }
public static LNode of(LNode node, IMessageSink sink) { LNode kind; if (node.ArgCount == 2 && (kind = node.Args[0]).IsId) { if (kind.IsIdNamed(_array)) return node.WithArgChanged(0, kind.WithName(S.Array)); if (kind.IsIdNamed(_opt)) return node.WithArgChanged(0, kind.WithName(S.QuestionMark)); if (kind.IsIdNamed(_ptr)) return node.WithArgChanged(0, kind.WithName(S._Pointer)); } else if (node.ArgCount == 3 && (kind = node.Args[0]).IsIdNamed(_array) && node.Args[1].IsLiteral) { return node.WithArgs(kind.WithName(S.GetArrayKeyword((int)node.Args[1].Value)), node.Args[2]); } return null; }
public static LNode priorityTestHi(LNode node, IMessageSink sink) { if (node.ArgCount >= 1 && !node[0].IsIdNamed("hi")) return node.WithArgChanged(0, LNode.Id("hi")); return null; }
// This method's main goal is to move #runSequence from child nodes to outer nodes: // Foo(a, #runSequence(b(), c())) => #runSequence(var a_10 = a; b(); Foo(a_10, c())); // It also converts variable declarations, e.g. // Foo()::foo => #runSequence(var foo = Foo(), foo) LNode BubbleUpBlocks(LNode expr, bool stmtContext = false) { if (!expr.IsCall) return expr; LNode result = null; if (!stmtContext) { { LNode tmp_14, value, varName, varType; VList<LNode> attrs; if (expr.Calls(CodeSymbols.Braces)) { Context.Sink.Warning(expr, "A braced block is not supported directly within an expression. Did you mean to use `#runSequence {...}`?"); result = expr; } else if ((attrs = expr.Attrs).IsEmpty | true && attrs.NodeNamed(S.Out) != null && expr.Calls(CodeSymbols.Var, 2) && (varType = expr.Args[0]) != null && (varName = expr.Args[1]) != null && varName.IsId) { if (varType.IsIdNamed(S.Missing)) Context.Sink.Error(expr, "#useSequenceExpressions: the data type of this variable declaration cannot be inferred and must be stated explicitly."); result = LNode.Call(LNode.List(_trivia_pure), (Symbol) "#runSequence", LNode.List(expr.WithoutAttrNamed(S.Out), varName.PlusAttrs(LNode.List(LNode.Id(CodeSymbols.Out))))); } else if ((attrs = expr.Attrs).IsEmpty | true && expr.Calls(CodeSymbols.Var, 2) && (varType = expr.Args[0]) != null && (tmp_14 = expr.Args[1]) != null && tmp_14.Calls(CodeSymbols.Assign, 2) && (varName = tmp_14.Args[0]) != null && (value = tmp_14.Args[1]) != null) if (stmtContext) result = expr; // no-op else result = ConvertVarDeclToRunSequence(attrs, varType, varName, value); } } if (result == null) { { LNode args, code, value, varName; VList<LNode> attrs; if ((attrs = expr.Attrs).IsEmpty | true && expr.Calls(CodeSymbols.ColonColon, 2) && (value = expr.Args[0]) != null && IsQuickBindLhs(value) && (varName = expr.Args[1]) != null && varName.IsId) result = ConvertVarDeclToRunSequence(attrs, F.Missing, varName, value); else if ((attrs = expr.Attrs).IsEmpty | true && expr.Calls(CodeSymbols.Lambda, 2) && (args = expr.Args[0]) != null && (code = expr.Args[1]) != null) result = expr.WithArgChanged(1, EliminateSequenceExpressionsInLambdaExpr(code, F.Missing)); else if (expr.Calls(__numrunSequence)) result = expr; else result = BubbleUp_GeneralCall(expr); } } // #runSequences can be nested by the user or produced by BubbleUp_GeneralCall, // so process the code inside #runSequence too if (result.Calls(__numrunSequence)) return result.WithArgs(EliminateSequenceExpressions(result.Args, false)); else return result; }