/* * the operator == + - * / % are buildin * + - * / % only for int value */ private static EnviromentNode ApplyBuildinFunction(EnviromentNode funcNode, EnviromentNode[] parameters, Enviroment enviroment) { EnviromentNode retNode = new EnviromentNode(enviroment); foreach (EnviromentNode node in parameters) { EvalDelayEvalBlock(node); } if (funcNode.Symbol == "==") { if (parameters.Length < 2 ) throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = (parameters[0].Equals(parameters[1])) ? 1 : 0; } else if( funcNode.Symbol == "+") { if (parameters.Length < 2 || parameters[0].ValueType != "int" || parameters[1].ValueType != "int") throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = parameters[0].IValue + parameters[1].IValue; } else if( funcNode.Symbol == "-") { if (parameters.Length < 2 || parameters[0].ValueType != "int" || parameters[1].ValueType != "int") throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = parameters[0].IValue - parameters[1].IValue; } else if( funcNode.Symbol == "*") { if (parameters.Length < 2 || parameters[0].ValueType != "int" || parameters[1].ValueType != "int") throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = parameters[0].IValue * parameters[1].IValue; } else if( funcNode.Symbol == "/") { if (parameters.Length < 2 || parameters[0].ValueType != "int" || parameters[1].ValueType != "int") throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = parameters[0].IValue / parameters[1].IValue; } else if( funcNode.Symbol == "%") { if (parameters.Length < 2 || parameters[0].ValueType != "int" || parameters[1].ValueType != "int") throw new Exception("invalid parameters for ==, different value type"); retNode.ValueType = "int"; retNode.IValue = parameters[0].IValue % parameters[1].IValue; } else { throw new NotImplementedException(); } retNode.Symbol = "__tmp__" + enviroment.TopCount().ToString(); return retNode; }
public Enviroment(Enviroment src) { if (src != null) { envList.Clear(); foreach (EnviromentBlock block in src.envList) { envList.Add(new EnviromentBlock(block)); } } }
public DelayEvalBlock(ParseNode node, Enviroment env) { this.node = node; this.enviroment = new Enviroment(env); }
private static void EvalVarSentense(ParseNode node, Enviroment enviroment, EnviromentNode envNode) { envNode.Symbol = node.Nodes[1].Token.Text; if (node.Nodes[2].Token.Type == TokenType.FUNCTION) { EnviromentNode resultNode = EvalDirectFunctionCall(node.Nodes[2], enviroment, envNode.Enviroment); // before bind function result to a smbol, eval result node first EvalDelayEvalBlock(resultNode); if (resultNode.GrammerNode != null) { ParseNode result = resultNode.GrammerNode; if (resultNode.ValueType != "lambda") { envNode.Type = GetTypeString(result.Nodes[2], null); envNode.Value = GetValueOfLambda(result.Nodes[2]); } envNode.GrammerNode = result; } envNode.Enviroment = resultNode.Enviroment; envNode.CopyValueFromNode(resultNode); return; } else if (node.Nodes[2].Token.Type == TokenType.IFTHENELSE) { DelayEvalBlock block = new DelayEvalBlock(node.Nodes[2], enviroment); envNode.AddDelayEvalBlock(block); EvalDelayEvalBlock(envNode); } else if (node.Nodes[2].Token.Type == TokenType.LAMBDA) { envNode.GrammerNode = node; envNode.Type = GetTypeString(node.Nodes[2], null); envNode.Value = GetValueOfLambda(node.Nodes[2]); envNode.ValueType = "lambda"; } else if (node.Nodes[2].Token.Type == TokenType.DIGIT) { envNode.GrammerNode = null; envNode.ValueType = "int"; envNode.IValue = Int32.Parse(node.Nodes[2].Token.Text); } else if (node.Nodes[2].Token.Type == TokenType.STRINGVAL) { envNode.GrammerNode = null; envNode.ValueType = "string"; string tmp = node.Nodes[2].Token.Text; envNode.SValue = tmp.Substring(1, tmp.Length - 2); } }
private static void EvalVariableSentense(ParseNode node, Enviroment enviroment, EnviromentNode envNode) { /* * node[0] == 'let' | 'set' * node[1] == symbol name * node[2] == body */ if (node.Nodes.Count < 3) return; if (node.Nodes[1].Token.Type != TokenType.DEFINITION) return; EvalVarSentense(node, enviroment, envNode); }
private static void EvalTree(ParseTree tree, Enviroment enviroment) { /* * for each sentence, eval it * 1. every sentence has an enviroment to eval itself * 2. when enter into a function body, or apply parameter to a lambda object, * create new enviroment for this block * 2.1 scan symbol from buttom of the enviroment list to top * 2.2 every symbol has a reference of the it's enviroment, so when a function * returns a lambda object, this object has it's own enviroment * 3. when eval direct function call, change it into lambda first, then apply * 4. when eval function define, change it into lambda define */ ParseNode starter = tree.Nodes[0]; ParseNode sentences = starter.Nodes[0]; EvalSentenses(sentences, enviroment); }
static void EvalSentenses(ParseNode sentences, Enviroment enviroment) { foreach (ParseNode node in sentences.Nodes) { EnviromentNode result = EvalSentense(node, enviroment); if (result != null && result.Terminate) break; } }
/* * IO need special support */ static EnviromentNode ApplyIO(EnviromentNode func, EnviromentNode[] parameters, Enviroment enviroment) { EnviromentNode retNode = null; if (parameters.Length > 0) { EnviromentNode paramNode = parameters[0]; retNode = new EnviromentNode(enviroment, paramNode); } else { retNode = new EnviromentNode(enviroment); } retNode.Terminate = false; if (func.Symbol == "return") { if (parameters.Length == 0) throw new Exception("invalid IO: return without parameters"); retNode.Terminate = true; } else if (func.Symbol == "print") { StringBuilder printString = new StringBuilder(); foreach (EnviromentNode node in parameters) { EvalDelayEvalBlock(node); if( node.ValueType == "string" ) { printString.Append(node.SValue); } else if( node.ValueType == "int") { printString.Append(node.IValue.ToString()); } else if( node.ValueType == "lambda") { printString.Append(node.Type); } else { printString.Append(node.Symbol); } } Console.WriteLine(printString.ToString()); } else if (func.Symbol == "readline") { retNode.SValue = Console.ReadLine(); } retNode.Symbol = "__tmp__" + enviroment.TopCount().ToString(); return retNode; }
public void CopyFullFromNode(EnviromentNode node) { this.Symbol = node.Symbol; this.Type = node.Type; this._value = node.Value; this.grammerNode = node.GrammerNode; this.CopyValueFromNode(node); this.enviroment = node.enviroment; this.terminate = node.terminate; }
static void EvalFunctionDefineSentense(ParseNode node, Enviroment enviroment, EnviromentNode envNode) { // node[0] == symbol name // node[1] == parameters // node[2] == function body envNode.GrammerNode = node; envNode.Symbol = node.Nodes[0].Token.Text; envNode.Type = "__function__"; envNode.Value = "1"; }
/* * eval sentense like " AND TRUE FALSE" * parameters can be another function call, like AND (AND TRUE TRUE) FALSE * currently eval all parameters before apply to functor * if parameter ( and after eval function all ) does not exist in enviroment, just leave a symbol in it * in order to support IO, add special functor type __io__, which evals special, need to do IOs internally * and in order to support function, add special functor type __function__ * when eval __function__, eval sentenses inside this function until we met a "return" * if a lambda like \(x,y).x , say only one element in body, it's a terminal symbol * else treate first elememt in body as a functor and eval it */ private static EnviromentNode EvalDirectFunctionCall(ParseNode node, Enviroment enviroment, Enviroment closure) { // nodes[0] == 'var' // nodes[1] == symbol // nodes[2] == function body if (node.Nodes.Count < 1) throw new Exception("invalid direct function call"); ParseNode funcBody = node; string lambdaSymbol = funcBody.Nodes[0].Token.Text; EnviromentNode funcNode = closure.LookupSymbol(lambdaSymbol); if (funcNode == null) funcNode = enviroment.LookupSymbol(lambdaSymbol); // before apply parameters to a lambda, eval functor to get a result EvalDelayEvalBlock(funcNode); /* * sentense like * var a = \(x,y).x TRUE FALSE * FIXME */ EnviromentNode[] parameters = new EnviromentNode[funcBody.Nodes.Count - 1]; for (int index = 1; index < funcBody.Nodes.Count; index++) { ParseNode paramNode = funcBody.Nodes[index]; if (paramNode.Token.Type == TokenType.FUNCTION || paramNode.Token.Type == TokenType.IFTHENELSE ) { // parameters[index - 1] = EvalDirectFunctionCall(funcBody.Nodes[index], enviroment, null); DelayEvalBlock block = new DelayEvalBlock(funcBody.Nodes[index], enviroment); parameters[index - 1] = new EnviromentNode(enviroment); parameters[index - 1].AddDelayEvalBlock(block); } else { Token token = funcBody.Nodes[index].Token; EnviromentNode tmp = null; if (token.Type == TokenType.DIGIT) { // like foo 12; tmp = new EnviromentNode(enviroment); tmp.IValue = Int32.Parse(token.Text); tmp.ValueType = "int"; } else if (token.Type == TokenType.STRINGVAL) { // like foo "hello"; tmp = new EnviromentNode(enviroment); tmp.SValue = token.Text.Substring(1, token.Text.Length - 2); tmp.ValueType = "string"; } else if (token.Type == TokenType.LAMBDA) { /* * sentense like * return \(x,y).x * foo \(x,y).x FALSE */ tmp = new EnviromentNode(enviroment); tmp.Type = GetTypeString(funcBody.Nodes[index], null); tmp.Value = GetValueOfLambda(funcBody.Nodes[index]); tmp.GrammerNode = funcBody.Nodes[index]; tmp.ValueType = "lambda"; } else { // like foo a b; tmp = enviroment.LookupSymbol(token.Text); if (tmp == null) { tmp = new EnviromentNode(null); tmp.Symbol = token.Text; } } parameters[index - 1] = tmp; } } EnviromentNode result = null; if (funcNode.Type == "__io__") { result = ApplyIO(funcNode, parameters, enviroment); } else if (funcNode.Type == "__function__") { result = ApplyFunction(funcNode, parameters, enviroment); } else if (funcNode.Type == "__buildin__") { result = ApplyBuildinFunction(funcNode, parameters, enviroment); } else // lambda { if (parameters.Length == 0) { result = closure.LookupSymbol(lambdaSymbol); if (result == null) result = enviroment.LookupSymbol(lambdaSymbol); } else result = ApplyToLambda(lambdaSymbol, parameters, enviroment, funcNode.Enviroment); } return result; }
static void BindParameterToEnviroment(EnviromentNode[] parameters, Enviroment newEnviro, ParseNode paraNode) { if (paraNode.Nodes.Count > 0) { int index = 0; foreach (ParseNode paramNode in paraNode.Nodes) { string symbol = paramNode.Token.Text; BindParameter(parameters, newEnviro, index, symbol); index++; } } else { string symbol = paraNode.Token.Text; if (!string.IsNullOrEmpty(symbol)) BindParameter(parameters, newEnviro, 0, symbol); } }
private static void BindParameter(EnviromentNode[] parameters, Enviroment newEnviro, int index, string symbol) { EnviromentNode applyTo = parameters.Length < index ? null : parameters[index]; EnviromentNode newNode = null; if (applyTo == null) { newNode = new EnviromentNode(newEnviro); newNode.Symbol = symbol; } else { EnviromentNode applyEnvNode = applyTo; newNode = new EnviromentNode(newEnviro, applyEnvNode); newNode.Symbol = symbol; } newEnviro.AddEnviromentNode(newNode); }
/* * apply parameters to a lambda * create a new enviroment block, add all parameter symbol into this enviroment * then bind parameters with these symbol * for example we have symbols in upper table: TRUE = \(x,y).x; FALSE = \(x,y).y; AND = \(p,q).p q FALSE; * then we want to eval "a = AND TRUE FALSE"; we call ApplyToLambda( AND, {TRUE, FALSE}, env) * what should be done in ApplyToLambda is * 1. create new enviroment block * 2. p bind to TRUE; q bind to FALSE * 3. call ApplyToLambda(p, {q, FALSE}) which is equal to ApplyToLambda(TRUE, {FALSE, FALSE}) * do 1-3 until we meet a terminal symbol * */ private static EnviromentNode ApplyToLambda(string lambdaSymbol, EnviromentNode[] parameters, Enviroment enviroment, Enviroment closure) { EnviromentNode lambda = enviroment.LookupSymbol(lambdaSymbol); if (lambda == null) throw new Exception("unknow symbol: " + lambdaSymbol); EvalDelayEvalBlock(lambda); // bind parameter to enviroment Enviroment newEnviro = new Enviroment(enviroment); EnviromentBlock block = new EnviromentBlock(); newEnviro.AddEnviroment(block); ParseNode paramNodes = null; if (lambda.GrammerNode.Token.Type != TokenType.LAMBDA) { paramNodes = lambda.GrammerNode.Nodes[2]; } else { paramNodes = lambda.GrammerNode; } BindParameterToEnviroment(parameters, newEnviro, paramNodes.Nodes[0]); return EvalDirectFunctionCall(paramNodes.Nodes[1], newEnviro, closure); }
public EnviromentNode(Enviroment env, EnviromentNode node) { CopyFullFromNode(node); this.enviroment = new Enviroment(env); }
public EnviromentNode(Enviroment env) { enviroment = new Enviroment(env); terminate = false; this.delayEvalBlock = null; this.valueType = "unknow"; }
/* * eval if (a) then {b} else {c}; * block a,b,c are all function, * block a return a int value node, * run b if a is not 0, run c else */ private static EnviromentNode EvalIfThenElse(ParseNode parseNode, Enviroment enviroment, Enviroment closure) { EnviromentNode a = EvalDirectFunctionCall(parseNode.Nodes[0], enviroment, closure); if (a.ValueType != "int") throw new Exception("invalid result in if block"); EnviromentNode ret = null; ParseNode b = null; if (a.IValue != 0) b = parseNode.Nodes[1]; else b = parseNode.Nodes[2]; if (b.Token.Type == TokenType.IFTHENELSE) ret = EvalIfThenElse(b, enviroment, closure); else if (b.Token.Type == TokenType.DIGIT || b.Token.Type == TokenType.STRINGVAL || b.Token.Type == TokenType.DOUBLE) { ret = new EnviromentNode(enviroment); switch (b.Token.Type) { case TokenType.DIGIT: ret.ValueType = "int"; ret.IValue = Int32.Parse(b.Token.Text); break; case TokenType.STRINGVAL: ret.ValueType = "string"; ret.SValue = b.Token.Text; break; case TokenType.DOUBLE: ret.ValueType = "double"; ret.DValue = Double.Parse(b.Token.Text); break; default: break; } } else ret = EvalDirectFunctionCall(b, enviroment, closure); return ret; }
private static EnviromentNode EvalSentense(ParseNode node, Enviroment enviroment) { EnviromentNode envNode = new EnviromentNode(enviroment); if (node.Token.Type == TokenType.LET) { EvalVariableSentense(node, enviroment, envNode); if (!string.IsNullOrEmpty(envNode.Symbol)) enviroment.AddEnviromentNode(envNode); } else if (node.Token.Type == TokenType.SET) { EvalVariableSentense(node, enviroment, envNode); if (!string.IsNullOrEmpty(envNode.Symbol)) enviroment.UpdateEnviromentNode(envNode); } else if (node.Token.Type == TokenType.FUNCDEF) { EvalFunctionDefineSentense(node, enviroment, envNode); if (!string.IsNullOrEmpty(envNode.Symbol)) enviroment.AddEnviromentNode(envNode); } else if (node.Token.Type == TokenType.FUNCTION) { // if a direct call occures in eval sentense, it must be an IO, or return envNode = EvalDirectFunctionCall(node, enviroment, envNode.Enviroment); if (!string.IsNullOrEmpty(envNode.Symbol)) enviroment.AddEnviromentNode(envNode); } return envNode; }
/* * when ApplyFunction, do parameter bind like eval lambda, then eval every sentense in function body * until we met "return" * if every parameter in parameters[] occures in current enviroment, no need to create new enviroment */ static EnviromentNode ApplyFunction(EnviromentNode func, EnviromentNode[] parameters, Enviroment enviroment) { // nodes[0] == Symbol // node[1] == parameters // node[2] == function body Enviroment newEnviro = null; ParseNode paraNode = func.GrammerNode.Nodes[1]; newEnviro = new Enviroment(globalEnviroment); EnviromentBlock block = new EnviromentBlock(); newEnviro.AddEnviroment(block); // bind parameter to enviroment BindParameterToEnviroment(parameters, newEnviro, paraNode); // eval all body sentense ParseNode body = func.GrammerNode.Nodes[2]; foreach (ParseNode sentense in body.Nodes) { EnviromentNode ret = EvalSentense(sentense, newEnviro); if (ret != null && ret.Terminate) { return ret; } } return null; }