예제 #1
0
 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);
 }
예제 #2
0
        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());
        }
예제 #3
0
            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);
                }
            }
예제 #4
0
        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);
        }
예제 #5
0
            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);
                }
            }
예제 #6
0
        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);
        }