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); } } }
void ExtendFrame(Types.Environment env, string name, ParseTree nameAst) { var topFrame = env.Frame; var currentStartPos = nameAst.StartPos; var previousDefinition = topFrame.EntryFor(name); if (previousDefinition != null) { 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(currentStartPos, nameAst.EndPos, message); } else { var newVar = new EnvVar() { Name = name, FileName = this.fileName, Place = topFrame.Vars.Count, StartPos = currentStartPos }; topFrame.Vars.Add(newVar); } }
void CompileBlock(IEnumerable <ParseTree> ast, Types.Environment env, bool useVal, bool more) { int?meatStart = PositionOf(ast, pt => pt.Label != ParseTree.Labels.FileName, false); int?meatEnd = PositionOf(ast, pt => pt.Label != ParseTree.Labels.FileName, true); IEnumerable <ParseTree> prefixFluff, meat, suffixFluff; if (meatStart == null && meatEnd == null) { prefixFluff = ast; meat = new List <ParseTree> (); suffixFluff = new List <ParseTree> (); } else { prefixFluff = ast.Take(meatStart.Value); meat = ast.Skip(meatStart.Value).Take(meatEnd.Value - meatStart.Value + 1); suffixFluff = ast.Skip(meatEnd.Value + 1); } foreach (var pt in prefixFluff) { CompileAst(pt, this.EmptyEnv(), true, true); } CompileBlockMeat(meat, env, useVal, more); foreach (var pt in suffixFluff) { CompileAst(pt, this.EmptyEnv(), true, true); } }
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 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 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); }
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 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); }
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); }
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); }
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 CompileAtom(ParseTree ast, Types.Environment env, bool useVal, bool more) { if (useVal) { this.Gen( Instruction.Opcodes.Const, this.CompileAtomValue(ast.Label, ast.Content), ast); if (!more) { this.Gen(Instruction.Opcodes.Return); } } }
void CompileBlockMeat(IEnumerable <ParseTree> ast, Types.Environment env, bool useVal, bool more) { if (ast.Count() > 0) { var newVars = ast.Where(child => child.Label == ParseTree.Labels.Var); var newVarCount = newVars.Count(); var dropValueCount = ast.Count() - 1; var dropValueAsts = ast.Take(dropValueCount); var valueAst = ast.Skip(dropValueCount).First(); var newEnv = this.EmptyEnv(); newEnv.Next = env; if (newVarCount > 0) { var newVarNames = newVars .Select(child => child.Children.ElementAt(0).Content).ToArray(); this.Gen( Instruction.Opcodes.NewFrame, newVarNames, startPos: newVars.First().StartPos, endPos: newVars.Last().EndPos); foreach (var varName in newVarNames) { this.ExtendFrame(newEnv, varName, null, true); } this.CompileStatements(newEnv, dropValueAsts, valueAst, more); if (more) { this.Gen(Instruction.Opcodes.DropFrame); } if (!useVal) { this.Gen(Instruction.Opcodes.Pop); } } else { this.CompileStatements(env, dropValueAsts, valueAst, more); if (!useVal) { this.Gen(Instruction.Opcodes.Pop); } } } else { this.Gen(Instruction.Opcodes.Const, Value.Make()); FinishInstruction(useVal, more); } }
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); } }
int[] FindName(string varName, Types.Environment env, int startPos, int endPos, int frameNumber = 0) { if (env == null) { var message = String.Format("Undefined variable '{0}'.", varName); this.RaiseError(startPos, endPos, message); return(null); } else { for (var j = 0; j < env.Frame.Vars.Count; j++) { if (varName == env.Frame.Vars [j].Name) { return(new int[] { frameNumber, j }); } } return(FindName(varName, env.Next, startPos, endPos, frameNumber + 1)); } }
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); this.Gen(Instruction.Opcodes.Fn, new object[] { fn, args.Children.Count() } ); if (!more) { this.Gen(Instruction.Opcodes.Return); } } }
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 CompileAst(ParseTree ast, Types.Environment env, bool useVal, bool more) { switch (ast.Label) { case ParseTree.Labels.FileName: this.fileName = ast.Content; this.Gen(Instruction.Opcodes.FileName, this.fileName); break; case ParseTree.Labels.Var: this.CompileVar(ast, env, useVal, more); break; case ParseTree.Labels.Fn: this.CompileFn(ast, env, useVal, more); break; case ParseTree.Labels.Begin: this.CompileBlock(ast.Children, env, useVal, more); break; case ParseTree.Labels.Assignment: this.CompileSet(ast, env, useVal, more); break; case ParseTree.Labels.If: this.CompileIf(ast, env, useVal, more); break; case ParseTree.Labels.Name: this.CompileName(ast, env, useVal, more); break; case ParseTree.Labels.Call: this.CompileFuncall(ast, env, useVal, more); break; case ParseTree.Labels.Prim0: this.CompilePrim0(ast, useVal, more); break; case ParseTree.Labels.UserDefinedPrimitive: this.CompileUserDefinedPrimitive(ast, useVal, more); break; case ParseTree.Labels.Number: this.CompileAtom(ast, env, useVal, more); break; case ParseTree.Labels.String: this.CompileAtom(ast, env, useVal, more); break; case ParseTree.Labels.Bool: this.CompileAtom(ast, env, useVal, more); break; case ParseTree.Labels.Void: this.CompileAtom(ast, env, useVal, more); break; case ParseTree.Labels.NamedBlock: this.CompileNamedBlock(ast, env, useVal, more); break; case ParseTree.Labels.BlockReturn: this.CompileBlockReturn(ast, env); break; case ParseTree.Labels.Context: this.CompileContext(ast, useVal, more); break; case ParseTree.Labels.Placeholder: RaiseError(ast.StartPos, ast.EndPos, "'$' without '->'."); break; default: Utils.Panic(); break; } }