예제 #1
0
        // Called recursively to generate tokens/instructions from a source code string
        public static IEnumerable<string> Parse(string source, ref int sourceI, GrammarElement element, ParseContext context)
        {
            IEnumerable<string> res = null;
            if (element is GrammarElementA) {
                var elA = element as GrammarElementA;
                int iter = sourceI;
                using (var level = context.GetCurrentContext()) {
                    for (int e = 0; e < elA.Elements.Length; ++e) {
                        var ok = Parse(source, ref iter, elA.Elements[e], context);
                        if (ok == null) {
                            level.RemoveChanges();
                            return null;
                        }
                        if (res == null) res = ok; else res = res.Concat(ok);
                    }
                }
                sourceI = iter;
            } else if (element is GrammarElementO) {
                var elA = element as GrammarElementO;
                for (int e = 0; e < elA.Elements.Length; ++e) {
                    int iter = sourceI;
                    var ok = Parse(source, ref iter, elA.Elements[e], context);
                    if (ok != null) { sourceI = iter; res = ok; break; }
                }
            } else if (element is GrammarElementT) {
                ParserUtil.IteratePastWhitespaceAndComments(source, ref sourceI);
                var elA = element as GrammarElementT;
                var token = elA.TokenType;
                int match = token.Match(source, sourceI);
                if (match < 0) {
                    context.MarkError(sourceI, token);
                    return null;
                }
                sourceI += match;
                return new string[] { token.Name };
            } else if (element is GrammarElementR) {
                var elA = element as GrammarElementR;
                var rule = elA.Rule;

                ParserUtil.IteratePastWhitespaceAndComments(source, ref sourceI);
                int sourceS = sourceI;
                int iter = sourceI;
                bool isImplicit = rule.Name == "Implicit";
                isImplicit = false;
                ParseContext.Level level = new ParseContext.Level();
                if (!isImplicit) {
                    context.PushRule(rule);
                    level = context.GetCurrentContext();
                }
                for (int r = 1; r <= rule.Cardinality.To; ++r) {
                    if (!isImplicit) level.ClearChildren();
                    var ok = Parse(source, ref iter, rule.Element, context);
                    if (ok != null) {
                        if (!isImplicit) {
                            if (rule.Build != null) {
                                SourceIterator sourceIter = new SourceIterator(source, sourceI, iter);
                                rule.Build(level, sourceIter);
                            } else {
                                level.PassThrough();
                            }
                        }
                        if (r >= rule.Cardinality.From) {
                            if (res == null) res = ok; else res = res.Concat(ok);
                            sourceI = iter;
                        }
                    } else break;
                }
                if (res == null && rule.Cardinality.From == 0) {
                    res = new string[] { };
                }
                if (!isImplicit) {
                    context.PopRule(rule);
                    if (res == null) level.RemoveChanges();
                    level.Dispose();
                }
            }
            return res;
        }
예제 #2
0
 public Level(ParseContext context, int level) {
     Context = context;
     Index = level;
     oldStoreStart = Context.GetStoreAt(Index).Count;
     oldChildStart = Context.GetStoreAt(Index + 1).Count;
 }
