private Pred TranslateLoopExpr(LNode expr, Context ctx) { Symbol type = expr.Name; bool? greedy = null; bool g; expr = expr.Args[0]; if ((g = expr.Calls(_Greedy, 1)) || expr.Calls(_Nongreedy, 1)) { greedy = g; expr = expr.Args[0]; } BranchMode branchMode; Pred subpred = BranchToPred(expr, out branchMode, ctx); if (branchMode != BranchMode.None) { _sink.Write(Severity.Warning, expr, "'default' and 'error' only apply when there are multiple arms (a|b, a/b)"); } if (type == _Star) { return(new Alts(expr, LoopMode.Star, subpred, greedy)); } else if (type == _Plus) { return(new Seq(subpred, new Alts(expr, LoopMode.Star, subpred.Clone(), greedy), expr)); } else // type == _Opt { return(new Alts(expr, LoopMode.Opt, subpred, greedy)); } }
static LNode MergeIdentifiers(LNode left, LNode right) { if (left == null) { return(right); } if (right.IsIdNamed(S.Missing)) { return(left); } { LNode right1, right2; if (right.Calls(CodeSymbols.Dot, 1) && (right2 = right.Args[0]) != null) { return(LNode.Call(CodeSymbols.Dot, LNode.List(left, right2))); } else if (right.Calls(CodeSymbols.Dot, 2) && (right1 = right.Args[0]) != null && (right2 = right.Args[1]) != null) { return(LNode.Call(CodeSymbols.Dot, LNode.List(MergeIdentifiers(left, right1), right2))); } else { throw new LogException(Severity.Note, right, "Multi-using statement seems malformed. Correct example: `using System(.Text, .Linq));`"); } } }
public override Pred CodeToTerminalPred(LNode expr, ref string errorMsg) { bool isInt = false; PGIntSet set; if (expr.IsIdNamed(_underscore)) { set = PGIntSet.AllExceptEOF; } else if (expr.IsIdNamed(_EOF)) { set = PGIntSet.EOF; } else if (expr.Calls(S.DotDot, 2) || expr.Calls(S.DotDotDot, 2)) { int? from = ConstValue(expr.Args[0], ref isInt); int? to = ConstValue(expr.Args[1], ref isInt); if (from == null || to == null) { errorMsg = "Expected int32 or character literal on each side of «..»"; return null; } set = PGIntSet.WithRanges(from.Value, to.Value); } else if (expr.Value is string) { return Pred.Seq((string)expr.Value); } else { int? num = ConstValue(expr, ref isInt); if (num == null) { errorMsg = "Unrecognized expression. Expected int32 or character literal instead of: " + expr.ToString(); // warning return null; } set = PGIntSet.With(num.Value); } set.IsCharSet = !isInt; return new TerminalPred(expr, set, true); }
// A complex identifier has the form Id, ComplexId.Id, or ComplexId!(ComplexId, ...) // where Id is a simple identifier and ComplexId is a complex identifier. Also, the // form X!Y!Z, i.e. #of(#of(...), ...) is not allowed. $Substitution is allowed. public static bool IsComplexId(LNode id, bool allowOf = true) { if (id.IsCall) { if (id.Name == S.Of) { if (allowOf) { return((id.HasSimpleHead() || IsComplexId(id.Target, false)) && id.Args.All(a => IsComplexId(a))); } return(false); } else if (id.Calls(S.Dot, 2)) { return(id.Args.Last.IsId && IsComplexId(id.Args[0])); } else if (id.Calls(S.Substitute, 1)) { return(true); } else { return(false); } } else { return(id.IsId); } }
Pred BranchToPred(LNode expr, out BranchMode mode, Context ctx) { if (expr.Calls(_Default, 1) || expr.Calls(_Default2, 1)) { expr = expr.Args[0]; mode = BranchMode.Default; } else if (expr.Calls(_Error, 1) || expr.IsIdNamed(_DefaultError)) { mode = (expr.AttrNamed(S.Continue) != null || expr.AttrNamed(GSymbol.Get("continue")) != null) ? BranchMode.ErrorContinue : BranchMode.ErrorExit; if (expr.Calls(_Error, 1)) { expr = expr.Args[0]; } else { return(DefaultErrorBranch.Value); } } else { mode = BranchMode.None; } return(NodeToPred(expr, ctx)); }
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)); }
// Converts the subject of a substitution expr like $'*' to a valid ident- // ifier, under the assumption that it doesn't refer to a rule or label. static Symbol PickVarNameForLNode(LNode label) { if (label.IsId) { // Ignore the predefined special substitutions $LA and $LI //if (label.Name.Name == "LA" || label.Name.Name == "LI") // return null; return(GSymbol.Get("tok_" + label.Name)); } else if (label.IsLiteral) { return(LiteralToVarName(label.Value)); } else if (label.Calls(S.Dot, 2)) { return(GSymbol.Get("tok__" + label.Args[1].Name)); } else if (label.Calls(S.DotDot, 2) || label.Calls(S.DotDotDot, 2)) { return(GSymbol.Get(PickVarNameForLNode(label[0]).Name + "_" + PickVarNameForLNode(label[1]).Name)); } else // can't return null { return(GSymbol.Get(label.GetHashCode().ToString())); } }
/// <summary> /// Decodes an LNode as a type definition. /// </summary> /// <param name="node">The node to decode.</param> /// <param name="state">The decoder's state.</param> /// <returns>A decoded type.</returns> public static IrType Decode(LNode node, DecoderState state) { QualifiedName name; if (!FeedbackHelpers.AssertArgCount(node, 4, state.Log) || !state.AssertDecodeQualifiedName(node.Args[0], out name)) { return(null); } else if (node.Calls(TypeParameterDefinitionSymbol)) { return(new IrGenericParameter(node, state)); } else if (node.Calls(TypeDefinitionSymbol)) { return(new IrType(node, state)); } else { state.Log.LogSyntaxError( node, FeedbackHelpers.QuoteEven( "expected ", TypeDefinitionSymbol.Name, " or ", TypeParameterDefinitionSymbol.Name, ".")); return(null); } }
public static LNode ECSharpRule(LNode node, IMacroContext context) { // This will be called for all methods and properties, so we have to // examine it for the earmarks of a rule definition. bool isProp; if (!(isProp = node.Calls(S.Property, 4)) && !node.Calls(S.Fn, 4)) { return(null); } LNode returnType = node.Args[0]; bool isToken; bool retValIsRule = (isToken = returnType.IsIdNamed(_token)) || returnType.IsIdNamed(_rule); var attrs = node.Attrs; LNode lastAttr = null; if (!retValIsRule) { if (attrs.IsEmpty) { return(null); } lastAttr = attrs.Last; if (!(isToken = lastAttr.IsIdNamed(_hash_token)) && !lastAttr.IsIdNamed(_hash_rule)) { return(null); } attrs.RemoveAt(attrs.Count - 1); } else { returnType = F.Void; } //node = context.PreProcessChildren(); LNode name = node.Args[1]; LNode args = node.Args[2]; if (args.IsIdNamed(S.Missing)) // @`` { args = F.List(); // output will be a #fn, which does not allow @`` as its arg list } LNode newBody = ParseRuleBody(node.Args.Last, context); if (newBody != null) { return(LNode.Call(isToken ? _hash_token : _hash_rule, new VList <LNode> { returnType, name, args, newBody }, node.Range, node.Style).WithAttrs(attrs)); } else { return(null); } }
private static Symbol LinqClauseKind(LNode n, Pedantics p) { Symbol name = n.Name; var args = n.Args; if (name == S.From) { if (!IsInExpr(args[0], p)) { return(null); } } else if (name == S.Let) { if (args.Count != 1) { return(null); } } else if (n.Calls(S.WhereClause)) { if (args.Count != 1) { return(null); } } else if (n.Calls(S.Join)) { if (args.Count < 2 || args.Count > 3) { return(null); } if (!IsInExpr(args[0], p)) { return(null); } if (!args[1].Calls("#equals", 2)) { return(null); } if (args.Count >= 3) { LNode into = args[2], id; if (!(into.Calls(S.Into, 1) && !HasPAttrsOrParens(id = into[0], p) && (id.IsId || id.Calls(S.Substitute, 1)))) { return(null); } } } else if (n.Calls(S.OrderBy)) { // All argument lists are acceptable } else { return(null); } return(name); }
static bool IsCaseLabel(LNode @case) { if (@case.Calls(CodeSymbols.Case) || @case.Calls(CodeSymbols.Label, 1) && @case.Args[0].IsIdNamed((Symbol)"#default")) { return(true); } return(false); }
internal static LNode GetName(LNode type) { { LNode name; if (type.Calls(CodeSymbols.Class, 3) && (name = type.Args[0]) != null && type.Args[1].Calls(CodeSymbols.AltList) && type.Args[2].Calls(CodeSymbols.Braces) || type.Calls(CodeSymbols.Struct, 3) && (name = type.Args[0]) != null && type.Args[1].Calls(CodeSymbols.AltList) && type.Args[2].Calls(CodeSymbols.Braces) || type.Calls(CodeSymbols.Enum, 3) && (name = type.Args[0]) != null && type.Args[1].Calls(CodeSymbols.AltList) && type.Args[2].Calls(CodeSymbols.Braces)) return name; else return null; } }
static bool IsParamsCapture(LNode pattern) { if (pattern.Calls(S.Substitute, 1)) { LNode arg = pattern.Args.Last; return((arg.Calls(S.DotDot, 1) || arg.Calls(S.DotDotDot, 1) || arg.AttrNamed(S.Params) != null) && GetCaptureIdentifier(pattern) != null); } return(false); }
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()); }
public static LNode ECSharpRule(LNode node, IMacroContext context) { // This will be called for all methods and properties, so we have to // examine it for the earmarks of a rule definition. bool isProp; if (!(isProp = node.Calls(S.Property, 4)) && !node.Calls(S.Fn, 4)) { return(null); } LNode returnType = node.Args[0]; bool isToken; bool retValIsRule = (isToken = returnType.IsIdNamed(_token)) || returnType.IsIdNamed(_rule); var attrs = node.Attrs; if (!retValIsRule) { int?i_rule = attrs.FinalIndexWhere(n => n.IsIdNamed(_hash_token) || n.IsIdNamed(_hash_rule)); if (i_rule == null) { return(null); } isToken |= attrs[i_rule.Value].IsIdNamed(_hash_token); attrs.RemoveAt(i_rule.Value); } else { returnType = F.Void; } //node = context.PreProcessChildren(); LNode name = node.Args[1]; LNode args = node.Args[2]; if (args.IsIdNamed(S.Missing)) // @`` { args = F.AltList(); // output will be a #fn, which does not allow @`` as its arg list } LNode newBody = ParseRuleBody(node.Args.Last, context); if (newBody != null) { // #rule($returnType, $name, $args, $newBody) return(LNode.Call(isToken ? _hash_token : _hash_rule, LNode.List(returnType, name, args, newBody), node.Range, node.Style).WithAttrs(attrs)); } else { return(null); } }
private static void DoDeconstruct(LNode arg, IMacroContext context, bool printErrorOnFailure) { LNode patternSpec = arg.Args[0, LNode.Missing], input = arg.Args[1, LNode.Missing]; if (!arg.Calls(S.Assign, 2)) { if (arg.Calls(S.Lambda, 2)) { G.Swap(ref patternSpec, ref input); } else { context.Sink.Error(arg, "expected an assignment (`patterns = input`)"); return; } } // Build list of patterns out of the binary operator series p1 | p2 | p3 var patterns = new FVList <LNode>(); while (patternSpec.Calls(S.OrBits, 2) && !patternSpec.IsParenthesizedExpr()) { patterns.Add(patternSpec[1]); patternSpec = patternSpec[0]; } patterns.Add(patternSpec); // Remove outer braces, then run macros patterns = patterns.SmartSelect(p => p.Calls(S.Braces, 1) ? p[0] : p); input = input.Calls(S.Braces, 1) ? input[0] : input; input = context.PreProcess(input); // Perform matching & capturing foreach (var pattern in patterns) { IDictionary <Symbol, LNode> captures; if (LNodeExt.MatchesPattern(input, pattern, out captures)) { if (captures.Count == 0) { context.Write(printErrorOnFailure ? Severity.Warning : Severity.Error, pattern, "This pattern has no effect, since it does not use `$` to capture any variables."); } SetSyntaxVariables(captures, context); return; } } if (printErrorOnFailure) { context.Sink.Error(arg, "Deconstruction failed."); } }
public static LNode ECSharpRule(LNode node, IMessageSink sink) { // This will be called for all methods and properties, so we have to // examine it for the earmarks of a rule definition. bool isProp; if (!(isProp = node.Calls(S.Property, 3)) && !node.Calls(S.Fn, 4)) { return(null); } LNode returnType = node.Args[0]; bool isToken; bool ruleRetVal = (isToken = returnType.IsIdNamed(_token)) || returnType.IsIdNamed(_rule); var attrs = node.Attrs; LNode lastAttr = null; if (!ruleRetVal) { if (attrs.IsEmpty) { return(null); } lastAttr = attrs.Last; if (!(isToken = lastAttr.IsIdNamed(_hash_token)) && !lastAttr.IsIdNamed(_hash_rule)) { return(null); } attrs.RemoveAt(attrs.Count - 1); } else { returnType = F.Void; } LNode name = node.Args[1]; LNode args = isProp ? F.List() : node.Args[2]; LNode newBody = ParseRuleBody(node.Args.Last, sink); if (newBody != null) { return(LNode.Call(isToken ? _hash_token : _hash_rule, new RVList <LNode> { returnType, name, args, newBody }, node.Range, node.Style).WithAttrs(attrs)); } else { return(null); } }
internal static Symbol DecodeSubstitutionExpr(LNode expr, out LNode condition, out bool isParams, out bool refExistingVar) { condition = null; isParams = false; refExistingVar = false; if (expr.Calls(S.Substitute, 1)) { LNode id = expr.Args[0]; if (id.AttrNamed(S.Params) != null) { isParams = true; } else if (id.Calls(S.DotDotDot, 1) || id.Calls(S.DotDot, 1)) { isParams = true; id = id.Args[0]; } if (id.AttrNamed(S.Ref) != null) { refExistingVar = true; } if (id.Calls(S.IndexBracks, 2)) { // very old style condition = id.Args[1]; id = id.Args[0]; } else { while (id.Calls(S.And, 2) || id.Calls(S.When, 2)) { // old style `&&` and new style `when` condition = condition == null ? id.Args[1] : LNode.Call(CodeSymbols.And, LNode.List(id.Args[1], condition)).SetStyle(NodeStyle.Operator); id = id.Args[0]; } } if (condition != null) { condition = condition.ReplaceRecursive(n => n.IsIdNamed(S._HashMark) ? id : null); } if (!id.IsId) { return(null); } return(id.Name); } return(null); }
static IEnumerable<LNode> GetNamespaces(LNode multiName) { { LNode outerNamespace; VList<LNode> args; if (multiName.Calls(CodeSymbols.Dot) || multiName.Calls(CodeSymbols.Of)) { } else if (multiName.IsCall && (outerNamespace = multiName.Target) != null) { args = multiName.Args; if (args.Count == 1 && args[0].Calls(S.Braces)) args = args[0].Args; return args.SelectMany(arg => GetNamespaces(arg) ?? ListExt.Single(arg)).Select(subNS => MergeIdentifiers(outerNamespace, subNS)); } } return null; }
/// <summary> /// Decodes an LNode as a reference to a generic member. /// Logs an error if the decoding process fails. /// </summary> /// <param name="node">A node to decode as a generic member.</param> /// <param name="genericMember">The generic member described by <paramref name="node"/>.</param> /// <returns> /// <c>true</c> if <paramref name="node"/> can be decoded as a /// generic member; otherwise, <c>false</c>. /// </returns> public bool AssertDecodeGenericMember( LNode node, out IGenericMember genericMember) { if (node.Calls(EncoderState.typeHintSymbol)) { if (!FeedbackHelpers.AssertArgCount(node, 1, Log)) { genericMember = null; return(false); } else { var type = DecodeType(node.Args[0]); genericMember = type; return(!(type == null || type is ErrorType)); } } else if (node.Calls(EncoderState.methodHintSymbol)) { if (!FeedbackHelpers.AssertArgCount(node, 1, Log)) { genericMember = null; return(false); } else { var method = DecodeMethod(node.Args[0]); genericMember = method; return(method != null); } } else { FeedbackHelpers.LogSyntaxError( Log, node, FeedbackHelpers.QuoteEven( "unknown kind of generic member; " + "generic member kinds must be hinted using either ", EncoderState.methodHintSymbol.ToString(), " or ", EncoderState.typeHintSymbol.ToString(), " nodes.")); genericMember = null; return(false); } }
bool SetPropertyHelper(string exprStr, bool quote) { LNode expr = (InLang ?? ParsingService.Default).ParseSingle(exprStr, Sink, ParsingMode.Expressions); if (expr.Calls(CodeSymbols.Assign, 2) && !expr[0].IsCall) { LNode keyNode = expr[0], valueNode = expr[1]; if (!keyNode.IsCall) { object key = keyNode.IsLiteral ? keyNode.Value : keyNode.Name; if (quote) { valueNode = valueNode.Calls(CodeSymbols.Braces) ? valueNode.WithTarget(CodeSymbols.Splice) : valueNode; MacroProcessor.DefaultScopedProperties[key] = valueNode; return(true); } else if (!valueNode.IsCall) { object value = valueNode.IsLiteral ? valueNode.Value : valueNode.Name.Name; MacroProcessor.DefaultScopedProperties[key] = value; return(true); } } } if (quote) { Sink.Error("Command line", "--snippet: syntax error. Expected `key=code` where `key` is a literal or identifier with which to associate a code snippet."); } else { Sink.Error("Command line", "--set: syntax error. Expected `key=value` where `key` and `value` are literals or identifiers."); } return(false); }
public static LNode In(LNode node, IMacroContext context) { { LNode range, x; if (node.Calls(CodeSymbols.In, 2) && (x = node.Args[0]) != null && (range = node.Args[1]) != null) { LNode parens; range = range.WithoutAttrNamed(S.TriviaInParens, out parens); if (parens == null) { { LNode hi, lo; if (range.Calls(CodeSymbols.DotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRangeExcludeHi"))).SetStyle(NodeStyle.Operator), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (range.Calls(CodeSymbols.DotDotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRange"))).SetStyle(NodeStyle.Operator), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); } } return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(range, LNode.Id((Symbol) "Contains"))).SetStyle(NodeStyle.Operator), LNode.List(x)); } } return null; }
LNode EliminateBlockExprsInExecStmt(LNode stmt) { if (!stmt.IsCall) { return(stmt); } { LNode cond; VList <LNode> blocks; if (stmt.Calls(CodeSymbols.Braces)) { return(stmt.WithArgs(EliminateBlockExprs(stmt.Args, false))); } else if (stmt.CallsMin(CodeSymbols.If, 1) && (cond = stmt.Args[0]) != null) { blocks = new VList <LNode>(stmt.Args.Slice(1)); return(ProcessBlockCallStmt(stmt, 1)); } else if (stmt.HasSpecialName && stmt.ArgCount >= 1 && stmt.Args.Last.Calls(S.Braces)) { return(ProcessBlockCallStmt(stmt, stmt.ArgCount - 1)); } else { stmt = BubbleUpBlocks(stmt); if (stmt.CallsMin(__runSequence, 1)) { return(stmt.Args.AsLNode(S.Splice)); } } } return(stmt); }
public static RVList<LNode> WithSpliced(this RVList<LNode> list, int index, LNode node, Symbol listName) { if (node.Calls(listName)) return list.InsertRange(index, node.Args); else return list.Insert(index, node); }
[LexicalMacro("x in lo..hi; x in lo...hi; x in ..hi; x in lo..._; x in range", "Converts an 'in' expression to a normal C# expression using the following rules " + "(keeping in mind that the EC# parser treats `..<` as an alias for `..`):\n" + "1. `x in _..hi` and `x in ..hi` become `x.IsInRangeExcl(hi)`\n" + "2. `x in _...hi` and `x in ...hi` become `x.IsInRangeIncl(hi)`\n" + "3. `x in lo.._` and `x in lo..._` become simply `x >= lo`\n" + "4. `x in lo..hi` becomes `x.IsInRangeExcludeHi(lo, hi)`\n" + "5. `x in lo...hi` becomes `x.IsInRange(lo, hi)`\n" + "6. `x in range` becomes `range.Contains(x)`\n" + "The first applicable rule is used.", "#in")] public static LNode In(LNode node, IMacroContext context) { { LNode range, x; if (node.Calls(CodeSymbols.In, 2) && (x = node.Args[0]) != null && (range = node.Args[1]) != null) { LNode parens; range = range.WithoutAttrNamed(S.TriviaInParens, out parens); if (parens == null) { { LNode hi, lo; if (range.Calls(CodeSymbols.DotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRangeExcludeHi"))), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (range.Calls(CodeSymbols.DotDotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRange"))), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); } } return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(range, LNode.Id((Symbol) "Contains"))), LNode.List(x)); } } return null; }
/// <summary>Verifies that a declaration of a single variable is valid, and gets its parts.</summary> /// <param name="expr">Potential variable or field declaration</param> /// <param name="type">Variable type (empty identifier if `var`)</param> /// <param name="name">Variable name (identifier or $substutution expr)</param> /// <param name="initialValue">Initial value that is assigned in <c>expr</c>, or null if unassigned.</param> /// <returns>True if <c>expr</c> is declaration of a single variable.</returns> public static bool IsVariableDeclExpr(LNode expr, out LNode type, out LNode name, out LNode initialValue, Pedantics p = Pedantics.Lax) { type = name = initialValue = null; if (expr.Calls(S.Var, 2)) { type = expr.Args[0]; name = expr.Args[1]; // don't need to call HasPAttrs(type, p) because IsComplexIdentifier will check for printable attrs if (HasPAttrs(expr, p) || HasPAttrs(name, p)) { return(false); } if (name.Calls(S.Assign, 2)) { initialValue = name.Args[1]; name = name.Args[0]; if (HasPAttrs(name, p) || HasPAttrs(initialValue, p)) { return(false); } } return(IsComplexIdentifier(type, ICI.AllowAnyExprInOf, p) && (name.IsId || name.Calls(S.Substitute, 1))); } return(false); }
public static void SpliceAdd(this RWList<LNode> list, LNode node, Symbol listName) { if (node.Calls(listName)) list.AddRange(node.Args); else list.Add(node); }
protected override void Stmt(string result, LNode input, Action <EcsPrinterOptions> configure = null, Mode mode = Mode.Both) { bool exprMode = (mode & Mode.Expression) != 0; if ((mode & Mode.PrinterTest) == 0) { return; } var options = new EcsPrinterOptions(); options.IndentString = " "; // by default, test the mode that is more difficult to get right options.AllowChangeParentheses = false; // TODO: make round tripping work without this options.NewlineOptions &= ~(NewlineOpt.AfterOpenBraceInNewExpr | NewlineOpt.BeforeCloseBraceInNewExpr); if (configure != null) { configure(options); } var sb = new StringBuilder(); var mode2 = exprMode ? ParsingMode.Expressions : ParsingMode.Statements; if (input.Calls(S.Splice)) { EcsLanguageService.Value.Print(input.Args, sb, MessageSink.Default, mode2, options); } else { EcsLanguageService.Value.Print(input, sb, MessageSink.Default, mode2, options); } AreEqual(result, sb.ToString()); }
private static LNode StmtToCSharp(LNode stmt, IMacroContext context, bool execContext, Symbol parentConstruct) { if (!stmt.IsCall) { return(stmt); } if (stmt.Calls(S.Braces)) { return(null); } var helpers = RVList <LNode> .Empty; ExecInfo info; if (!StatementTypes.TryGetValueSafe(stmt.Name, out info)) { info = new ExecInfo(-1, execContext); } var args = stmt.Args; for (int i = info.IgnoreMax; i < stmt.Args.Count; i++) { if (i == info.BraceIndex) { continue; } } return(stmt); }
public virtual LNode VisitInput(LNode stmt, IMessageSink sink) { LNode aliasSet; if ((stmt.Calls(_alias, 1) || stmt.CallsMin(S.Alias, 1)) && (aliasSet = stmt.Args[0]).Calls(S.Assign, 2)) { IEnumerable <KeyValuePair <LNode, LNode> > q; LNode alias = aliasSet.Args[0], replacement = aliasSet.Args[1], old; if (_definedAliases.TryGetValue(alias, out old)) { if (stmt.AttrNamed(S.Partial) == null || !old.Equals(replacement)) { sink.Warning(alias, "Redefinition of alias '{0}'", alias); } } else if ((q = _definedAliases.Where(pair => replacement.Equals(pair.Value))).Any()) { sink.Warning(replacement, "Aliases '{0}' and '{1}' have the same replacement value", q.First().Key, alias); } _definedAliases[alias] = replacement; return(LNode.Call(S.Splice, VList <LNode> .Empty)); // erase alias from output } return(null); }
bool SetPropertyHelper(string exprStr, bool quote) { LNode expr = (InLang ?? ParsingService.Current).ParseSingle(exprStr, Sink, ParsingMode.Expressions); if (expr.Calls(CodeSymbols.Assign, 2) && !expr[0].IsCall) { object key = expr[0].IsLiteral ? expr[0].Value : expr[0].Name; LNode valueN = expr[1]; object value = valueN.Value; if (quote) { value = valueN.Calls(CodeSymbols.Braces) ? valueN.Args.AsLNode(CodeSymbols.Splice) : valueN; } if (!(value is NoValue)) { MacroProcessor.DefaultScopedProperties[key] = value; return(true); } } if (quote) { Sink.Write(Severity.Error, "Command line", "--snippet: syntax error. Expected `key=code` where `key` is a literal or identifier with which to associate a code snippet."); } else { Sink.Write(Severity.Error, "Command line", "--set: syntax error. Expected `key=value` where `key` is a literal or identifier with which to associate a value."); } return(false); }
/// <summary>Returns Basis if it's a method signature; otherwise constructs a default signature.</summary> public LNode GetMethodSignature() { if (Basis != null && Basis.Calls(S.Fn) && Basis.ArgCount.IsInRange(3, 4)) { var parts = Basis.Args; if (parts.Count == 4) { parts.RemoveAt(3); } if (IsRecognizer) { parts[0] = F.Bool; } parts[1] = F.Id(Name); return(Basis.WithArgs(parts)); } else { var method = F.Fn(IsRecognizer ? F.Bool : F.Void, F.Id(Name), F.List()); if (IsPrivate) { method = F.Attr(F.Id(S.Private), method); } else if (IsStartingRule | IsToken) { method = F.Attr(F.Id(S.Public), method); } return(method); } }
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); }
public static void SpliceInsert(this RWList<LNode> list, int index, LNode node, Symbol listName) { if (node.Calls(listName)) list.InsertRange(index, node.Args); else list.Insert(index, node); }
public static LNode NullDot(LNode node, IMacroContext context) { if (!node.Calls(S.NullDot, 2)) return null; var a = node.Args; LNode leftSide = a[0], rightSide = a[1]; // So our input will be something like a.b?.c().d<x>, which is parsed // (a.b) ?. (c().d<x>) // in EC# we would transform this to // a.b::tmp != null ? tmp.c().d<x> : null // but there's no EC# compiler yet, so instead use code that plain C# // can support: // a.b != null ? (a.b).c().d<x> : null LNode condition, thenExpr; if (StandardMacros.LooksLikeSimpleValue(leftSide)) { condition = F.Call(S.Neq, leftSide, F.@null); thenExpr = ConvertToNormalDot(leftSide, rightSide); } else { LNode tempVar = F.Id(StandardMacros.NextTempName(context, leftSide)); condition = F.Call(S.Neq, F.Var(F.Missing, tempVar, leftSide), F.@null); thenExpr = ConvertToNormalDot(tempVar, rightSide); } return F.InParens(F.Call(S.QuestionMark, condition, thenExpr, F.Null)); }
public static LNode NullDot(LNode node, IMacroContext context) { if (!node.Calls(S.NullDot, 2)) { return(null); } var a = node.Args; LNode leftSide = a[0], rightSide = a[1]; // So our input will be something like a.b?.c().d<x>, which is parsed // (a.b) ?. (c().d<x>) // in EC# we would transform this to // a.b::tmp != null ? tmp.c().d<x> : null // but there's no EC# compiler yet, so instead use code that plain C# // can support: // a.b != null ? (a.b).c().d<x> : null LNode condition, thenExpr; if (LeMP.ecs.StandardMacros.LooksLikeSimpleValue(leftSide)) { condition = F.Call(S.NotEq, leftSide, F.@null); thenExpr = ConvertToNormalDot(leftSide, rightSide); } else { LNode tempVar = F.Id(StandardMacros.NextTempName(context, leftSide)); condition = F.Call(S.NotEq, F.Var(F.Missing, tempVar, leftSide), F.@null); thenExpr = ConvertToNormalDot(tempVar, rightSide); } return(F.InParens(F.Call(S.QuestionMark, condition, thenExpr, F.Null))); }
static LNode MergeIdentifiers(LNode left, LNode right) { if (left == null) return right; if (right.IsIdNamed(S.Missing)) return left; { LNode right1, right2; if (right.Calls(CodeSymbols.Dot, 1) && (right2 = right.Args[0]) != null) return LNode.Call(CodeSymbols.Dot, LNode.List(left, right2)); else if (right.Calls(CodeSymbols.Dot, 2) && (right1 = right.Args[0]) != null && (right2 = right.Args[1]) != null) return LNode.Call(CodeSymbols.Dot, LNode.List(MergeIdentifiers(left, right1), right2)); else throw new LogException(Severity.Note, right, "Multi-using statement seems malformed. Correct example: `using System(.Text, .Linq));`"); } }
public static RVList<LNode> WithSpliced(this RVList<LNode> list, LNode node, Symbol listName) { if (node.Calls(listName)) return list.AddRange(node.Args); else return list.Add(node); }
internal static VList <LNode> GetArgNamesFromFormalArgList(LNode args, Action <LNode> onError) { VList <LNode> formalArgs = args.Args; VList <LNode> argList = VList <LNode> .Empty; foreach (var formalArg in formalArgs) { if (!formalArg.Calls(S.Var, 2)) { onError(formalArg); } else { 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); } } return(argList); }
public static LNode static_if(LNode @if, IMacroContext context) { if (!Range.IsInRange(@if.ArgCount, 2, 3)) { return(null); } LNode cond = context.PreProcess(@if.Args[0]); object @bool; if ((@bool = cond.Value) is bool) { LNode output = (bool)@bool ? @if.Args[1] : @if.Args.TryGet(2, null) ?? F.Call(S.Splice); if (output.Calls(S.Braces)) { return(output.WithTarget(S.Splice)); } else { return(output); } } else { return(Reject(context, @if.Args[0], "'static if' is incredibly limited right now. Currently it only supports a literal boolean or (x `tree==` y)")); } }
private void PrintWhereClauses(LNode name) { // Example: #of(Foo, [#where(#class, IEnumerable)] T) // represents Foo<T> where T: class, IEnumerable if (!name.Calls(S.Of)) { return; } // Look for "where" clauses and print them bool first = true; for (int i = 1, c = name.ArgCount; i < c; i++) { var param = name.Args[i]; for (int a = 0, ac = param.AttrCount; a < ac; a++) { var where = param.Attrs[a]; if (where.CallsMin(S.Where, 1)) { using (Indented) { if (!Newline(first ? NewlineOpt.BeforeWhereClauses : NewlineOpt.BeforeEachWhereClause)) { _out.Space(); } first = false; _out.Write("where", true); PrintSimpleIdent(param.Name, 0); Space(SpaceOpt.BeforeWhereClauseColon); WriteThenSpace(':', SpaceOpt.AfterColon); bool firstC = true; foreach (var constraint in where.Args) { if (firstC) { firstC = false; } else { WriteThenSpace(',', SpaceOpt.AfterComma); } if (constraint.Name == S.New && constraint.ArgCount == 0) { _out.Write("new()", true); } else if (constraint.IsId && (constraint.Name == S.Class || constraint.Name == S.Struct)) { WriteOperatorName(constraint.Name); } else { PrintExpr(constraint, StartExpr); } } } } } } }
public static LNode static_if(LNode @if, IMessageSink sink) { if (!MathEx.IsInRange(@if.ArgCount, 2, 3)) { return(null); } RVList <LNode> conds = MacroProcessor.Current.ProcessSynchronously(@if.Args[0]); object @bool; if (conds.Count == 1 && (@bool = conds[0].Value) is bool) { LNode output = (bool)@bool ? @if.Args[1] : @if.Args.TryGet(2, null) ?? F.Call(S.Splice); if (output.Calls(S.Braces)) { return(output.WithTarget(S.Splice)); } else { return(output); } } else { return(Reject(sink, @if.Args[0], "'static if' is incredibly limited right now. Currently it only supports a literal boolean or (x `tree==` y)")); } }
public Rule(LNode basis, Symbol name, Pred pred, bool isStartingRule = true) { Basis = basis; Pred = pred; Name = name; IsStartingRule = isStartingRule; EndOfRule = new EndOfRule(this); if (basis != null && basis.Calls(S.Fn) && basis.ArgCount >= 3) ReturnType = basis.Args[0]; }
/// <summary>Sets ListType and/or ListInitializer based on an expression. /// A statement like <c>Type x = expr</c> sets <c>ListType = Type</c> and <c>ListInitializer = expr</c>; /// A statement like <c>Type x</c> just sets <c>ListType = Type</c>; and any other /// expression <c>expr</c> sets <c>ListInitializer = expr</c>.</summary> public void SetListInitializer(LNode varDecl) { if (varDecl.Calls(S.Var, 2)) { ListType = varDecl.Args[0]; if (varDecl.Args[1].Calls(S.Assign, 2)) ListInitializer = varDecl.Args[1].Args[1]; } else { ListInitializer = varDecl; } }
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)) return target.WithArgChanged(1, target.Args[1].WithName( Ecs.EcsNodePrinter.KeyNameComponentOf(methodName))); return target; } else return null; }
private static void DoDeconstruct(LNode arg, IMacroContext context, bool printErrorOnFailure) { LNode patternSpec = arg.Args[0, LNode.Missing], input = arg.Args[1, LNode.Missing]; if (!arg.Calls(S.Assign, 2)) { if (arg.Calls(S.Lambda, 2)) G.Swap(ref patternSpec, ref input); else { context.Sink.Error(arg, "expected an assignment (`patterns = input`)"); return; } } // Build list of patterns out of the binary operator series p1 | p2 | p3 var patterns = new FVList<LNode>(); while (patternSpec.Calls(S.OrBits, 2) && !patternSpec.IsParenthesizedExpr()) { patterns.Add(patternSpec[1]); patternSpec = patternSpec[0]; } patterns.Add(patternSpec); // Remove outer braces, then run macros patterns = patterns.SmartSelect(p => p.Calls(S.Braces, 1) ? p[0] : p); input = input.Calls(S.Braces, 1) ? input[0] : input; input = context.PreProcess(input); // Perform matching & capturing foreach (var pattern in patterns) { IDictionary<Symbol, LNode> captures; if (LNodeExt.MatchesPattern(input, pattern, out captures)) { if (captures.Count == 0) context.Write(printErrorOnFailure ? Severity.Warning : Severity.Error, pattern, "This pattern has no effect, since it does not use `$` to capture any variables."); SetSyntaxVariables(captures, context); return; } } if (printErrorOnFailure) context.Sink.Error(arg, "Deconstruction failed."); }
public static LNode replace(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); var args = args_body.A; var body = args_body.B; if (args.Count == 1 && args[0].Calls(S.Tuple)) args = args[0].Args; // LESv2 if (args.Count >= 1) { bool preprocess = node.Calls("replacePP"); var patterns = new Pair<LNode, LNode>[args.Count]; for (int i = 0; i < patterns.Length; i++) { var pair = args[i]; if (pair.Calls(S.Lambda, 2)) { LNode pattern = pair[0], repl = pair[1]; if (preprocess) { pattern = context.PreProcess(pattern); repl = context.PreProcess(repl); } if (pattern.Calls(S.Braces)) { if (pattern.ArgCount == 1) pattern = pattern.Args[0]; else context.Write(Severity.Error, pattern, "The braces must contain only a single statement. To search for braces literally, use `{{ ... }}`"); } if (repl.Calls(S.Braces)) repl = repl.Args.AsLNode(S.Splice); // Avoid StackOverflowException when pattern is $Id (sadly, it // is uncatchable so it can crash LeMP.exe and even Visual Studio) if (LNodeExt.GetCaptureIdentifier(pattern) != null) return Reject(context, pattern, "The left side of `=>` cannot be a capture. Remove the `$`."); patterns[i] = Pair.Create(pattern, repl); } else { string msg = "Expected 'pattern => replacement'."; if (pair.Descendants().Any(n => n.Calls(S.Lambda, 2))) msg += " " + "(Using '=>' already? Put the pattern on the left-hand side in parentheses.)"; return Reject(context, pair, msg); } } int replacementCount; var output = Replace(body, patterns, out replacementCount); if (replacementCount == 0) context.Sink.Warning(node, "No patterns recognized; no replacements were made."); return output.AsLNode(S.Splice); } return null; }
protected override VList<LNode> AttachTriviaTo(ref LNode node, IListSource<Token> trivia, TriviaLocation loc, LNode parent, int indexInParent) { int nli; if (loc == TriviaLocation.Trailing && indexInParent == 1 && parent != null && parent.Calls(CodeSymbols.If, 3) && (nli = trivia.LastIndexWhere(t => t.Type() == TokenType.Newline)) != -1) { // The 'else' keyword is invisible here, but it often appears on a line by // itself; remove a newline to avoid creating a blank line when printing. var triviaSans = new DList<Token>(trivia); triviaSans.RemoveAt(nli); trivia = triviaSans; } return base.AttachTriviaTo(ref node, trivia, loc, parent, indexInParent); }
[LexicalMacro("using System(, .Collections.Generic, .Linq, .Text);", "Generates multiple using-statements from a single one.", "#import", Mode = MacroMode.Passive | MacroMode.Normal)] public static LNode UsingMulti(LNode stmt, IMacroContext context) { { LNode multiNamespace; if (stmt.Calls(CodeSymbols.Import, 1) && (multiNamespace = stmt.Args[0]) != null) try { var list = GetNamespaces(stmt[0]); if (list == null) return null; return LNode.Call(CodeSymbols.Splice, LNode.List(list.Select(namespc => (LNode) LNode.Call(CodeSymbols.Import, LNode.List(namespc))))); } catch (LogException exc) { exc.Msg.WriteTo(context.Sink); } } return null; }
public static LNode UsingMulti(LNode stmt, IMacroContext context) { { LNode multiNamespace; if (stmt.Calls(CodeSymbols.Import, 1) && (multiNamespace = stmt.Args[0]) != null) try { var list = GetNamespaces(stmt[0]); if (list == null) return null; return LNode.Call(CodeSymbols.Splice, LNode.List(list.Select(namespc => (LNode) LNode.Call(CodeSymbols.Import, LNode.List(namespc))))); } catch (LogException exc) { exc.Msg.WriteTo(context.Sink); } } return null; }
public static LNode NullDot(LNode node, IMessageSink sink) { if (!node.Calls(S.NullDot, 2)) return null; var a = node.Args; LNode prefix = a[0], suffix = a[1]; // So our input will be something like a.b?.c().d<x>, which is parsed // (a.b) ?. (c().d<x>) // in EC# we would transform this to // a.b::tmp != null ? tmp.c().d<x> : null // but there's no EC# compiler yet, so instead use code that plain C# // can support: // a.b != null ? (a.b).c().d<x> : null return F.Call(S.QuestionMark, F.Call(S.Neq, prefix, F.@null), ConvertToNormalDot(prefix, suffix), F.Null); }
[LexicalMacro("e.g. alt class Pair<A,B> { alt this(A Item1, B Item2); }", "Expands a short description of an 'algebraic data type' into a set of classes with a common base class. " + "All data members are read-only, and for each member (e.g. Item1 and Item2 above), " + "a With() method is generated to let users create modified versions.", "#class", Mode = MacroMode.Passive | MacroMode.Normal)] public static LNode AlgebraicDataType(LNode classDecl, IMacroContext context) { int i; { LNode baseName; VList<LNode> attrs, baseTypes, body; if ((attrs = classDecl.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && classDecl.Calls(CodeSymbols.Class, 3) && (baseName = classDecl.Args[0]) != null && classDecl.Args[1].Calls(CodeSymbols.AltList) && classDecl.Args[2].Calls(CodeSymbols.Braces)) { baseTypes = classDecl.Args[1].Args; body = classDecl.Args[2].Args; attrs = attrs.RemoveAt(i); var adt = new AltType(attrs, baseName, baseTypes, null); adt.ScanClassBody(body); var output = new VList<LNode>(); adt.GenerateOutput(ref output); return LNode.Call(CodeSymbols.Splice, LNode.List(output)); } } return null; }
public static LNode AlgebraicDataType(LNode classDecl, IMacroContext context) { int i; { LNode baseName; VList<LNode> attrs, baseTypes, body; if ((attrs = classDecl.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && classDecl.Calls(CodeSymbols.Class, 3) && (baseName = classDecl.Args[0]) != null && classDecl.Args[1].Calls(CodeSymbols.AltList) && classDecl.Args[2].Calls(CodeSymbols.Braces)) { baseTypes = classDecl.Args[1].Args; body = classDecl.Args[2].Args; attrs = attrs.RemoveAt(i); var adt = new AltType(attrs, baseName, baseTypes, null); adt.ScanClassBody(body); var output = new VList<LNode>(); adt.GenerateOutput(ref output); return LNode.Call(CodeSymbols.Splice, LNode.List(output)); } } return null; }
[LexicalMacro("e.g. alt class Tree<T> { alt Node(Tree<T> Left, Tree<T> Right); alt Leaf(T Value); }", "Expands a short description of an 'algebraic data type' into a set of classes with a common base class.", "#class", Mode = MacroMode.Passive | MacroMode.Normal)] public static LNode AlgebraicDataType(LNode classDecl, IMacroContext context) { int i; { LNode baseName; RVList<LNode> attrs, baseTypes, body; if ((attrs = classDecl.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && classDecl.Calls(CodeSymbols.Class, 3) && (baseName = classDecl.Args[0]) != null && classDecl.Args[1].Calls(CodeSymbols.AltList) && classDecl.Args[2].Calls(CodeSymbols.Braces)) { baseTypes = classDecl.Args[1].Args; body = classDecl.Args[2].Args; attrs = attrs.RemoveAt(i); var adt = new AltType(attrs, baseName, baseTypes, null); adt.ScanClassBody(body); var output = new RVList<LNode>(); adt.GenerateOutput(ref output); return LNode.Call(CodeSymbols.Splice, new RVList<LNode>(output)); } } return null; }
public static LNode replace(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); var args = args_body.A; var body = args_body.B; if (args.Count == 1 && args[0].Calls(S.Tuple)) args = args[0].Args; // LESv2 if (args.Count >= 1) { bool preprocess = node.Calls("replacePP"); var patterns = new Pair<LNode, LNode>[args.Count]; for (int i = 0; i < patterns.Length; i++) { var pair = args[i]; if (pair.Calls(S.Lambda, 2)) { LNode pattern = pair[0], repl = pair[1]; if (preprocess) { pattern = context.PreProcess(pattern); repl = context.PreProcess(repl); } if (pattern.Calls(S.Braces, 1) && repl.Calls(S.Braces)) { pattern = pattern.Args[0]; repl = repl.WithTarget(S.Splice); } patterns[i] = Pair.Create(pattern, repl); } else { string msg = "Expected 'pattern => replacement'."; if (pair.Descendants().Any(n => n.Calls(S.Lambda, 2))) msg += " " + "(Using '=>' already? Put the pattern on the left-hand side in parentheses.)"; return Reject(context, pair, msg); } } int replacementCount; var output = Replace(body, patterns, out replacementCount); if (replacementCount == 0) context.Write(Severity.Warning, node, "No patterns recognized; no replacements were made."); return output.AsLNode(S.Splice); } return null; }
// Decides whether a value should be placed in a temporary variable // if it is evaluated more than once by a macro. This returns true for // non-uppercase identifiers like "x" or "_Foo", for literals like 42, // and for dotted expressions in which all components are non-uppercase // identifiers, e.g. foo.bar.baz. Returns false for everything else. internal protected static bool LooksLikeSimpleValue(LNode value) { if (value.IsCall) { if (value.Calls(S.Dot)) { if (value.ArgCount == 1) value = value.Args[0]; else if (value.ArgCount == 2) { if (!LooksLikeSimpleValue(value.Args[0])) return false; value = value.Args[1]; } else return false; } else return false; } if (value.IsId) return !char.IsUpper(value.Name.Name.TryGet(0, '\0')); if (value.IsLiteral) return true; return false; }
protected override void Stmt(string result, LNode input, Action<EcsPrinterOptions> configure = null, Mode mode = Mode.Both) { bool exprMode = (mode & Mode.Expression) != 0; if ((mode & Mode.PrinterTest) == 0) return; var options = new EcsPrinterOptions(); options.IndentString = " "; // by default, test the mode that is more difficult to get right options.AllowChangeParentheses = false; // TODO: make round tripping work without this options.NewlineOptions &= ~(NewlineOpt.AfterOpenBraceInNewExpr | NewlineOpt.BeforeCloseBraceInNewExpr); if (configure != null) configure(options); var sb = new StringBuilder(); var mode2 = exprMode ? ParsingMode.Expressions : ParsingMode.Statements; if (input.Calls(S.Splice)) EcsLanguageService.Value.Print(input.Args, sb, MessageSink.Default, mode2, options); else EcsLanguageService.Value.Print(input, sb, MessageSink.Default, mode2, options); AreEqual(result, sb.ToString()); }
private bool SimpleEnoughToRepeat(LNode code) { Debug.Assert(code.Calls(S.Braces)); // a simple heuristic if (code.ArgCount > 1) return false; return code.ArgCount == 1 && !code.Args[0].Calls(S.If) && code.ArgNamed(S.Braces) == 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), [#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; }
static LNode VarName(LNode varStmt) { if (varStmt.Calls(S.Var, 2)) { var nameAndInit = varStmt.Args[1]; if (nameAndInit.Calls(S.Assign, 2)) return nameAndInit.Args[0]; else return nameAndInit; } return null; }