void CompileBlockReturn(ParseTree ast, Types.Environment env) { var blockName = ast.Children.ElementAt (0); var result = ast.Children.ElementAt (1); this.CompileAst (blockName, env, true, true); this.CompileAst (result, env, true, true); this.Gen (Instruction.Opcodes.BlockReturn, null, ast); }
ParseTree WithAnchoredParseTree(int startPos, ParseTree.Labels label, Action<ParseTree> action) { ParseTree result = new ParseTree (); result.StartPos = startPos; result.Label = label; action (result); result.EndPos = this.LastToken ().EndPos; return result; }
static ParseTree GetFalseParseTree() { if (Parser.falseParseTree == null) { Parser.falseParseTree = new ParseTree () { Label = ParseTree.Labels.Bool, Content = "false", StartPos = -1, EndPos = -1 }; } return Parser.falseParseTree; }
// Gref is short for 'generic reference'. ParseTree ParseIdentifierOrCallOrRef(ParseTree forcedStart = null) { var start = forcedStart; if (start == null) { start = this.ParseParenthesizedOrName (); } if (this.TokenIs (Token.Types.Punctuation, ".")) { var first = this.WithAnchoredParseTree (start.StartPos, ParseTree.Labels.Call, pt => { this.RequireTokenExactly (Token.Types.Punctuation, "."); pt.Children = new ParseTree[] { this.MakePrim0ParseTree ("svm_gref_dot"), start, this.ParseNameAsString () }; } ); return this.ParseIdentifierOrCallOrRef (first); } else if (this.TokenIs (Token.Types.Punctuation, "(")) { var first = this.WithAnchoredParseTree (start.StartPos, ParseTree.Labels.Call, pt => { var children = new List<ParseTree> (); children.Add (start); children.AddRange (this.ParseList ( this.ParseExpression, Parser.OpenParenthesis, Parser.Comma, Parser.CloseParenthesis) ); pt.Children = children; } ); return this.ParseIdentifierOrCallOrRef (first); } else if (this.TokenIs (Token.Types.Punctuation, "[")) { ParseTree grefParseTree = null; int grefStartPos = 0; int grefEndPos = 0; var result = this.WithAnchoredParseTree (start.StartPos, ParseTree.Labels.Call, pt => { grefStartPos = this.CurrentToken ().StartPos; this.RequireTokenExactly (Token.Types.Punctuation, "["); grefParseTree = this.MakePrim0ParseTree ("svm_gref"); pt.Children = new ParseTree[] { grefParseTree, start, this.ParseExpression () }; this.ConsumeToken (Token.Types.Punctuation, "]"); grefEndPos = this.LastToken ().EndPos; } ); grefParseTree.StartPos = grefStartPos; grefParseTree.EndPos = grefEndPos; return this.ParseIdentifierOrCallOrRef(result); } else { return start; } }
ParseTree PostProcessLogicalOr(ParseTree arg) { return this.MaybeRewriteAsIfExpression ( arg, "||", (t1, t2) => new ParseTree[] { t1, Parser.GetTrueParseTree (), t2 }); }
// Gref is short for 'generic reference'. bool IsGrefCall(ParseTree ast) { if (ast.Label == ParseTree.Labels.Call) { if (ast.Children.Count () > 0) { var fn = ast.Children.First (); if (fn.Label == ParseTree.Labels.Prim0) { if (fn.Content == "svm_gref" || fn.Content == "svm_gref_dot") { return true; } } } } return false; }
ParseTree MaybeRewriteAsIfExpression( ParseTree parseTree, string opName, Func<ParseTree, ParseTree, IEnumerable<ParseTree>> ifChildren) { if (this.IsRequiredPrimitiveCall (parseTree, opName)) { var operands = parseTree.Children.ToArray (); var op = operands [0]; var t1 = operands [1]; var t2 = operands [2]; return new ParseTree () { Label = ParseTree.Labels.If, StartPos = op.StartPos, EndPos = op.EndPos, Children = ifChildren(t1, t2) }; } else { return parseTree; } }
void CompileIf(ParseTree ast, Types.Environment env, bool useVal, bool more) { if (more) { var l1 = this.GenLabel (); var l2 = this.GenLabel (); var pred = ast.Children.ElementAt (0); this.CompileAst (pred, env, true, true); this.Gen (Instruction.Opcodes.Fjump, l1); var thenAction = ast.Children.ElementAt (1); this.CompileAst (thenAction, env, useVal, true); this.Gen (Instruction.Opcodes.Jump, l2); this.Gen (Instruction.Opcodes.Label, l1); var elseAction = ast.Children.ElementAt (2); this.CompileAst (elseAction, env, useVal, true); this.Gen (Instruction.Opcodes.Label, l2); } else { var l1 = this.GenLabel (); var pred = ast.Children.ElementAt (0); this.CompileAst (pred, env, true, true); this.Gen (Instruction.Opcodes.Fjump, l1); var thenAction = ast.Children.ElementAt (1); this.CompileAst (thenAction, env, useVal, false); this.Gen (Instruction.Opcodes.Label, l1); var elseAction = ast.Children.ElementAt (2); this.CompileAst (elseAction, env, useVal, false); } }
void CompileName(ParseTree ast, Types.Environment env, bool useVal, bool more) { var varName = ast.Content; this.Gen (Instruction.Opcodes.Lget, this.FindName (varName, env, ast.StartPos, ast.EndPos), ast); this.FinishInstruction (useVal, more); }
void CompileFnBody(ParseTree args, ParseTree body, Types.Environment env) { if (args.Children.Count () > 0) { var newEnv = this.EmptyEnv (); newEnv.Next = env; foreach (var arg in args.Children) { this.ExtendFrame (newEnv, arg.Content, arg, false); } var varNames = args.Children.Select (arg => arg.Content).ToArray (); this.Gen ( Instruction.Opcodes.NewFrame, varNames, startPos: args.Children.First ().StartPos, endPos: args.Children.Last ().EndPos); this.Gen (Instruction.Opcodes.Args, varNames.Length); this.CompileAst (body, newEnv, true, false); } else { this.CompileAst (body, env, true, false); } }
void CompileFuncall(ParseTree ast, Types.Environment env, bool useVal, bool more) { foreach (var child in ast.Children.Skip(1)) { this.CompileAst (child, env, true, true); } var funAst = ast.Children.First (); var compiledAsInstruction = false; if (funAst.Label == ParseTree.Labels.Prim0) { compiledAsInstruction = this.CompiledAsInstruction (funAst, ast.Children.Count () - 1, useVal, more); } if (!compiledAsInstruction) { this.CompileAst (funAst, env, true, true); if (more) { this.Gen (Instruction.Opcodes.Call, ast.Children.Count () - 1, ast); if (!useVal) { this.Gen (Instruction.Opcodes.Pop); } } else { this.Gen (Instruction.Opcodes.CallJ, ast.Children.Count () - 1, ast); } } }
void CompileFn(ParseTree ast, Types.Environment env, bool useVal, bool more) { if (useVal) { var fn = this.GenLabel ("FN"); var l = this.GenLabel (); this.Gen (Instruction.Opcodes.Jump, l); this.Gen (Instruction.Opcodes.Label, fn, ast); var args = ast.Children.ElementAt (0); var body = ast.Children.ElementAt (1); this.CompileFnBody (args, body, env); this.Gen (Instruction.Opcodes.Label, l); var arity = args.Children.Count(); var hasCollectParams = args.Children.Any(pt => pt.Label == ParseTree.Labels.CollectName); if (hasCollectParams) { arity --; arity += Callable.CollectParamsArityModifier; } this.Gen (Instruction.Opcodes.Fn, new object[] { fn, arity }); if (!more) { this.Gen (Instruction.Opcodes.Return); } } }
bool CompiledAsInstruction(ParseTree funAst, int actualArity, bool useVal, bool more) { foreach (var pair in compilableAsInstructions) { if (pair.Item1 == funAst.Content) { if (actualArity == pair.Item3) { this.Gen (pair.Item2, startPos: funAst.StartPos, endPos: funAst.EndPos); this.FinishInstruction (useVal, more); } else { var message = String.Format ( "Primitive requires {0} arguments, but was called with {1} arguments.", pair.Item3, actualArity); this.RaiseError (funAst.StartPos, funAst.EndPos, message); } return true; } } return false; }
void CompileContext(ParseTree ast, bool useVal, bool more) { this.Gen (Instruction.Opcodes.Context, null, ast); FinishInstruction (useVal, more); }
void ExtendFrame(Types.Environment env, string name, ParseTree nameAst, bool placeHolder) { var topFrame = env.Frame; var previousDefinition = topFrame.EntryFor (name); if (previousDefinition != null) { if (!previousDefinition.PlaceHolder) { var sourceFile = SourceFile.FindSource (this.sources, previousDefinition.FileName); var pos = Position.CalculatePosition (sourceFile, previousDefinition.StartPos); var message = String.Format ( "Variable '{0}' is already defined in this frame in file '{1}', at line {2}, column {3}.", name, pos.FileName, pos.Line, pos.Column); this.RaiseError (nameAst.StartPos, nameAst.EndPos, message); } else { previousDefinition.PlaceHolder = placeHolder; if (nameAst != null) { previousDefinition.StartPos = nameAst.StartPos; } else { previousDefinition.StartPos = 0; } } } else { int currentPos = 0; if (nameAst != null) { currentPos = nameAst.StartPos; } var newVar = new EnvVar () { Name = name, FileName = this.fileName, Place = topFrame.Vars.Count, StartPos = currentPos, PlaceHolder = placeHolder }; topFrame.Vars.Add (newVar); } }
void CompileNamedBlock(ParseTree ast, Types.Environment env, bool useVal, bool more) { var blockName = ast.Children.ElementAt (0); var blockContents = ast.Children.ElementAt (1); var blockEnd = this.GenLabel ("BE"); this.CompileAst (blockName, env, true, true); this.Gen (Instruction.Opcodes.Block, blockEnd, ast); this.CompileAst (blockContents, env, true, true); this.Gen (Instruction.Opcodes.Label, blockEnd); this.Gen (Instruction.Opcodes.PopBlock, null, ast); this.FinishInstruction (useVal, more); }
void Gen(Instruction.Opcodes opcode, object arguments = null, ParseTree pos = null, string comments = null, int? startPos = null, int? endPos = null) { var instruction = new Instruction () { Opcode = opcode, Arguments = arguments, Comments = comments }; if (startPos != null) { instruction.StartPos = startPos; } else if (pos != null && pos.StartPos != -1) { instruction.StartPos = pos.StartPos; } if (endPos != null) { instruction.EndPos = endPos; } else if (pos != null && pos.EndPos != -1) { instruction.EndPos = pos.EndPos; } this.bytecode.Add (instruction); }
void CompilePrim0(ParseTree ast, bool useVal, bool more) { this.Gen (Instruction.Opcodes.Prim0, ast.Content, ast); FinishInstruction (useVal, more); }
bool IsRequiredPrimitiveCall(ParseTree parseTree, string opName) { if (parseTree.Label == ParseTree.Labels.Call) { var op = parseTree.Children.First (); if (op.Label == ParseTree.Labels.Prim0 && op.Content == opName) { return true; } } return false; }
void CompileSet(ParseTree ast, Types.Environment env, bool useVal, bool more) { var leftHandSide = ast.Children.First (); if (leftHandSide.Label == ParseTree.Labels.Name) { this.CompileAst (ast.Children.ElementAt (2), env, true, true); this.CompileSetVar (leftHandSide.Content, env, useVal, more, ast); } else { if (!this.IsGrefCall (leftHandSide)) { this.RaiseError ( ast.StartPos, ast.EndPos, "Assignment only supported for names, arrays and hashes."); } var arrayOrHash = leftHandSide.Children.ElementAt (1); var index = leftHandSide.Children.ElementAt (2); var literalStringIndex = index.Label == ParseTree.Labels.String; var setOperator = leftHandSide.Children.ElementAt (0); var primitiveParseTree = new ParseTree () { Label = ParseTree.Labels.Prim0, StartPos = setOperator.StartPos, EndPos = setOperator.EndPos, Content = literalStringIndex ? "svm_set_dot_indexed" : "svm_set_indexed" }; var rightHandSide = ast.Children.ElementAt (2); var callParseTree = new ParseTree () { Label = ParseTree.Labels.Call, StartPos = ast.StartPos, EndPos = ast.EndPos, Children = new ParseTree[] { primitiveParseTree, arrayOrHash, index, rightHandSide } }; this.CompileAst (callParseTree, env, useVal, more); } }
ParseTree ParseAssignment(ParseTree expr) { return this.WithAnchoredParseTree (expr.StartPos, ParseTree.Labels.Assignment, pt => { pt.Children = new ParseTree[] { expr, this.TokenAsParseTree (ParseTree.Labels.Prim0), this.ParseExpression () }; } ); }
void CompileSetVar(string name, Types.Environment env, bool useVal, bool more, ParseTree astForPos) { this.Gen ( Instruction.Opcodes.Lset, this.FindName (name, env, astForPos.StartPos, astForPos.EndPos), astForPos); this.FinishInstruction (useVal, more); }
ParseTree PostProcessLogicalAnd(ParseTree arg) { return this.MaybeRewriteAsIfExpression ( arg, "&&", (t1, t2) => new ParseTree[] { t1, t2, Parser.GetFalseParseTree () }); }
void CompileStatements( Types.Environment env, IEnumerable<ParseTree> dropValueAsts, ParseTree valueAst, bool more) { foreach (var ast in dropValueAsts) { this.CompileAst (ast, env, false, true); } this.CompileAst (valueAst, env, true, more); }
ParseTree TokenAsParseTree(ParseTree.Labels prim0) { return this.WithNewParseTree (prim0, pt => { pt.Content = this.CurrentToken ().Content; this.NextToken (); } ); }
void CompileUserDefinedPrimitive(ParseTree ast, bool useVal, bool more) { this.Gen (Instruction.Opcodes.Prim, ast.Content.Substring (1), ast); FinishInstruction (useVal, more); }
ParseTree WithNewParseTree(ParseTree.Labels label, Action<ParseTree> action) { ParseTree result = new ParseTree (); result.StartPos = this.CurrentToken ().StartPos; result.Label = label; action (result); result.EndPos = this.LastToken ().EndPos; return result; }
void CompileVar(ParseTree ast, Types.Environment env, bool useVal, bool more) { var nameAst = ast.Children.ElementAt (0); var name = nameAst.Content; this.ExtendFrame (env, name, nameAst, false); this.CompileAst (ast.Children.ElementAt (1), env, true, true); this.CompileSetVar (name, env, useVal, more, ast); }
static ParseTree GetNullParseTree() { if (Parser.nullParseTree == null) { Parser.nullParseTree = new ParseTree() { Label = ParseTree.Labels.Void, Content = "null", StartPos = -1, EndPos = -1 }; } return Parser.nullParseTree; }
object CompileAtomValue(ParseTree.Labels label, string content) { switch (label) { case ParseTree.Labels.Number: { double doubleResult; long longResult; if (long.TryParse (content, out longResult)) { return Value.MakeInt (longResult); } else if (double.TryParse (content, out doubleResult)) { return Value.MakeFloat (doubleResult); } } Utils.Panic (); return null; case ParseTree.Labels.String: return Value.Make (content.Substring (1, content.Length - 2)); case ParseTree.Labels.Bool: switch (content) { case "true": return Value.Make (true); case "false": return Value.Make (false); default: Utils.Panic (); return null; } case ParseTree.Labels.Void: return Value.Make (); default: Utils.Panic (); return null; } }