예제 #3
0
        public static InstructionInstance[] Parse(string source, Action<string, ErrorLevelE, int> onError)
        {
            int iter = 0;
            ParseContext context = new ParseContext();
            // Try to parse the code into rough tokens/instructions
            var res = Parse(source, ref iter, ParserSetup.BlockRule, context);
            ParserUtil.IteratePastWhitespaceAndComments(source, ref iter);
            // Was there more code that couldnt be parsed?
            if (res == null || iter < source.Length) {
                int line = source.Substring(0, context.MaxParseChar).Count(c => c == '\n') + 1;
                string lineData = null;
                int at = context.MaxParseChar;
                while (at > 0 && source[at - 1] != '\n') --at;
                int end = source.IndexOf('\n', at);
                while (end > 0 && char.IsWhiteSpace(source[end - 1])) --end;
                while (at < end && char.IsWhiteSpace(source[at])) ++at;
                if (end == -1) end = source.Length;
                lineData = source.Substring(at, end - at);
                onError(
                    "Unable to parse XScript at line " + line + " byte " + context.MaxParseChar + "\r\n" +
                    " " + lineData + "\r\n" +
                    " " + new string(Enumerable.Repeat(' ', context.MaxParseChar - at).ToArray()) + "^\r\n" +
                    "Expected:\r\n" +
                    context.ExpectedTokens.Select(k => "  As <" + k.Key.ToString() + ">:\r\n" +
                        "\t" + k.Value.Select(v => v.ToString()).Aggregate((s1, s2) => s1 + ", " + s2) + "\r\n"
                    ).Aggregate((s1, s2) => s1 + s2),
                    ErrorLevelE.Error,
                    line
                );
            }
            // Ensure no temporary instructions are still present (starting with '+')
            var parsedInstrs = context.GetCurrentContext().Children;
            for (int i = 0; i < parsedInstrs.Count; ++i) {
                var instr = parsedInstrs[i];
                if (instr.Command.StartsWith("+")) {
                    switch (instr.Command) {
                        case "+Error": onError((string)instr.Value, ErrorLevelE.Error, instr.Location.GetLineNumber(source)); break;
                        case "+Info": onError((string)instr.Value, ErrorLevelE.Info, instr.Location.GetLineNumber(source)); break;
                        case "+Warning": onError((string)instr.Value, ErrorLevelE.Warning, instr.Location.GetLineNumber(source)); break;
                        default: {
                            onError("Unexpected token " + instr.Command, ErrorLevelE.Error, instr.Location.GetLineNumber(source));
                        } break;
                    }
                }
            }
            // Begin matching internal instructions to tokens/instructions extracted
            // from the source string
            List<InstructionInstance> instrList = new List<InstructionInstance>();
            {
                var instrSet = StackBasedVM.Instructions.Set;
                List<StackType> stack = new List<StackType>();
                // Helper method to insert instructions into the instrList array
                // without putting them at the end
                Action<int, InstructionInstance> insertInstruction = (at, instr) => {
                    instrList.Insert(at, instr);
                    for (int s = 0; s < stack.Count; ++s) {
                        if (stack[s].InstructionId >= at) {
                            var stackT = stack[s];
                            stackT.InstructionId++;
                            stack[s] = stackT;
                        }
                    }
                };
                // Where to read instructions from
                for (int i = 0; i < parsedInstrs.Count; ++i) {
                    var instr = parsedInstrs[i];
                    // We get the instruction which best matches the parameters
                    // available on the stack, when the instruction is invoked
                    // (for type safety)
                    Instruction bestInstr = null;
                    int bestScore = 0;
                    foreach (var iSet in instrSet) {
                        var instrP = iSet.Value;
                        // Must match names
                        if (instrP.Name != instr.Command) continue;
                        // Calculate a score for how well the parameters match
                        int score = 100;
                        for (int p = 0; p < instrP.ConsumptionValues.Length; ++p) {
                            string paramType = instrP.ConsumptionValues[p];
                            string stackType = stack[stack.Count - 1 - p].Type;
                            score = score * ParserUtil.CompareConversion(stackType, paramType) / 100;
                            if (score <= bestScore) break;
                        }
                        // Is this score better than our previous score?
                        if (score <= bestScore) continue;
                        bestScore = score;
                        bestInstr = instrP;
                    }
                    // No instruction was found for this token/instruction
                    if (bestInstr == null) {
                        onError("Unable to find instruction for " + instr + "\r\n" +
                            (bestInstr != null ?
                                " Best match expected (" + bestInstr.ConsumptionValues.Aggregate((s1, s2) => s1 + ", " + s2) +
                                ") but was given (" + stack.Skip(stack.Count - bestInstr.ConsumptionValues.Length).Select(v => v.Type).Aggregate((s1, s2) => s1 + ", " + s2) + ")"
                            :
                                " Using params (" + stack.Skip(Math.Max(stack.Count - 3, 0)).Select(v => v.Type).Aggregate((s1, s2) => s1 + ", " + s2) + ")"
                            ),
                            ErrorLevelE.Error,
                            instr.Location.GetLineNumber(source)
                        );
                        break;
                    } else {
                        // Are any of the parameters available on the stack incorrect?
                        // Do they need to be cast to anoter type? (ie. float to int)
                        for (int p = 0; p < bestInstr.ConsumptionValues.Length; ++p) {
                            var stackT = stack[stack.Count - 1 - p];
                            var toConvInstr = instrList[stackT.InstructionId];
                            string paramType = bestInstr.ConsumptionValues[p];
                            string stackType = stackT.Type;
                            if (paramType == stackType) continue;
                            if (ParserUtil.StripStars(paramType) == "var") continue;
                            // Constants can be changed at compile-time
                            if (toConvInstr.Instruction.Name == "Constant") {
                                string type = ParserUtil.GetCleanTypeName(toConvInstr.Data.GetType().Name);
                                Debug.Assert(stackT.Type == type,
                                    "Stack type and constant value type must match!");
                                if (paramType == "int") {
                                    toConvInstr.Data = Convert.ToInt32(toConvInstr.Data);
                                    continue;
                                } else if (paramType == "real") {
                                    toConvInstr.Data = (XReal)Convert.ToSingle(toConvInstr.Data);
                                    continue;
                                }
                            }
                            // Otherwise fall back to the more expensive run-time conversion
                            int score = ParserUtil.CompareConversion(stackType, paramType);
                            if (score < 100) {
                                var convInstr = instrSet.FirstOrDefault(i2 => i2.Value.Type == paramType && i2.Value.Name == "Conform").Value;
                                if (convInstr == null) {
                                    onError("Unable to find conversion from " + stackType + " to " + paramType,
                                        ErrorLevelE.Warning,
                                        instr.Location.GetLineNumber(source));
                                    continue;
                                }
                                insertInstruction(stackT.InstructionId + 1, new InstructionInstance() {
                                    Instruction = convInstr,
                                    Data = stackType + "2" + paramType
                                });
                            }
                        }
                        // Simulate executing the instruction to maintain our
                        // faux stack, so next instructions can get the correct
                        // instruction for the expected types
                        Debug.Assert(bestInstr != null,
                            "Unable to find matching instruction!");
                        int toRemove = bestInstr.ConsumptionValues.Length;
                        string retVal = bestInstr.ReturnValues.Length > 0 ? bestInstr.ReturnValues[0] : "void";
                        int retStars = ParserUtil.CountStars(retVal);
                        if (retStars > 0) retVal = retVal.Substring(0, retVal.Length - retStars);
                        if (retVal == "var") {
                            retVal = ParserUtil.StripStars(instr.Type);
                            if (retVal == "auto") retVal = "var";
                        }
                        if (retVal == "var" && toRemove > 0)
                            retVal = ParserUtil.StripStars(stack[stack.Count - 1].Type);
                        else if (retStars > 0) retVal = ParserUtil.AddStars(retVal, retStars);
                        if (toRemove > 0) stack.RemoveRange(stack.Count - toRemove, toRemove);
                        if (retVal != "void") stack.Add(new StackType(instrList.Count, retVal));

                        instrList.Add(new InstructionInstance() {
                            Instruction = bestInstr,
                            Data = instr.Value,
                        });
                    }
                }
                Debug.Assert(stack.Count == 0,
                    "Stack is not balanced!");
            }
            // Our parser wraps the entire file in a block, remove this block
            // so that global variables are not cleaned up after execution
            if(instrList.Count > 0) {
                Debug.Assert(instrList[0].Instruction.Name == "BlockStart",
                    "Script should be contained within a block");
                Debug.Assert(instrList[instrList.Count - 1].Instruction.Name == "BlockEnd",
                    "Script should be contained within a block");
                instrList.RemoveAt(0);
                instrList.RemoveAt(instrList.Count - 1);
            }
            return instrList.ToArray();
            //context.BreakOnError = true;
            //res = Parse(str, ref iter, blockRule, context);
        }