public static LNode methodStyleMacro(LNode node, IMacroContext context) { if (EcsValidators.MethodDefinitionKind(node, out var defineKw, out var macroName, out var args, out var body) == S.Fn && body != null && defineKw.IsIdNamed("macro")) { var pattern = args.WithTarget(macroName); return(CompileMacro(pattern, body, context, node.Attrs)); } return(null); }
public static LNode replaceFn(LNode node, IMacroContext context1) { var retType = node.Args[0, LNode.Missing].Name; if (retType != _replace && retType != _define) { return(null); } LNode replaceKw, macroName, args, body; if (EcsValidators.MethodDefinitionKind(node, out replaceKw, out macroName, out args, out body, allowDelegate: false) != S.Fn || body == null) { return(null); } MacroMode mode, modes = 0; var leftoverAttrs = node.Attrs.SmartWhere(attr => { if (attr.IsId && Loyc.Compatibility.EnumStatic.TryParse(attr.Name.Name, out mode)) { modes |= mode; return(false); } return(true); }); LNode pattern = F.Call(macroName, args.Args).PlusAttrs(leftoverAttrs); LNode replacement = body.AsList(S.Braces).AsLNode(S.Splice).PlusAttrs(replaceKw.Attrs); replacement.Style &= ~NodeStyle.OneLiner; WarnAboutMissingDollarSigns(args, context1, pattern, replacement); // Note: we could fill out the macro's Syntax and Description with the // pattern and replacement converted to strings, but it's generally a // waste of CPU time as those strings are usually not requested. var lma = new LexicalMacroAttribute( string.Concat(macroName.Name, "(", args.Args.Count.ToString(), " args)"), "", macroName.Name.Name); var macroInfo = new MacroInfo(null, lma, (candidate, context2) => { MMap <Symbol, LNode> captures = new MMap <Symbol, LNode>(); VList <LNode> unmatchedAttrs; if (candidate.MatchesPattern(pattern, ref captures, out unmatchedAttrs)) { return(ReplaceCaptures(replacement, captures).PlusAttrsBefore(unmatchedAttrs)); } return(null); }) { Mode = modes }; context1.RegisterMacro(macroInfo); return(F.Splice()); }
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); } }
Pred NodeToPredCore(LNode expr, Context ctx = Context.Rule) { if (expr.IsCall) { bool slash = false, not; var name = expr.Name; if (name == S.Tuple) { // sequence: (a, b, c) if (expr.Calls(S.Tuple, 1)) { return(NodeToPred(expr.Args[0], ctx)); } return(TranslateToSeq(expr, ctx)); } else if (name == S.Braces) { // User action {block} if (ctx == Context.And || ctx == Context.GateLeft) { _sink.Error(expr, ctx == Context.And ? "Cannot use an action block inside an '&' or '&!' predicate; these predicates are for prediction only." : "Cannot use an action block on the left side of a '=>' gate; the left side is for prediction only."); } return(new ActionPred(expr, expr.Args)); } else if (expr.Calls(S.OrBits, 2) || (slash = expr.Calls(S.Div, 2))) { // alternatives: a | b, a || b, a / b LNode lhs = expr.Args[0], rhs = expr.Args[1]; BranchMode lhsMode, rhsMode; Pred left = BranchToPred(lhs, out lhsMode, ctx); Pred right = BranchToPred(rhs, out rhsMode, ctx); return(Pred.Or(left, right, slash, expr, lhsMode, rhsMode, _sink)); } else if (expr.Calls(_Star, 1) || expr.Calls(_Plus, 1) || expr.Calls(_Opt, 1)) { // loop (a+, a*) or optional (a?) return(TranslateLoopExpr(expr, ctx)); } else if (expr.Calls(_Gate, 1) || expr.Calls(_EqGate, 1)) { // => foo (LES-based parser artifact) return(new Gate(expr, new Seq(F.Missing), NodeToPred(expr.Args[0], Context.GateRight)) { IsEquivalency = expr.Calls(_EqGate) }); } else if (expr.Calls(_Gate, 2) || expr.Calls(_EqGate, 2)) { if (ctx == Context.GateLeft) { _sink.Error(expr, "Cannot use a gate in the left-hand side of another gate"); } return(new Gate(expr, NodeToPred(expr.Args[0], Context.GateLeft), NodeToPred(expr.Args[1], Context.GateRight)) { IsEquivalency = expr.Calls(_EqGate) }); } else if ((not = expr.Calls(_AndNot, 1)) || expr.Calls(_And, 1)) { return(TranslateAndPred(expr, not)); } else if (expr.Calls(S.NotBits, 1)) { var subpred = NodeToPred(expr.Args[0], ctx); if (subpred is TerminalPred) { var term = (TerminalPred)subpred; term.Set = term.Set.Inverted().WithoutEOF(); return(term); } else { _sink.Error(expr, "The set-inversion operator ~ can only be applied to a single terminal, not a '{0}'", subpred.GetType().Name); return(subpred); } } else if ((name.Name.EndsWith(":") || name.Name.EndsWith("=")) && expr.ArgCount == 2) { return(TranslateLabeledExpr(expr, ctx)); } else if (expr.Calls(_Any, 2) && expr.Args[0].IsId) { return(Translate_any_in_Expr(expr, ctx)); } } // expr is an Id, literal, or non-special call Rule rule = TryGetRule(expr); if (rule != null) { LNode _, args; if (EcsValidators.MethodDefinitionKind(rule.Basis, out _, out _, out args, out _, false) == S.Fn) { if (expr.ArgCount > args.ArgCount) // don't complain about too few args, in case there are default args (I'm too lazy to check) { _sink.Error(expr, "Rule '{0}' takes {1} arguments ({2} given)", rule.Name, args.ArgCount, expr.ArgCount); } } return(new RuleRef(expr, rule) { Params = expr.Args }); } string errorMsg = null; Pred terminal = _helper.CodeToTerminalPred(expr, ref errorMsg); if (terminal == null) { errorMsg = errorMsg ?? "LLLPG: unrecognized expression"; terminal = new TerminalPred(expr, _helper.EmptySet); _sink.Error(expr, errorMsg); } else if (errorMsg != null) { _sink.Warning(expr, errorMsg); } return(terminal); }
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); } }
private static object RunCSharpCodeWithRoslyn(LNode parent, LNodeList code, IMacroContext context, ParsingMode printMode = null) { // Note: when using compileTimeAndRuntime, the transforms here affect the version // sent to Roslyn, but not the runtime version placed in the output file. code = code.SmartSelectMany(stmt => { // Ensure #r gets an absolute path; I don't know what Roslyn does with a // relative path (maybe WithMetadataResolver would let me control this, // but it's easier not to) if ((stmt.Calls(S.CsiReference, 1) || stmt.Calls(S.CsiLoad, 1)) && stmt[0].Value is string fileName) { fileName = fileName.Trim().WithoutPrefix("\"").WithoutSuffix("\""); var inputFolder = context.ScopedProperties.TryGetValue((Symbol)"#inputFolder", "").ToString(); var fullPath = Path.Combine(inputFolder, fileName); return(LNode.List(stmt.WithArgChanged(0, stmt[0].WithValue("\"" + fullPath + "\"")))); } // For each (top-level) LexicalMacro method, call #macro_context.RegisterMacro(). LNode attribute = null; if ((attribute = stmt.Attrs.FirstOrDefault( attr => AppearsToCall(attr, "LeMP", nameof(LexicalMacroAttribute).WithoutSuffix("Attribute")) || AppearsToCall(attr, "LeMP", nameof(LexicalMacroAttribute)))) != null && EcsValidators.MethodDefinitionKind(stmt, out _, out var macroName, out _, out _) == S.Fn) { var setters = SeparateAttributeSetters(ref attribute); attribute = attribute.WithTarget((Symbol)nameof(LexicalMacroAttribute)); setters.Insert(0, attribute); var newAttribute = F.Call(S.New, setters); var registrationCommand = F.Call(F.Dot(__macro_context, nameof(IMacroContext.RegisterMacro)), F.Call(S.New, F.Call(nameof(MacroInfo), F.Null, newAttribute, macroName))); return(LNode.List(stmt, registrationCommand)); } return(LNode.List(stmt)); }); var outputLocationMapper = new LNodeRangeMapper(); var options = new LNodePrinterOptions { IndentString = " ", SaveRange = outputLocationMapper.SaveRange }; string codeText = EcsLanguageService.WithPlainCSharpPrinter.Print(code, context.Sink, printMode, options); _roslynSessionLog?.WriteLine(codeText); _roslynSessionLog?.Flush(); _roslynScriptState.GetVariable(__macro_context_sanitized).Value = context; try { // Allow users to write messages via MessageSink.Default using (MessageSink.SetDefault(new MessageSinkFromDelegate((sev, ctx, msg, args) => { _roslynSessionLog?.Write("{0} from user ({1}): ", sev, MessageSink.GetLocationString(ctx)); _roslynSessionLog?.WriteLine(msg, args); context.Sink.Write(sev, ctx, msg, args); }))) { _roslynScriptState = _roslynScriptState.ContinueWithAsync(codeText).Result; } return(_roslynScriptState.ReturnValue); } catch (CompilationErrorException e) when(e.Diagnostics.Length > 0 && e.Diagnostics[0].Location.IsInSource) { // Determine the best location in the source code at which to report the error. // Keep in mind that the error may have occurred in a synthetic location not // found in the original code, and we cannot report such a location. Microsoft.CodeAnalysis.Text.TextSpan range = e.Diagnostics[0].Location.SourceSpan; var errorLocation = new IndexRange(range.Start, range.Length); var mappedErrorLocation = outputLocationMapper.FindRelatedNodes(errorLocation, 10) .FirstOrDefault(p => !p.A.Range.Source.Text.IsEmpty); string locationCaveat = ""; if (mappedErrorLocation.A != null) { bool mappedIsEarly = mappedErrorLocation.B.EndIndex <= errorLocation.StartIndex; if (mappedIsEarly || mappedErrorLocation.B.StartIndex >= errorLocation.EndIndex) { locationCaveat = "; " + "The error occurred at a location ({0}) that doesn't seem to exist in the original code.".Localized( mappedIsEarly ? "after the location indicated".Localized() : "before the location indicated".Localized()); } } // Extract the line where the error occurred, for inclusion in the error message int column = e.Diagnostics[0].Location.GetLineSpan().StartLinePosition.Character; int lineStart = range.Start - column; int lineEnd = codeText.IndexOf('\n', lineStart); if (lineEnd < lineStart) { lineEnd = codeText.Length; } string line = codeText.Substring(lineStart, lineEnd - lineStart); string errorMsg = e.Message + " - in «{0}»{1}".Localized(line, locationCaveat); context.Sink.Error(mappedErrorLocation.A ?? parent, errorMsg); LogRoslynError(e, context.Sink, parent, compiling: true); } catch (Exception e) { while (e is AggregateException ae && ae.InnerExceptions.Count == 1) { e = ae.InnerExceptions[0]; } context.Sink.Error(parent, "An exception was thrown from your code:".Localized() + " " + e.ExceptionMessageAndType()); LogRoslynError(e, context.Sink, parent, compiling: false); } return(NoValue.Value); }