public static LNode IfUnless(LNode node, bool isUnless, IMessageSink sink) { var args = node.Args; LNode cond = args.TryGet(0, null), then = args.TryGet(1, null), elseKW = args.TryGet(2, null), @else = args.TryGet(3, null); if (cond == null) { return(null); } if (then == null) { return(Reject(sink, cond, "'{0}' statement ended early", isUnless ? "unless" : "if")); } if (isUnless) { cond = F.Call(S.Not, cond); } if (elseKW == null) { return(node.With(S.If, cond, then)); } if (!elseKW.IsIdNamed(_else)) { return(Reject(sink, elseKW, "'{0}': expected else clause or end-of-statement marker", isUnless ? "unless" : "if")); } if (@else.IsId && args.Count > 4) { @else = LNode.Call(@else.Name, new VList <LNode>(args.Slice(4)), node); } return(node.With(S.If, cond, then, @else)); }
public static LNode @defaultCase(LNode node, IMacroContext context) { if (node.ArgCount == 0) { return(node.With(S.Label, LNode.Id(S.Default, node)).SetBaseStyle(NodeStyle.Default)); } else if (node.ArgCount == 1) { var arg = node.Args[0]; if (arg.Calls(S.Colon, 1) // .default: {...} ) { arg = arg.Args[0]; } else if (!arg.Calls(S.Braces) // expecting .default {...} ) { return(null); } return(F.Call(S.Splice, node.With(S.Label, LNode.Id(S.Default, node)).SetBaseStyle(NodeStyle.Default), arg)); } return(null); }
public static LNode on_finally(LNode node, IMacroContext context) { LNode firstArg, rest, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) return null; return node.With(S.Try, rest, node.With(S.Finally, on_handler)); }
private static LNode TransformOnCatch(LNode node, LNode firstArg, LNode rest, LNode on_handler) { if (on_handler == null) return null; if (firstArg == null) firstArg = LNode.Missing; else if (firstArg.IsId) firstArg = firstArg.With(S.Var, F.Id(_Exception), firstArg); return node.With(S.Try, rest, node.With(S.Catch, firstArg, F.Missing, on_handler)); }
public static LNode on_finally(LNode node, IMacroContext context) { VList<LNode> rest; LNode firstArg, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) return null; node.Style &= ~NodeStyle.OneLiner; // avoid collapsing output to one line return node.With(S.Try, F.Braces(rest), node.With(S.Finally, on_handler)); }
public static LNode on_finally(LNode node, IMacroContext context) { LNode firstArg, rest, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) { return(null); } return(node.With(S.Try, rest, node.With(S.Finally, on_handler))); }
private static LNode TransformOnCatch(LNode node, LNode firstArg, LNode rest, LNode on_handler) { if (on_handler == null) return null; if (firstArg == null) firstArg = LNode.Missing; else if (firstArg.IsId) firstArg = firstArg.With(S.Var, F.Id(_Exception), firstArg); node.Style &= ~NodeStyle.OneLiner; // avoid collapsing output to one line return node.With(S.Try, rest, node.With(S.Catch, firstArg, F.Missing, on_handler)); }
public static LNode @default1(LNode node, IMessageSink sink) { if (node.IsId) { return(node.With(S.Label, F.Id(S.Default))); } else if (node.ArgCount == 1 && node.Args[0].Calls(S.Braces)) { return(F.Call(S.Splice, new VList <LNode>(node.With(S.Label, new VList <LNode>(F.Id(S.Default))), node.Args[0]))); } return(null); }
public static LNode on_finally(LNode node, IMacroContext context) { VList <LNode> rest; LNode firstArg, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) { return(null); } node.Style &= ~NodeStyle.OneLiner; // avoid collapsing output to one line return(node.With(S.Try, F.Braces(rest), node.With(S.Finally, on_handler))); }
public static LNode @do(LNode node, IMessageSink sink) { var args = node.Args; if (node.ArgCount == 2 && args.Last.Calls(_while, 1)) { return(node.With(S.DoWhile, new VList <LNode>(node.Args[0], node.Args[1].Args[0]))); } else if (node.ArgCount == 3 && args.TryGet(1, null).IsIdNamed(_while)) { return(node.With(S.DoWhile, new VList <LNode>(node.Args[0], node.Args[2]))); } return(null); }
public static LNode @prop(LNode node, IMessageSink sink) { var parts = node.Args; LNode sig = parts.TryGet(0, null), body = parts.TryGet(1, null), name, retVal = null; if (parts.Count != 2 || !body.Calls(S.Braces)) { return(Reject(sink, node, "A property definition must have the form prop(Name, { Body }), or prop(Name::type, { Body })")); } if (sig.Calls(S._RightArrow, 2) || sig.Calls(S.ColonColon, 2)) { name = sig.Args[0]; retVal = sig.Args[1]; } else { name = sig; retVal = F.Missing; } if (!IsComplexId(name)) { return(Reject(sink, name, "Property name must be a complex identifier")); } return(node.With(S.Property, retVal, name, F.Missing, body)); }
public static LNode _set(LNode node, IMacroContext context) { var lhs = node.Args[0, LNode.Missing]; var name = lhs.Name; bool isSnippet = name == _hash_snippet; if ((isSnippet || name == _hash_set) && node.ArgCount == 2 && lhs.IsId) { Symbol newTarget = isSnippet ? _hash_setScopedPropertyQuote : _hash_setScopedProperty; var stmts = node.Args.Slice(1).Select(key => { LNode value = F.@true; if (key.Calls(S.Assign, 2)) { value = key.Args[1]; value = context.PreProcess(value); key = key.Args[0]; if (isSnippet && value.Calls(S.Braces)) { value = value.Args.AsLNode(S.Splice); } } if (!key.IsId) { context.Write(Severity.Error, key, "Invalid key; expected an identifier."); } return((LNode)node.With(newTarget, LNode.Literal(key.Name, key), value)); }); return(F.Call(S.Splice, stmts)); } return(null); }
private static LNode TransformOnCatch(LNode node, LNode firstArg, LNode rest, LNode on_handler) { if (on_handler == null) { return(null); } if (firstArg == null) { firstArg = LNode.Missing; } else if (firstArg.IsId) { firstArg = firstArg.With(S.Var, F.Id(_Exception), firstArg); } return(node.With(S.Try, rest, node.With(S.Catch, firstArg, F.Missing, on_handler))); }
public static LNode _set(LNode node, IMacroContext context) { var lhs = node.Args[0, LNode.Missing]; var name = lhs.Name; bool isSnippet = name == _hash_snippet; if ((isSnippet || name == _hash_set) && node.ArgCount == 2 && lhs.IsId) { node = context.PreProcessChildren(); Symbol newTarget = isSnippet ? _hash_setScopedPropertyQuote : _hash_setScopedProperty; var stmts = node.Args.Slice(1).Select(key => { LNode value = F.@true; if (key.Calls(S.Assign, 2)) { value = key.Args[1]; key = key.Args[0]; if (isSnippet && value.Calls(S.Braces)) value = value.Args.AsLNode(S.Splice); } if (!key.IsId) context.Write(Severity.Error, key, "Invalid key; expected an identifier."); return node.With(newTarget, LNode.Literal(key.Name, key), value); }); return F.Call(S.Splice, stmts); } return null; }
public sealed override LNode Select(Func <LNode, LNode> selector) { LNode result = WithAttrs(n => Maybe.Value(selector(n))); LNode target = selector(Target); var args = Args.SmartSelect(selector); return(result.With(target, args)); }
public static LNode runSequence(LNode node, IMacroContext context) { if (context.Parent.Calls(S.Braces)) return node.With(S.Splice, MaybeRemoveNoOpFromRunSeq(node.Args)); if (!context.ScopedProperties.ContainsKey(_useSequenceExpressionsIsRunning)) Reject(context, node, "#useSequenceExpressions is required to make #runSequence work"); return null; }
public static LNode Do(LNode node, IMacroContext context) { if (node.ArgCount == 2 && node.Args[1].Calls(S.While, 1)) { return(node.With(S.DoWhile, node[0], node[1].Args[0])); } return(null); }
public static LNode @throw(LNode node, IMessageSink sink) { if (node.ArgCount > 1) { return(null); } return(node.With(S.Throw, node.Args)); // change throw -> #throw() and throw(x) -> #throw(x) }
public static LNode QuestionMark(LNode node, IMessageSink sink) { if (node.ArgCount == 2 && node.Args[1].Calls(S.Colon, 2)) { return(node.With(S.QuestionMark, node.Args[0], node.Args[1].Args[0], node.Args[1].Args[1])); } return(null); }
public static LNode GotoCase(LNode node, IMessageSink sink) { if (node.ArgCount == 2 && node.Args[0].IsIdNamed(_case)) { return(node.With(S.GotoCase, node.Args[1])); } return(null); }
LNode EliminateSequenceExpressionsInChildStmt(LNode stmt) { stmt = EliminateSequenceExpressionsInExecStmt(stmt); if (stmt.Calls(__numrunSequence)) { return(stmt.With(S.Braces, MaybeRemoveNoOpFromRunSeq(stmt.Args))); } return(stmt); }
private static LNode TransformOnCatch(LNode node, LNode firstArg, LNode rest, LNode on_handler) { if (on_handler == null) { return(null); } if (firstArg == null) { firstArg = LNode.Missing; } else if (firstArg.IsId) { firstArg = firstArg.With(S.Var, F.Id(_Exception), firstArg); } node.Style &= ~NodeStyle.OneLiner; // avoid collapsing output to one line return(node.With(S.Try, rest, node.With(S.Catch, firstArg, F.Missing, on_handler))); }
public static LNode QuickBind(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { return(node.With(S.Var, new VList <LNode>(F.Missing, F.Call(S.Assign, a[1], a[0])))); } return(null); }
public static LNode ColonEquals(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { LNode name = a[0], value = a[1]; return(node.With(S.Var, F.Missing, F.Call(S.Assign, name, value))); } return(null); }
public static LNode ColonColon(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { var r = node.With(S.Var, a[1], a[0]); r.BaseStyle = NodeStyle.Operator; return(r); } return(null); }
public static LNode runSequence(LNode node, IMacroContext context) { if (context.Parent.Calls(S.Braces)) { return(node.With(S.Splice, MaybeRemoveNoOpFromRunSeq(node.Args))); } if (!context.ScopedProperties.ContainsKey(_useSequenceExpressionsIsRunning)) { Reject(context, node, "#useSequenceExpressions is required to make #runSequence work"); } return(null); }
public static LNode ColonColonInit(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { LNode name = a[0], value = a[1]; if (name.Calls(S.ColonColon, 2)) { return(node.With(S.Var, name.Args[1], F.Call(S.Assign, name.Args[0], value))); } } return(null); }
public static LNode @for(LNode node, IMessageSink sink) { LNode tuple; if (node.ArgCount == 2 && (tuple = node.Args[0]).Calls(S.Tuple, 3)) { return(node.With(S.For, tuple.Args[0], tuple.Args[1], tuple.Args[2], node.Args[1])); } else if (node.ArgCount == 4) { return(node.WithTarget(S.For)); } return(null); }
public static LNode @foreach(LNode node, IMessageSink sink) { var args = node.Args; if (args.Count == 2 && args[0].Calls(_in, 2)) { LNode decl = args[0].Args[0], list = args[0].Args[1], body = args[1]; if (decl.IsId) { decl = F.Var(F.Missing, decl); } return(node.With(S.ForEach, decl, list, body)); } return(null); }
public static LNode rule(LNode node, IMacroContext context) { bool isToken; if ((isToken = node.Calls(_token, 2)) || node.Calls(_rule, 2)) { node = context.PreProcessChildren(); LNode sig = node.Args[0]; // Ugh. Because the rule has been macro-processed, "rule X::Y ..." // has become "rule #var(Y,X) ...". We must allow this, because in // case of something like "rule X(arg::int)::Y" we actually do want // the argument to become `#var(int, arg)`; so just reverse the // transform that we didn't want. if (sig.Calls(S.Var, 2)) { sig = F.Call(S.ColonColon, sig.Args[1], sig.Args[0]); } LNode name = sig, returnType = F.Void; if (sig.Calls(S.ColonColon, 2)) { returnType = sig.Args[1]; name = sig.Args[0]; } if (EcsValidators.IsComplexIdentifier(name)) { name = F.Call(name); // def requires an argument list } LNodeList args = name.Args; name = name.Target; LNode newBody = ParseRuleBody(node.Args[1], context); if (newBody != null) { return(node.With(isToken ? _hash_token : _hash_rule, returnType, name, F.AltList(args), newBody)); } } return(null); }
private static LNode ParseRuleBody(LNode ruleBody, IMessageSink sink) { TokenTree ruleTokens; if ((ruleTokens = ruleBody.Value as TokenTree) == null && !ruleBody.Calls(S.Braces)) { return(null); } if (ruleTokens != null) { return(StageOneParser.ParseTokenTree(ruleTokens, sink, ruleBody)); } else { if (ruleBody.Args.Any(stmt => stmt.Value is TokenTree)) { ruleBody = ruleBody.With(S.Tuple, ruleBody.Args.SmartSelect(stmt => ParseStmtInRule(stmt, sink))); } } return(ruleBody); }
public static LNode rule(LNode node, IMacroContext context) { bool isToken; if ((isToken = node.Calls(_token, 2)) || node.Calls(_rule, 2)) { node = context.PreProcessChildren(); LNode sig = node.Args[0]; // Ugh. Because the rule has been macro-processed, "rule X::Y ..." // has become "rule #var(Y,X) ...". Reverse this transform. if (sig.Calls(S.Var, 2)) { sig = F.Call(S.ColonColon, sig.Args[1], sig.Args[0]); } LNode name = sig, returnType = F.Void; if (sig.Calls(S.ColonColon, 2)) { returnType = sig.Args[1]; name = sig.Args[0]; } if (LeMP.Prelude.Les.Macros.IsComplexId(name)) { name = F.Call(name); // def requires an argument list } RVList <LNode> args = name.Args; name = name.Target; LNode newBody = ParseRuleBody(node.Args[1], context); if (newBody != null) { return(node.With(isToken ? _hash_token : _hash_rule, returnType, name, F.List(args), newBody)); } } return(null); }
private static LNode ParseRuleBody(LNode ruleBody, IMessageSink sink) { TokenTree ruleTokens; // Expecting @{...} or {...} if ((ruleTokens = ruleBody.Value as TokenTree) == null && !ruleBody.Calls(S.Braces)) { sink.Error(ruleBody, "Expected token tree or braced block"); return(null); } if (ruleTokens != null) { return(StageOneParser.ParseTokenTree(ruleTokens, sink)); } else { if (ruleBody.Args.Any(stmt => stmt.Value is TokenTree)) { ruleBody = ruleBody.With(S.Tuple, ruleBody.Args.SmartSelect(stmt => ParseStmtInRule(stmt, sink))); } } return(ruleBody); }
public static LNode VarDecl(LNode node, IMacroContext context) { var a = node.Args; if (a.Count == 2) { LNode name = a[0], type = a[1], nameAssignment = name; if (type.Calls(S.Assign, 2)) { nameAssignment = type.WithArgs(name, type[1]); type = type[0]; } if (name.IsId) { return(node.With(S.Var, type, nameAssignment).SetBaseStyle(NodeStyle.Default)); } else { context.Write(Severity.Note, node, "Unrecognized variable declaration syntax"); return(null); } } return(null); }
public static LNode IfUnless(LNode node, bool isUnless, IMessageSink sink) { var args = node.Args; LNode cond = args.TryGet(0, null), then = args.TryGet(1, null), elseKW = args.TryGet(2, null), @else = args.TryGet(3, null); if (cond == null) return null; if (then == null) return Reject(sink, cond, "'{0}' statement ended early", isUnless ? "unless" : "if"); if (isUnless) cond = F.Call(S.Not, cond); if (elseKW == null) return node.With(S.If, cond, then); if (!elseKW.IsIdNamed(_else)) return Reject(sink, elseKW, "'{0}': expected else clause or end-of-statement marker", isUnless ? "unless" : "if"); if (@else.IsId && args.Count > 4) @else = LNode.Call(@else.Name, new RVList<LNode>(args.Slice(4)), node); return node.With(S.If, cond, then, @else); }
public static LNode unroll(LNode var, LNode cases, LNode body, IMessageSink sink) { if (!cases.Calls(S.Tuple) && !cases.Calls(S.Braces)) return Reject(sink, cases, "unroll: the right-hand side of 'in' should be a tuple"); // Maps identifiers => replacements. The integer counts how many times replacement occurred. var replacements = InternalList<Triplet<Symbol, LNode, int>>.Empty; if (var.IsId && !var.HasPAttrs()) { replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0)); } else { var vars = var.Args; if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs())) { replacements = new Triplet<Symbol, LNode, int>[vars.Count].AsInternalList(); for (int i = 0; i < vars.Count; i++) { replacements.InternalArray[i].A = vars[i].Name; // Check for duplicate names for (int j = 0; j < i; j++) if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_") sink.Write(Severity.Error, vars[i], "unroll: duplicate name in the left-hand tuple"); // non-fatal } } else return Reject(sink, cases, "unroll: the left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers."); } UnrollCtx ctx = new UnrollCtx { Replacements = replacements }; WList<LNode> output = new WList<LNode>(); int iteration = 0; foreach (LNode replacement in cases.Args) { iteration++; bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces); int count = tuple ? replacement.ArgCount : 1; if (replacements.Count != count) { sink.Write(Severity.Error, replacement, "unroll, iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count); if (count < replacements.Count) continue; // too few } for (int i = 0; i < replacements.Count; i++) replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement; if (body.Calls(S.Braces)) { foreach (LNode stmt in body.Args) output.Add(ctx.Replace(stmt).Value); } else output.Add(ctx.Replace(body).Value); } foreach (var r in replacements) if (r.C == 0 && !r.A.Name.StartsWith("_")) sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A); return body.With(S.Splice, output.ToVList()); }
LNode EliminateSequenceExpressionsInChildStmt(LNode stmt) { stmt = EliminateSequenceExpressionsInExecStmt(stmt); if (stmt.Calls(__numrunSequence)) return stmt.With(S.Braces, MaybeRemoveNoOpFromRunSeq(stmt.Args)); return stmt; }
public static LNode QuickBind(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) return node.With(S.Var, new RVList<LNode>(F._Missing, F.Call(S.Assign, a[1], a[0]))); return null; }
public static LNode ColonColonInit(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { LNode name = a[0], value = a[1]; if (name.Calls(S.ColonColon, 2)) return node.With(S.Var, name.Args[1], F.Call(S.Assign, name.Args[0], value)); } return null; }
public static LNode QuestionMark(LNode node, IMessageSink sink) { if (node.ArgCount == 2 && node.Args[1].Calls(S.Colon, 2)) return node.With(S.QuestionMark, node.Args[0], node.Args[1].Args[0], node.Args[1].Args[1]); return null; }
public static LNode @try(LNode node, IMessageSink sink) { if (!node.IsCall) return null; // try(code, catch, Exception::e, handler, catch, ..., finally, handler) // ...becomes... // #try(#{ stmt1; stmt2; ... }, #catch(#var(Exception, e), handler), #finally(handler)) LNode finallyCode = null; RWList<LNode> clauses = new RWList<LNode>(); var parts = node.Args; for (int i = parts.Count-2; i >= 1; i -= 2) { var p = parts[i]; if (p.IsIdNamed(_finally)) { if (clauses.Count != 0 || finallyCode != null) sink.Write(Severity.Error, p, "The «finally» clause must come last, there can only be one of them."); finallyCode = parts[i+1]; } else if (p.Name == _catch) { if (p.ArgCount > 0) { // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, F.Call(S.Splice, p.Args), parts[i + 1])); } else { // This is a catch-all clause (the type argument is missing) if (clauses.Count != 0) sink.Write(Severity.Error, p, "The catch-all clause must be the last «catch» clause."); clauses.Add(F.Call(S.Catch, F._Missing, parts[i + 1])); } } else if (i > 1 && parts[i-1].IsIdNamed(_catch)) { // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, AutoRemoveParens(p), parts[i+1])); i--; } else { return Reject(sink, p, "Expected «catch» or «finally» clause here. Clause is missing or malformed."); } if (i == 2) return Reject(sink, parts[1], "Expected «catch» or «finally» clause here. Clause is missing or malformed."); } if (clauses.Count == 0 && finallyCode == null) { Debug.Assert(node.ArgCount <= 1); return Reject(sink, node, "Missing «catch, Type, Code» or «finally, Code» clause"); } if (finallyCode != null) clauses.Add(F.Call(S.Finally, finallyCode)); clauses.Insert(0, node.Args[0]); return node.With(S.Try, clauses.ToRVList()); }
static LNode DefOrConstructor(LNode node, IMessageSink sink, bool isCons) { var parts = node.Args; LNode sig = parts.TryGet(0, null), body = parts.TryGet(1, null); if (!parts.Count.IsInRange(1, 2) || !sig.IsCall || (body != null && !body.Calls(S.Braces))) return null; LNode forwardTo = null, retVal = null; if (sig.Calls(S.Forward, 2)) { forwardTo = sig.Args[1]; sig = sig.Args[0]; if (body != null) return Reject(sink, sig.Target, "Cannot use ==> and a method body {...} at the same time."); } if (sig.Calls(S._RightArrow, 2) || sig.Calls(S.ColonColon, 2)) { retVal = sig.Args[1]; sig = sig.Args[0]; } if (retVal != null && retVal.Calls(S.Braces) && body == null) { body = retVal; retVal = F._Missing; } var name = sig.Target ?? sig; if (!IsTargetDefinitionId(sig, true)) return Reject(sink, sig.Target, "Invalid method name"); var argList = sig.ArgCount != 0 ? sig.WithTarget(S.AltList) : F.List(); if (retVal == null) retVal = isCons ? F._Missing : F.Void; else if (isCons) return Reject(sink, retVal, "A constructor cannot have a return type"); Symbol kind = isCons ? S.Cons : S.Fn; if (body != null) return node.With(kind, retVal, name, argList, body); else if (forwardTo != null) return node.With(kind, retVal, name, argList, F.Call(S.Forward, forwardTo)); else return node.With(kind, retVal, name, argList); }
public static LNode TranslateSpaceDefinition(LNode node, IMacroContext context, Symbol newTarget) { if (!node.IsCall) return null; bool isAlias = newTarget == S.Alias, isNamespace = newTarget == S.Namespace; var args = node.Args; LNode nameEtc = args.TryGet(0, null), body = args.TryGet(1, null), oldName = null; if (args.Count == 1 ? !isAlias : (args.Count != 2 || !body.Calls(S.Braces))) { if (isNamespace && args.Count == 1) { // Special case: a namespace can auto-wrap whatever statements follow. body = F.Braces(context.RemainingNodes); context.DropRemainingNodes = true; } else return Reject(context, node, "A type definition must have the form kind(Name, { Body }) or kind(Name(Bases), { Body }) (where «kind» is struct/class/enum/trait/alias)"); } if (isAlias) { if (!nameEtc.Calls(S.Assign, 2)) return Reject(context, node, "An 'alias' (or 'using') definition must have the form alias(NewName = OldName, { Body }) or alias(NewName(Interfaces) = OldName, { Body })"); oldName = nameEtc.Args[1]; nameEtc = nameEtc.Args[0]; } LNode name, bases; if (IsComplexId(nameEtc, true)) { name = nameEtc; bases = F.List(); } else { name = nameEtc.Target ?? nameEtc; bases = nameEtc.WithTarget(S.AltList); } if (isNamespace) { if (!IsComplexId(name, true)) return Reject(context, name, "Invalid namespace name (expected a complex identifier)"); } else { if (!IsDefinitionId(name, false)) return Reject(context, name, "Invalid type name (expected a simple name or Name!(T1,T2,...))"); } if (isAlias) { if (body == null) return node.With(newTarget, F.Call(S.Assign, name, oldName), bases); else return node.With(newTarget, F.Call(S.Assign, name, oldName), bases, body); } else { Debug.Assert(body != null); return node.With(newTarget, name, bases, body); } }
public static LNode import_macros(LNode node, IMacroContext sink) { return node.With(_importMacros, node.Args); }
public static LNode @for(LNode node, IMessageSink sink) { LNode tuple; if (node.ArgCount == 2 && ((tuple = node.Args[0]).Calls(S.Tuple, 3) || tuple.Calls(S.Tuple, 2))) return node.With(S.For, asAltList(tuple.Args[0]), tuple.Args[1], asAltList(tuple.Args[2, LNode.Missing]), node.Args[1]); else if (node.ArgCount == 4) return node.With(S.For, asAltList(node.Args[0]), node.Args[1], asAltList(node.Args[2]), node.Args[3]); return null; }
public static LNode ColonColon(LNode node, IMessageSink context) { var a = node.Args; if (a.Count == 2) { if (a[0].IsId) { var r = node.With(S.Var, a[1], a[0]); r.BaseStyle = NodeStyle.Operator; return r; } else if (a[0].CallsMin(S.Tuple, 1)) { var r = node.With(S.Var, new VList<LNode>(a[1]).AddRange(a[0].Args)); r.BaseStyle = NodeStyle.Operator; return r; } else return Reject(context, node, "Expected a variable name or tuple to the left of `::`"); } return null; }
public static LNode @default1(LNode node, IMessageSink sink) { if (node.IsId) return node.With(S.Label, F.Id(S.Default)); else if (node.ArgCount == 1 && node.Args[0].Calls(S.Braces)) return F.Call(S.Splice, new RVList<LNode>(node.With(S.Label, new RVList<LNode>(F.Id(S.Default))), node.Args[0])); return null; }
public static LNode @prop(LNode node, IMessageSink sink) { var parts = node.Args; LNode sig = parts.TryGet(0, null), body = parts.TryGet(1, null), name, retVal = null; if (parts.Count != 2 || !body.Calls(S.Braces)) return Reject(sink, node, "A property definition must have the form prop(Name, { Body }), or prop(Name::type, { Body })"); if (sig.Calls(S._RightArrow, 2) || sig.Calls(S.ColonColon, 2)) { name = sig.Args[0]; retVal = sig.Args[1]; } else { name = sig; retVal = F._Missing; } if (!IsComplexId(name)) return Reject(sink, name, "Property name must be a complex identifier"); return node.With(S.Property, retVal, name, body); }
public static LNode GotoCase(LNode node, IMessageSink sink) { if (node.ArgCount == 2 && node.Args[0].IsIdNamed(_case)) return node.With(S.GotoCase, node.Args[1]); return null; }
public static LNode @var(LNode node, IMessageSink sink) { var parts = node.Args; if (parts.Count == 0) return Reject(sink, node, "A variable definition must have the form var(Name::Type), var(Name = value), or var(Name::Type = value)"); if (parts[0].IsId) return null; // e.g. this is true for "static readonly x::Foo" RWList<LNode> varStmts = null; LNode varStmt = null; for (int i = 0; i < parts.Count; i++) { LNode part = parts[i], type = null, init = null; if (part.Calls(S.Assign, 2)) { init = part.Args[1]; part = part.Args[0]; } if (part.Calls(S.ColonColon, 2)) { type = part.Args[1]; part = part.Args[0]; } if (init == null && part.Calls(S.Assign, 2)) { init = part.Args[1]; part = part.Args[0]; } if (!part.IsId) return Reject(sink, part, "Expected a simple variable name here"); if (type != null && !IsComplexId(type)) return Reject(sink, type, "Expected a type name here"); type = type ?? F._Missing; var nameAndInit = init == null ? part : F.Call(S.Assign, part, init); if (varStmt != null && varStmt.Args[0].Equals(type)) { // same type used again, e.g. (var x::int y::int) => (#var int x y) varStmt = varStmt.WithArgs(varStmt.Args.Add(nameAndInit)); } else { // first item (var x::int => #var int x) or type changed (var a::A b::B => #var A a; #var B b) if (varStmt != null) { varStmts = varStmts ?? new RWList<LNode>(); varStmts.Add(varStmt); } varStmt = node.With(S.Var, type, nameAndInit); } } // Return a single statement or a list of them if necessary if (varStmts != null) { varStmts.Add(varStmt); return F.Call(S.Splice, varStmts.ToRVList()); } else { return varStmt; } }
public static LNode @throw(LNode node, IMessageSink sink) { if (node.ArgCount > 1) return null; return node.With(S.Throw, node.Args); // change throw -> #throw() and throw(x) -> #throw(x) }
public static LNode import_macros(LNode node, IMessageSink sink) { return node.With(_importMacros, node.Args); }
public static LNode ColonColon(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { var r = node.With(S.Var, a[1], a[0]); r.BaseStyle = NodeStyle.Operator; return r; } return null; }
public static LNode @for(LNode node, IMessageSink sink) { LNode tuple; if (node.ArgCount == 2 && (tuple = node.Args[0]).Calls(S.Tuple, 3)) return node.With(S.For, tuple.Args[0], tuple.Args[1], tuple.Args[2], node.Args[1]); else if (node.ArgCount == 4) return node.WithTarget(S.For); return null; }
public static LNode ColonEquals(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2) { LNode name = a[0], value = a[1]; return node.With(S.Var, F._Missing, F.Call(S.Assign, name, value)); } return null; }
public static LNode @foreach(LNode node, IMessageSink sink) { var args = node.Args; if (args.Count == 2 && args[0].Calls(_in, 2)) { LNode decl = args[0].Args[0], list = args[0].Args[1], body = args[1]; if (decl.IsId) decl = F.Var(F._Missing, decl); return node.With(S.ForEach, decl, list, body); } return null; }
private void InjectTriviaInChildren(LNode parent, out SourceRange triviaRange, out Maybe <Trivia> trivia, int indexInParent, ref LNode node) { // Current trivia's range is within node's range: Apply it to // the node's children, if any. First gather list of children // and sort by source-code order, if necessary: int min = node.Min; InternalList <Pair <LNode, int> > children = InternalList <Pair <LNode, int> > .Empty; children.Resize(node.Max - min + 1); bool inOrder = true; int start, prevStart = int.MinValue; for (int i = 0; i < children.Count; i++, prevStart = start) { children[i] = Pair.Create(node[i + min], i + min); start = children[i].A.Range.StartIndex; if (prevStart > start) { inOrder = false; } } if (!inOrder) { children.Sort((a, b) => a.Item1.Range.StartIndex.CompareTo(b.Item1.Range.StartIndex)); } // Call ourself recursively to apply trivia to children. Usually, // newChildren is the same length as children, but it may have extra // trivia attributes added. InternalList <Pair <LNode, int> > newChildren = new InternalList <Pair <LNode, int> >(children.Count); var output = RunCore(children.GetEnumerator(), node); bool changed = false; while (output.MoveNext()) { var @new = output.Current; newChildren.Add(@new); if (@new.Item1 != children[@new.Item2 - min].Item1) { changed = true; } } // At the end, gather up any remaining trivia InternalList <Trivia> triviaList = InternalList <Trivia> .Empty; trivia = CurrentTrivia(out triviaRange); while (trivia.HasValue && triviaRange.EndIndex <= node.Range.EndIndex) { triviaList.Add(trivia.Value); trivia = AdvanceTrivia(out triviaRange); changed = true; } if (changed) { // If this is a call, attach any remaining trivia to the last child. if (node.IsCall && !triviaList.IsEmpty) { int i = newChildren.Count - 1; var last = newChildren.InternalArray[i]; newChildren.InternalArray[i].A = AttachTriviaTo(last.A, triviaList, TriviaLocation.TrailingExtra, node, last.B) ?? last.A; } // Put the children back in their "conceptual" order if (!inOrder) { newChildren.StableSort((a, b) => a.Item2.CompareTo(b.Item2)); } // Update node's attributes, if any int numAttrs = newChildren.TakeWhile(p => p.B < -1).Count(); if (numAttrs > 0) { var attrs = LNode.List(newChildren.Slice(0, numAttrs).SelectArray(p => p.A)); node = node.WithAttrs(attrs); } if (node.IsCall) { Debug.Assert(newChildren[numAttrs].B == -1); var newArgs = newChildren.Slice(numAttrs + 1).SelectArray(p => p.A); node = node.With(newChildren[numAttrs].A, LNode.List(newArgs)); } else if (!triviaList.IsEmpty) { // If current node is not a call, attach any remaining trivia to it. node = AttachTriviaTo(node, triviaList, TriviaLocation.Ambiguous, parent, indexInParent); } } }
public static LNode @do(LNode node, IMessageSink sink) { var args = node.Args; if (node.ArgCount == 2 && args.Last.Calls(_while, 1)) { return node.With(S.DoWhile, new RVList<LNode>(node.Args[0], node.Args[1].Args[0])); } else if (node.ArgCount == 3 && args.TryGet(1, null).IsIdNamed(_while)) { return node.With(S.DoWhile, new RVList<LNode>(node.Args[0], node.Args[2])); } return null; }
public static LNode unroll(LNode var, VList <LNode> cases, LNode body, IMessageSink sink) { // Maps identifiers => replacements. The integer counts how many times replacement occurred. var replacements = InternalList <Triplet <Symbol, LNode, int> > .Empty; if (var.IsId && !var.HasPAttrs()) { replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0)); } else { var vars = var.Args; if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs())) { replacements = new Triplet <Symbol, LNode, int> [vars.Count].AsInternalList(); for (int i = 0; i < vars.Count; i++) { replacements.InternalArray[i].A = vars[i].Name; // Check for duplicate names for (int j = 0; j < i; j++) { if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_") { sink.Error(vars[i], "Duplicate name in the left-hand tuple"); // non-fatal } } } } else { return(Reject(sink, var, "The left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers.")); } } UnrollCtx ctx = new UnrollCtx { Replacements = replacements }; WList <LNode> output = new WList <LNode>(); int iteration = 0; foreach (LNode replacement in cases) { iteration++; bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces); int count = tuple ? replacement.ArgCount : 1; if (replacements.Count != count) { sink.Error(replacement, "iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count); if (count < replacements.Count) { continue; // too few } } for (int i = 0; i < replacements.Count; i++) { replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement; } if (body.Calls(S.Braces)) { foreach (LNode stmt in body.Args) { output.Add(ctx.Replace(stmt).Value); } } else { output.Add(ctx.Replace(body).Value); } } foreach (var r in replacements) { if (r.C == 0 && !r.A.Name.StartsWith("_")) { sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A); } } return(body.With(S.Splice, output.ToVList())); }
public static LNode @unless(LNode node, IMessageSink sink) { var args = node.Args; LNode cond = args.TryGet(0, null), then = args.TryGet(1, null), @else = args.TryGet(3, null); if (node.ArgCount != 2 && (node.ArgCount != 4 || !args.TryGet(2, null).IsIdNamed(_else))) return Reject(sink, node, "An unless-statement must have the form «unless(Cond, expr)» or «unless(Cond, ThenClause, else, ElseClause)»"); if (@else == null) return node.With(S.If, F.Call(S.Not, cond), then); else return node.With(S.If, F.Call(S.Not, cond), then, @else); }