public string TryMatch(TokenType token) { ParserUtil.IteratePastWhitespaceAndComments(source, ref Iterator); int len = token.Match(source, Iterator); if (len < 0) { return(null); } Iterator += len; return(source.Substring(Iterator - len, len)); }
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); }
// 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); }
static ParserSetup() { // Create our language grammar TokenType emptyToken = new TokenType("~", (str, b) => 0); TokenType ifToken = new TokenType("if", "^(if)"); TokenType elseToken = new TokenType("else", "^(else)"); TokenType forToken = new TokenType("for", "^(for)"); TokenType flowControlToken = new TokenType("break", "^(break|continue|return)"); TokenType paramStartToken = new TokenType("(", "^[(]"); TokenType paramEndToken = new TokenType(")", "^[)]"); TokenType blockStartToken = new TokenType("{", "^[{]"); TokenType blockEndToken = new TokenType("}", "^[}]"); TokenType typeToken = new TokenType("type", "^(int|float|double|bool|string|void|class|fn|var)"); TokenType nameToken = new TokenType("name", "^[a-zA-Z_][a-zA-Z0-9_]*"); TokenType numberToken = new TokenType("const", "^[0-9]+(\\.[0-9]*)?[f]?"); TokenType stringToken = new TokenType("const", "^\"(\\.|[^\"])*\""); TokenType boolToken = new TokenType("const", "^(true|false)"); TokenType charToken = new TokenType("const", "^['][\\\\]?.[']"); TokenType commaToken = new TokenType(",", "^[,]"); TokenType endStatementToken = new TokenType(";", "^[;]"); TokenType assignToken = new TokenType("=", "^[=]"); TokenType andOrToken = new TokenType("oper", "^(&&|\\|\\|)"); TokenType mulDivToken = new TokenType("MulDiv", "^[*/]"); TokenType addSubToken = new TokenType("AddSub", "^[+\\-]"); TokenType operatorToken = new TokenType("oper", "^[*+\\-/^]"); TokenType comparitorToken = new TokenType("compar", "^[<>=][=]?"); TokenType unaryToken = new TokenType("unary", "^[+-]"); TokenType increment = new TokenType("incr", "^(\\+\\+|\\-\\-)"); GrammarRule emptyRule = new GrammarRule("Empty", emptyToken); //GrammarRule semicolonRule = new GrammarRule("Semicolon", endStatementToken); GrammarRule variableRule = new GrammarRule("Variable"); GrammarRule constantRule = new GrammarRule("Constant"); GrammarRule ifRule = new GrammarRule("If"); GrammarRule forRule = new GrammarRule("For"); GrammarRule flowControlRule = new GrammarRule("Flow", flowControlToken); GrammarRule blockOrStatementRule = new GrammarRule("BOS"); GrammarRule statementRule = new GrammarRule("Statement"); GrammarRule assignmentRule = new GrammarRule("Assignment"); GrammarRule vAssignmentRule = new GrammarRule("VAssignment"); GrammarRule callRule = new GrammarRule("Call"); GrammarRule termRule = new GrammarRule("Term"); GrammarRule equationRule = new GrammarRule("Equation"); GrammarRule andOrRule = new GrammarRule("AndOr"); GrammarRule mulDivRule = new GrammarRule("MulDiv"); GrammarRule addSubRule = new GrammarRule("AddSub"); GrammarRule compareRule = new GrammarRule("Compare"); GrammarRule paramRule = new GrammarRule("Parameter"); GrammarRule parmsRule = new GrammarRule("Parameters"); GrammarRule vDeclrRule = new GrammarRule("VDeclr"); GrammarRule fDeclrRule = new GrammarRule("FDeclr"); blockRule = new GrammarRule("Block"); GrammarRule rValueRule = new GrammarRule("RValue", equationRule); GrammarRule conditionRule = new GrammarRule("Condition", equationRule); constantRule.SetElements(numberToken | stringToken | charToken | boolToken); variableRule.SetElements(nameToken); // TODO: ++ operator should be here blockOrStatementRule.SetElements((blockStartToken & blockRule & blockEndToken) | (statementRule)); GrammarRule elseRule = null; ifRule.SetElements(ifToken & paramStartToken & conditionRule & paramEndToken & blockOrStatementRule & ((elseRule = ( elseToken & ( blockOrStatementRule | ifRule ) )) | emptyRule) ); GrammarRule forConditionRule = null; forRule.SetElements(forToken & paramStartToken & (statementRule | emptyRule) & (forConditionRule = ((comparitorToken & equationRule) | emptyRule)) & paramEndToken & blockOrStatementRule ); assignmentRule.SetElements(assignToken & rValueRule); vAssignmentRule.SetElements(nameToken & assignmentRule); callRule.SetElements(nameToken & paramStartToken & parmsRule & paramEndToken); GrammarRule unaryRule = null; termRule.SetElements( unaryRule = ((unaryToken | emptyRule) & ( (increment & variableRule) | (variableRule & increment) | callRule | constantRule | variableRule | (paramStartToken & equationRule & paramEndToken) )) ); GrammarRule andOrOperRule = null; GrammarRule mulDivOperRule = null; GrammarRule addSubOperRule = null; GrammarRule compOperRule = null; andOrRule.SetElements(compareRule & ((andOrOperRule = andOrToken & andOrRule) | emptyRule)); mulDivRule.SetElements(termRule & ((mulDivOperRule = mulDivToken & mulDivRule) | emptyRule)); addSubRule.SetElements(mulDivRule & ((addSubOperRule = addSubToken & addSubRule) | emptyRule)); compareRule.SetElements(addSubRule & ((compOperRule = comparitorToken & compareRule) | emptyRule)); equationRule.SetElements(andOrRule); paramRule.SetElements(rValueRule); parmsRule.SetElements( (paramRule & (commaToken & paramRule)[0, 1000]) | emptyRule ); vDeclrRule.SetElements(typeToken & nameToken & (assignmentRule | emptyToken)); GrammarRule pDeclrRule = new GrammarRule("PDeclr"); GrammarRule pArrRule = new GrammarRule("PArr"); pDeclrRule.SetElements(typeToken & nameToken & (assignmentRule | emptyToken)); pArrRule.SetElements(pDeclrRule & ((commaToken & pArrRule) | emptyToken)); fDeclrRule.SetElements(typeToken & nameToken & paramStartToken & (pArrRule | typeToken | emptyRule) & paramEndToken & blockStartToken & blockRule & blockEndToken ); GrammarRule nerfReturnRules = ( (fDeclrRule) | (vDeclrRule & endStatementToken) | (vAssignmentRule & endStatementToken) | (callRule & endStatementToken) | (termRule & endStatementToken) ); statementRule.SetElements( ifRule | forRule | (flowControlRule & endStatementToken) | nerfReturnRules ); blockRule.SetElements(new GrammarRule(statementRule)[0, 1000]); int uid = 0; // Setup how these grammars are built into instructions unaryRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string unary = source.TryMatch(unaryToken); context.PassThrough(); if (unary == "-") { context.Store.Add(new InstructionItem("Constant", "int", -1, source.From)); context.Store.Add(new InstructionItem("Mul", "auto", "unary", source.From)); } }; constantRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string data = source.SourceCode; if (numberToken.Match(data, 0) >= 0) { if (data.Contains('.')) { context.Store.Add(new InstructionItem("Constant", "real", XReal.Parse(data), source.From)); } else { context.Store.Add(new InstructionItem("Constant", "int", int.Parse(data), source.From)); } } else if (stringToken.Match(data, 0) >= 0) { context.Store.Add(new InstructionItem("Constant", "string", ParserUtil.UnescapeString(data), source.From)); } else if (boolToken.Match(data, 0) >= 0) { context.Store.Add(new InstructionItem("Constant", "bool", data == "true", source.From)); } else if (charToken.Match(data, 0) >= 0) { context.Store.Add(new InstructionItem("Constant", "char", data[1], source.From)); } }; variableRule.Build += delegate(ParseContext.Level context, SourceIterator source) { context.Store.Add(new InstructionItem("Variable", "auto", source.Match(nameToken), source.From)); }; vDeclrRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string type = ParserUtil.GetCleanTypeName(source.Match(typeToken)); string name = source.Match(nameToken); context.Store.Add(new InstructionItem("+Allocate", type, name, source.From)); context.Store.Add(new InstructionItem("Reference", type + "*", name, source.From)); // Needs to be here because it gets popped if not used if (context.Children.Count > 0) { context.PassThrough(); //context.Store.Add(new InstructionItem("Conform", type, type)); context.Store.Add(new InstructionItem("Assign", type, "~" + name, source.From)); } }; nerfReturnRules.Build += delegate(ParseContext.Level context, SourceIterator source) { context.PassThrough(); context.Store.Add(new InstructionItem("Pop", "void", null, source.From)); }; vAssignmentRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string name = source.Match(nameToken); context.Store.Add(new InstructionItem("Reference", "auto*", name, source.From)); context.PassThrough(); //context.Store.Add(new InstructionItem("Conform", "auto", "auto", source.From)); context.Store.Add(new InstructionItem("Assign", "auto", "~" + name, source.From)); }; Func <string, int, InstructionItem> GetInstructionFromString = (oper, loc) => { switch (oper) { case "&&": return(new InstructionItem("And", "bool", oper, loc)); case "||": return(new InstructionItem("Or", "bool", oper, loc)); case "<=": return(new InstructionItem("LEqual", "bool", oper, loc)); case ">=": return(new InstructionItem("GEqual", "bool", oper, loc)); case "<": return(new InstructionItem("Less", "bool", oper, loc)); case ">": return(new InstructionItem("Greater", "bool", oper, loc)); case "==": return(new InstructionItem("IsEqual", "bool", oper, loc)); case "!=": return(new InstructionItem("IsNEqual", "bool", oper, loc)); case "+": return(new InstructionItem("Add", "auto", oper, loc)); case "-": return(new InstructionItem("Sub", "auto", oper, loc)); case "*": return(new InstructionItem("Mul", "auto", oper, loc)); case "/": return(new InstructionItem("Div", "auto", oper, loc)); } return(new InstructionItem("+Error", "auto", "Unable to find operator for '" + oper + "'", loc)); }; andOrOperRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string oper = source.TryMatch(andOrToken); context.PassThrough(); context.Store.Add(GetInstructionFromString(oper, source.From)); }; mulDivOperRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string oper = source.TryMatch(operatorToken); context.PassThrough(); context.Store.Add(GetInstructionFromString(oper, source.From)); }; addSubOperRule.Build += mulDivOperRule.Build; compOperRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string oper = source.TryMatch(comparitorToken); context.PassThrough(); context.Store.Add(GetInstructionFromString(oper, source.From)); }; paramRule.Build += delegate(ParseContext.Level context, SourceIterator source) { context.Store.Add(new InstructionItem("+Param", null, null, source.From)); context.PassThrough(); }; callRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string name = source.Match(nameToken); int paramC = 0; for (int c = 0; c < context.Children.Count; ++c) { var child = context.Children[c]; if (child.Command == "+Param") { paramC++; } else { context.Store.Add(child); } } //context.PassThrough(); context.Store.Add(new InstructionItem("Call" + paramC, "auto", name, source.From)); }; statementRule.Build += delegate(ParseContext.Level context, SourceIterator source) { /*for (int c = 0; c < context.Children.Count; ++c) { * Debug.Assert(context.Children[c].Value != null, * "All statements should be converted to instructions!"); * }*/ context.PassThrough(); //context.Store.Add(new InstructionItem("RemoveItemIfNotVoid", "void", null, source.From)); }; conditionRule.Build += delegate(ParseContext.Level context, SourceIterator source) { context.PassThrough(); context.Store.Add(new InstructionItem("+CondEnd", source.From)); }; pDeclrRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string type = ParserUtil.GetCleanTypeName(source.Match(typeToken)); string name = source.Match(nameToken); FInternal.Parameter param = new FInternal.Parameter(); if (context.Children.Count > 0) { Debug.Assert(context.Children.Count == 1 && context.Children[0].Command == "Constant", "Parameter types must be constant"); param.Default = context.Children[0].Value; } param.Name = name; context.Store.Add(new InstructionItem("+Parameter", type, param, source.From)); // Do nothing }; fDeclrRule.Build += delegate(ParseContext.Level context, SourceIterator source) { string type = ParserUtil.GetCleanTypeName(source.Match(typeToken)); string name = source.Match(nameToken); int id = (++uid); context.Store.Add(new InstructionItem("+Allocate", "fn", name, source.From)); context.Store.Add(new InstructionItem("Reference", "fn*", name, source.From)); var fn = new FInternal() { Name = name + id }; List <FInternal.Parameter> parms = new List <FInternal.Parameter>(); context.Store.Add(new InstructionItem("Function", "fn", fn, source.From)); context.Store.Add(new InstructionItem("Marker", "void", name + id + "_start", source.From)); for (int c = 0; c < context.Children.Count; ++c) { var child = context.Children[c]; if (child.Command == "+Parameter") { parms.Add((FInternal.Parameter)child.Value); } else if (child.Command == "+Return") { context.Store.Add(new InstructionItem("Constant", "int", 0, source.From)); context.Store.Add(new InstructionItem("Return", "fn", name + id, source.From)); } else { context.Store.Add(child); } } fn.Parameters = parms.ToArray(); //context.PassThrough(); context.Store.Add(new InstructionItem("Constant", "int", 0, source.From)); context.Store.Add(new InstructionItem("Return", "fn", name + id, source.From)); context.Store.Add(new InstructionItem("Marker", "void", name + id + "_end", source.From)); context.Store.Add(new InstructionItem("Assign", "fn", "~" + name, source.From)); }; forConditionRule.Build += (level, source) => { level.Store.Add(new InstructionItem("+ContBeg", source.From)); string comparison = source.TryMatch(comparitorToken); level.PassThrough(); if (comparison != null) { level.Store.Add(GetInstructionFromString(comparison, source.From)); } level.Store.Add(new InstructionItem("+ContEnd", source.From)); }; forRule.Build += delegate(ParseContext.Level context, SourceIterator source) { int id = (++uid); int contBeg = context.FindIndexInChildrenOnce("+ContBeg"); int contEnd = context.FindIndexInChildrenOnce("+ContEnd"); int varRef = context.FindIndexInChildrenOnce("Reference", contBeg); Debug.Assert(varRef >= 0, "A single assignment should exist before the condition"); var varRefI = context.Children[varRef]; string varName = (string)varRefI.Value; string varType = ParserUtil.StripStars(varRefI.Type); int arAloc = context.FindIndexInChildrenOnce("Allocate", varRef); if (arAloc >= 0) { Debug.Assert(context.Children[arAloc].Value.Equals(varRefI.Value), "Assumption that the allocation and reference will always be the same value"); } else { if (varType == "auto") { varType = "int"; } context.Store.Add(new InstructionItem("+AllocateIfRequired", varType, varName, source.From)); } context.PassRange(0, contBeg); context.Store.Add(new InstructionItem("Marker", "void", "for" + id + "_beg", source.From)); context.Store.Add(new InstructionItem("Variable", varType, varName, source.From)); context.PassRange(contBeg + 1, contEnd); context.Store.Add(new InstructionItem("JumpIfFalse", "void", "for" + id + "_end", source.From)); for (int c = contEnd + 1; c < context.Children.Count; ++c) { var child = context.Children[c]; if (child.Command == "+Break") { child = new InstructionItem("JumpTo", "void", "for" + id + "_end", child.Location); } else if (child.Command == "+Continue") { child = new InstructionItem("JumpTo", "void", "for" + id + "_inc", child.Location); } context.Store.Add(child); } //context.PassRange(contEnd + 1, context.Children.Count); context.Store.Add(new InstructionItem("Marker", "void", "for" + id + "_inc", source.From)); context.Store.Add(new InstructionItem("Reference", "int*", varRefI.Value, source.From)); context.Store.Add(new InstructionItem("Variable", "int", varRefI.Value, source.From)); context.Store.Add(new InstructionItem("Constant", "int", 1, source.From)); context.Store.Add(new InstructionItem("Add", "int", "", source.From)); context.Store.Add(new InstructionItem("Assign", "int", "~" + (string)varRefI.Value, source.From)); context.Store.Add(new InstructionItem("Pop", "void", "", source.From)); context.Store.Add(new InstructionItem("JumpTo", "void", "for" + id + "_beg", source.From)); context.Store.Add(new InstructionItem("Marker", "void", "for" + id + "_end", source.From)); }; flowControlRule.Build += delegate(ParseContext.Level context, SourceIterator source) { Debug.Assert(context.Children.Count == 0, "For control statements dont support child elements, yet."); context.PassThrough(); string flow = source.Match(flowControlToken); switch (flow) { case "break": context.Store.Add(new InstructionItem("+Break", source.From)); break; case "continue": context.Store.Add(new InstructionItem("+Continue", source.From)); break; case "return": context.Store.Add(new InstructionItem("+Return", source.From)); break; } }; ifRule.Build += delegate(ParseContext.Level context, SourceIterator source) { int id = (++uid); int condEnd = context.FindIndexInChildrenOnce("+CondEnd"); int elseStart = context.FindIndexInChildrenOnce("+Else"); int elseEnd = context.FindIndexInChildrenOnce("+EndElse"); context.PassRange(0, condEnd); context.Store.Add(new InstructionItem("JumpIfFalse", "void", "if" + id, source.From)); if (elseStart >= 0) { Debug.Assert(elseEnd >= 0, "Else needs to have a start and end!"); context.PassRange(condEnd + 1, elseStart); context.Store.Add(new InstructionItem("JumpTo", "void", "if_else" + id, source.From)); context.Store.Add(new InstructionItem("Marker", "void", "if" + id, source.From)); context.PassRange(elseStart + 1, elseEnd); context.Store.Add(new InstructionItem("Marker", "void", "if_else" + id, source.From)); } else { context.PassRange(condEnd + 1, context.Children.Count); context.Store.Add(new InstructionItem("Marker", "void", "if" + id, source.From)); } Debug.Assert(condEnd >= 0, "Unable to determine where condition ends!"); }; elseRule.Build += delegate(ParseContext.Level context, SourceIterator source) { int id = (++uid); context.Store.Add(new InstructionItem("+Else", "void", id, source.From)); context.PassThrough(); context.Store.Add(new InstructionItem("+EndElse", "void", id, source.From)); }; blockRule.Build += delegate(ParseContext.Level context, SourceIterator source) { var instructions = context.Children; var destination = context.Store; Dictionary <string, string> name2Type = new Dictionary <string, string>(); destination.Add(new InstructionItem("BlockStart", "void", null, source.From)); int destStart = destination.Count; for (int i = 0; i < instructions.Count; ++i) { var instr = instructions[i]; if (instr.Command == "+AllocateIfRequired") { Debug.Assert(instr.Type != "auto", "Cant allocate a variable of unknown type!"); Debug.Assert(instr.Value is String, "Variable names should be strings!"); string name = (String)instr.Value; if (!name2Type.ContainsKey(name)) { name2Type.Add(name, instr.Type); } } else if (instr.Command == "+Allocate") { Debug.Assert(instr.Type != "auto", "Cant allocate a variable of unknown type!"); Debug.Assert(instr.Value is String, "Variable names should be strings!"); string name = (String)instr.Value; if (!name2Type.ContainsKey(name)) { name2Type.Add(name, instr.Type); } } else if (instr.Command == "Assign") { string name = ((String)instr.Value).Substring(1); if (name2Type.ContainsKey(name)) { instr.Type = name2Type[name]; } //else instr.Type = "var"; destination.Add(instr); } else if (instr.Command == "Reference" || instr.Command == "Variable") { Debug.Assert(instr.Value is String, "Variable names should be strings!"); string name = (String)instr.Value; string stars = (instr.Command == "Variable" ? "" : "*"); if (instr.Type == "auto" + stars && name2Type.ContainsKey(name)) { instr.Type = name2Type[(String)instr.Value] + stars; } destination.Add(instr); } else if (instr.Command.StartsWith("Call") && instr.Type == "auto") { instr.Type = "var"; destination.Add(instr); } else { destination.Add(instr); } } foreach (var n2t in name2Type) { destination.Insert(destStart, new InstructionItem("Allocate", n2t.Value, n2t.Key, source.From)); } destination.Add(new InstructionItem("BlockEnd", "void", null, source.From)); }; }