/// <summary> /// For testing expression parsing only /// </summary> public Expression CompileExpression(string input, bool normalize = true) { var tokens = Lexer.Tokenize(input).ToList(); var commandNodeRoot = new ActionNodeRoot(); // Build string tables foreach (Token token in tokens) { if (token.Type == TokenType.StringLiteral) { string data = token.Value.Substring(1, token.Value.Count() - 2); commandNodeRoot.StringTable.RegisterString(data); } } var compilationContext = new CompilationContext(m_HostCallTable, commandNodeRoot.StringTable, m_Engine.HostBridge.EnumDefineTable, m_Engine.HostBridge.TypeBindingTable, m_Engine.HostBridge.EventBindingTable); Expression expression = ExpressionParser.Parse(tokens, compilationContext, normalize); return expression; }
private int ParseFlagAccess(CompilationContext compilationContext, Token currentToken, List<Token> tokens, int index, ref IActionNode currentParent) { int varIndex = currentToken.GetAccessIndex(m_Engine.HostBridge.EnumDefineTable); // Map enums to their index counterparts if (currentToken.Type == TokenType.LocalFlagAccessEnum) currentToken.Type = TokenType.LocalFlagAccess; if (currentToken.Type == TokenType.GlobalFlagAccessEnum) currentToken.Type = TokenType.GlobalFlagAccess; // Global or local? var op = TokenType.GlobalFlagAccess; if (currentToken.Type == TokenType.LocalFlagAccess) op = TokenType.LocalFlagAccess; currentToken = tokens[++index]; // Assignment AssertExpectedTokenType(currentToken, TokenType.Assignment, "Expected Assignment"); currentToken = tokens[++index]; Expression expression; int skip = ParseExpression(index, tokens, compilationContext, out expression); index += skip; currentToken = tokens[index]; // Create the assignment command currentParent.Next = new ModifyFlag(varIndex, op, expression); currentParent = currentParent.Next; return index; }
private Int32 ParseFunctionParameterExpressions(Int32 startIndex, List<Token> tokenStream, CompilationContext compilationContext, out List<Expression> rv) { rv = new List<Expression>(); int currentIndex = startIndex; var positionStack = new Stack<Int32>(); Token currentToken = tokenStream[startIndex]; int position = startIndex; while (currentToken.Type != TokenType.EndStatement) { if (currentToken.Type == TokenType.OpenBracket) { positionStack.Push(currentIndex); } if (currentToken.Type == TokenType.CloseBracket) { position = positionStack.Pop(); } if ((currentToken.Type == TokenType.FunctionArgumentSeperator && positionStack.Count == 1) || (currentToken.Type == TokenType.CloseBracket && positionStack.Count == 0)) { if (position == startIndex) position += 1; var tokens = new List<Token>(); for (int i = position + 1; i < currentIndex; i++) { tokens.Add(tokenStream[i]); } if (tokens.Count > 0) { tokens.Add(new Token(TokenType.EndStatement, OperationType.None, "", new TokenPosition(0, 0, 0))); Expression expression; ParseExpression(0, tokens, compilationContext, out expression); rv.Add(expression); if (positionStack.Count == 1) { positionStack.Pop(); positionStack.Push(currentIndex); position = currentIndex; } } } currentToken = tokenStream[++currentIndex]; } return currentIndex - startIndex; }
private int ParseEvent(CompilationContext compilationContext, List<Token> tokens, int index, ref IActionNode currentParent) { Token currentToken = tokens[++index]; if (currentToken.Type == TokenType.ReferenceAction) { throw new GossipScriptException("Only Properties may be called on an event parameter"); } if (currentToken.Type == TokenType.ReferencePropertySet) { Token operand = currentToken; Expression expression; currentToken = tokens[++index]; AssertExpectedTokenType(currentToken, TokenType.Assignment, "Unexpected token"); currentToken = tokens[++index]; int skip = ParseExpression(index, tokens, compilationContext, out expression); index += skip; currentToken = tokens[index]; if (!m_Engine.HostBridge.TypeBindingTable.SetPropertyExists(operand.Value, compilationContext.EventType)) throw new GossipScriptException(String.Format("Property {0} not found or is read only.", operand.Value)); PropertySetBinding setter = m_Engine.HostBridge.TypeBindingTable.GetPropertySetByName(operand.Value, compilationContext .EventType); if (setter.ParameterType != expression.ReturnType) throw new GossipScriptException("Incompatile property set parameter"); currentParent.Next = new AssignReferenceProperty(-2, setter.Id, expression); currentParent = currentParent.Next; return index; } throw new Exception("Unexpected token"); }
/// <summary> /// Returns number of tokens to skip /// </summary> private Int32 ParseExpression(Int32 startIndex, List<Token> tokenStream, CompilationContext compilationContext, out Expression rv) { var countTokensToProcess = 0; int currentIndex = startIndex; var expressionTokenstream = new List<Token>(); var currentToken = tokenStream[startIndex]; while (currentToken.Type != TokenType.OpenCurlyBrace && currentToken.Type != TokenType.EndStatement && currentToken.Type != TokenType.EndOfProgram && currentToken.Type != TokenType.ReferenceAction && currentToken.Type != TokenType.ReferencePropertySet) { countTokensToProcess++; expressionTokenstream.Add(currentToken); currentToken = tokenStream[++currentIndex]; } var expression = ExpressionParser.Parse(expressionTokenstream, compilationContext); rv = expression; return countTokensToProcess; }
private int ParseReferenceObjectAccess(CompilationContext compilationContext, Token currentToken, List<Token> tokens, int index, ref IActionNode currentParent) { int varIndex = currentToken.GetAccessIndex(m_Engine.HostBridge.EnumDefineTable); // Map enums to their index counterparts if (currentToken.Type == TokenType.ReferenceObjectAccessEnum) currentToken.Type = TokenType.ReferenceObjectAccess; currentToken = tokens[++index]; // Assignment or Property set or Action only Token operand = currentToken; Expression expression; if (currentToken.Type == TokenType.ReferencePropertySet) { currentToken = tokens[++index]; AssertExpectedTokenType(currentToken, TokenType.Assignment, "Unexpected token"); currentToken = tokens[++index]; int skip = ParseExpression(index, tokens, compilationContext, out expression); index += skip; currentToken = tokens[index]; ReferenceAssignment assignment = compilationContext.ReferenceAssignmentTable.GetAssignment(varIndex); if (!m_Engine.HostBridge.TypeBindingTable.SetPropertyExists(operand.Value, assignment.Type)) throw new GossipScriptException(String.Format("Property {0} not found or is read only.", operand.Value)); PropertySetBinding setter = m_Engine.HostBridge.TypeBindingTable.GetPropertySetByName(operand.Value, assignment.Type); if (setter.ParameterType != expression.ReturnType) throw new GossipScriptException("Incompatile property set parameter"); currentParent.Next = new AssignReferenceProperty(varIndex, setter.Id, expression); currentParent = currentParent.Next; return index; } if (currentToken.Type == TokenType.Assignment) { currentToken = tokens[++index]; int skip = ParseExpression(index, tokens, compilationContext, out expression); index += skip; currentToken = tokens[index]; compilationContext.ReferenceAssignmentTable.TrackReference(varIndex, expression.HostReturnType); if (expression.ReturnType != GossipType.ReferenceObject) throw new GossipScriptException("Expected return type reference object"); // Create the assignment command currentParent.Next = new AssignReference(varIndex, expression); currentParent = currentParent.Next; return index; } // Parse Action { int skip = ParseExpression(index - 1, tokens, compilationContext, out expression); index += (skip); currentToken = tokens[index - 1]; if (currentToken.Type == TokenType.ReferenceAction) { // Create an action ReferenceAssignment actionType = compilationContext.ReferenceAssignmentTable.GetAssignment(varIndex); ActionBinding action = compilationContext.TypeBindingTable.GetActionByName(currentToken.Value, actionType.Type); ICustomActionNode node = action.CreateActionNode(); int refIndex = varIndex; // Unless the expression result is List<Expression> expressions; skip = ParseActionParameters(index, tokens, compilationContext, out expressions); if (action.NumParameters != expressions.Count) throw new GossipScriptParameterException( String.Format("Invalid number of parameters for action:{0} found:{1} expected:{2}", action.Name, expressions.Count, action.NumParameters)); var customActionContext = new CustomActionContext(expressions, refIndex); currentParent.Next = new ExecuteCustomActionNode(node, customActionContext); currentParent = currentParent.Next; index += (skip + 1); currentToken = tokens[index]; return index; } } throw new GossipScriptException("Unhandled operand"); }
/// <summary> /// Returns a list of semantic tokens in Reverse Polish Notation /// </summary> public Expression Parse(List<Token> tokenstream, CompilationContext compilationContext, Boolean normalize = true) { var exp = new Expression(); var instructions = new List<SemanticToken>(); List<SemanticToken> opcodes = m_ExpressionSemantics.ApplyExpressionSemantics(tokenstream, compilationContext).ToList(); if (normalize) opcodes = m_ExpressionNormalizer.NormalizeExpression(0, opcodes.Count, opcodes); var stack = new Stack<SemanticToken>(tokenstream.Count); // Shunting Yard Algorithm // http://en.wikipedia.org/wiki/Shunting-yard_algorithm foreach (SemanticToken opcode in opcodes) { if (opcode.IsValue()) { instructions.Add(opcode); } else { if (opcode.IsFunction()) { stack.Push(opcode); } else { if (opcode.IsFunctionArgumentSeperator()) { while (stack.Count > 0) { if (stack.Peek().TokenType == TokenType.OpenBracket) break; SemanticToken o = stack.Pop(); instructions.Add(o); } } else { if (opcode.IsOperator()) { while (stack.Count > 0) { SemanticToken peek = stack.Peek(); if ((opcode.Precedence < peek.Precedence) || opcode.OperatorAssociativity == OperatorAssociativity.Left && opcode.Precedence == peek.Precedence) { instructions.Add(stack.Pop()); } else { break; } } stack.Push(opcode); } else { // Left Bracket if (opcode.TokenType == TokenType.OpenBracket) stack.Push(opcode); if (opcode.TokenType == TokenType.CloseBracket) { SemanticToken token = stack.Pop(); while (stack.Count > 0 && token.TokenType != TokenType.OpenBracket) { instructions.Add(token); token = stack.Pop(); } if (token.TokenType != TokenType.OpenBracket) throw new GossipScriptException("Mismatched brackets"); } } } } } } // When there are no more tokens to read while (stack.Count > 0) { SemanticToken op = stack.Pop(); if (op.IsBracket()) throw new GossipScriptException("Mismatched brackets"); instructions.Add(op); } // Apply the instructs to the expression and determine the return type ExtendedTypeAdapter result = m_ExpressionAnalyzer.DetermineReturnType(instructions, compilationContext); exp.Instructions = ConvertToInstructions(instructions); exp.ReturnType = result.TypeAdapter.GossipType; exp.HostReturnType = result.TypeAdapter.HostType; // TODO split into ExpressionInfo and Expression classes exp.HostData = (Int32) result.Data; exp.Target = (Int32) result.Target; return exp; }
private ActionNodeRoot CompileEvent(List<Token> tokens, Type selfTarget, StringTable stringTable, Type eventType = null) { var commandNodeRoot = new ActionNodeRoot(); var compilationContext = new CompilationContext(m_HostCallTable, stringTable, m_Engine.HostBridge.EnumDefineTable, m_Engine.HostBridge.TypeBindingTable, m_Engine.HostBridge.EventBindingTable); compilationContext.ScriptOwnerType = selfTarget; compilationContext.EventType = eventType; commandNodeRoot.Next = Compile(tokens, compilationContext); return commandNodeRoot; }
private int ParseApiCalls(CompilationContext compilationContext, string currentTokenValue, int index, List<Token> tokens, Token currentToken, ref IActionNode currentParent) { bool match = false; foreach (ActionInfo function in m_Engine.GlobalActions.Values) { if (currentTokenValue == function.TokenName) { List<Expression> parametersAsExpressions; int skip = ParseFunctionParameterExpressions(index, tokens, compilationContext,out parametersAsExpressions); currentParent.Next = new ExecuteAction(function, parametersAsExpressions) {Next = new ActionNode()}; currentParent = currentParent.Next; match = true; index += skip; currentToken = tokens[index]; break; } } if (match == false) { foreach (HostCall function in m_Engine.HostBridge.HostCallTable.Values) { if (function.ReturnType == GossipType.ReferenceObject) { // First check if we are going to encounter an assignment Int32 enounterAssignmentIndex = 0; if (EncounterAssignment(index, tokens, ref enounterAssignmentIndex)) { return ParsePropertySet(compilationContext, index, tokens, ref currentParent, enounterAssignmentIndex); } Expression expression; int skip = ParseExpression(index, tokens, compilationContext, out expression); if (tokens[index + skip].Type == TokenType.ReferenceAction) { index = index + skip; currentToken = tokens[index]; // Create Action ActionBinding action = compilationContext.TypeBindingTable.GetActionByName(currentToken.Value, expression.HostReturnType); ICustomActionNode node = action.CreateActionNode(); List<Expression> expressions; skip = ParseActionParameters(index + 1, tokens, compilationContext, out expressions); if (action.NumParameters != expressions.Count) throw new GossipScriptParameterException( String.Format("Invalid number of parameters for action:{0} found:{1} expected:{2}", action.Name, expressions.Count, action.NumParameters)); var customActionContext = new CustomActionContext(expressions, expression); currentParent.Next = new ExecuteCustomActionNode(node, customActionContext); currentParent = currentParent.Next; index += (skip + 1); currentToken = tokens[index]; return index; } throw new NotImplementedException( "TODO anything that returns a reference might want to be invoked"); } } } if (match == false) throw new GossipScriptException("Encountered Unknown Token", currentToken); return index; }
public Int32 ParseActionParameters(Int32 startIndex, List<Token> tokenStream, CompilationContext context, out List<Expression> expressions) { if (tokenStream[startIndex].Type != TokenType.OpenBracket) throw new GossipScriptException("Expected Open bracket as first token"); expressions = new List<Expression>(); int height = 1; int currentIndex = startIndex; var tokens = new List<Token>(); for (int i = 1; i < tokenStream.Count; i++) { currentIndex = i + startIndex; if (tokenStream[currentIndex].Type == TokenType.OpenBracket) height++; if (tokenStream[currentIndex].Type == TokenType.CloseBracket) height--; if (height == 0 || tokenStream[currentIndex].Type == TokenType.FunctionArgumentSeperator) { // Parse if (tokens.Count > 0) { Expression expression = ExpressionParser.Parse(tokens, context); expressions.Add(expression); tokens.Clear(); } } else { tokens.Add(tokenStream[currentIndex]); } if (height == 0) break; } return currentIndex - startIndex; }
private IActionNode Compile(List<Token> tokenlist, CompilationContext compilationContext) { Precompiler.CompilationContext = compilationContext; List<Token> tokens = m_Precompiler.ApplyPrecompileSemantic(tokenlist); IActionNode rootNode = new ActionNode(); IActionNode currentParent = rootNode; int index = -1; while (index < tokens.Count - 2) { Token currentToken = tokens[++index]; string currentTokenValue = tokens[index].Value; switch (currentToken.Type) { case TokenType.EndStatement: continue; case TokenType.EvaluateExpression: index = ParseEvaluateExpression(compilationContext, tokens, index, ref currentParent); continue; case TokenType.ParallelBlock: index = ParseParallelBlock(compilationContext, index, tokens, ref currentParent); continue; case TokenType.LocalStringAccess: case TokenType.LocalStringAccessEnum: case TokenType.GlobalStringAccess: case TokenType.GlobalStringAccessEnum: index = ParseStringAccess(compilationContext, currentToken, tokens, index, ref currentParent); continue; case TokenType.LocalVariableAccess: case TokenType.LocalVariableAccessEnum: case TokenType.GlobalVariableAccess: case TokenType.GlobalVariableAccessEnum: index = ParseVariableAccess(compilationContext, currentToken, tokens, index, ref currentParent); continue; case TokenType.GlobalFlagAccess: case TokenType.GlobalFlagAccessEnum: case TokenType.LocalFlagAccess: case TokenType.LocalFlagAccessEnum: index = ParseFlagAccess(compilationContext, currentToken, tokens, index, ref currentParent); continue; case TokenType.Event: index = ParseEvent(compilationContext, tokens, index, ref currentParent); continue; case TokenType.Self: index = ParseSelf(compilationContext, tokens, index, ref currentParent); continue; //case TokenType.ModuleAccess: // index = ParseModuleAccess(compilationContext, index, tokens, ref currentParent); // continue; case TokenType.ReferenceObjectAccess: case TokenType.ReferenceObjectAccessEnum: index = ParseReferenceObjectAccess(compilationContext, currentToken, tokens, index, ref currentParent); continue; case TokenType.WaitStatement: index = ParseWait(tokens, index, ref currentParent); continue; case TokenType.If: index = ParseConditional(compilationContext, tokens, index, ref currentParent); continue; case TokenType.Else: throw new GossipScriptException("Else without If", currentToken); case TokenType.Yeild: currentParent = ParseYeild(currentParent, tokens, ref index); continue; case TokenType.Return: currentParent = ParseReturn(currentParent, tokens, ref index); continue; case TokenType.Exit: currentParent = ParseExit(currentParent, tokens, ref index); continue; } // If none of the above in the switch statement index = ParseApiCalls(compilationContext, currentTokenValue, index, tokens, currentToken, ref currentParent); } return rootNode; }
private int ParseVariableAccess(CompilationContext compilationContext, Token currentToken, List<Token> tokens, int index, ref IActionNode currentParent) { int varIndex = currentToken.GetAccessIndex(m_Engine.HostBridge.EnumDefineTable); // Map enums to their index counterparts if (currentToken.Type == TokenType.LocalVariableAccessEnum) currentToken.Type = TokenType.LocalVariableAccess; if (currentToken.Type == TokenType.GlobalVariableAccessEnum) currentToken.Type = TokenType.GlobalVariableAccess; // Global or local? var op = TokenType.GlobalVariableAccess; if (currentToken.Type == TokenType.LocalVariableAccess) op = TokenType.LocalVariableAccess; currentToken = tokens[++index]; // Assignment or increment or decrement if (currentToken.Value != "=" && currentToken.Value != "++" && currentToken.Value != "--" && currentToken.Value != "+=" && currentToken.Value != "-=") throw new GossipScriptException("Unexpected Token following variable access", currentToken); if (currentToken.Value == "++") { currentParent.Next = new ModifyVariable(varIndex, op, TokenType.Increment, 1); currentParent = currentParent.Next; return index; } if (currentToken.Value == "--") { currentParent.Next = new ModifyVariable(varIndex, op, TokenType.Decrement, 1); currentParent = currentParent.Next; return index; } if (currentToken.Type == TokenType.Assignment || currentToken.Type == TokenType.IncrementAndAssign || currentToken.Type == TokenType.DecrementAndAssign) { TokenType tokenType = currentToken.Type; currentToken = tokens[++index]; // First token after assignment Expression expression; int skip = ParseExpression(index, tokens, compilationContext, out expression); if (expression.ReturnType != GossipType.Number) throw new GossipScriptException(String.Format("Invalid return type:{0}, Expected Number", expression.ReturnType)); index += skip; currentToken = tokens[index]; // Specal case if (op == TokenType.LocalVariableAccess && currentToken.Type == TokenType.Assignment && expression.Instructions.Count == 1) { currentParent.Next = new AssignLocalVariableWithLiteral(varIndex, Convert.ToDouble( expression.Instructions[0].Data)); currentParent = currentParent.Next; return index; } // Generic Case currentParent.Next = new AssignVariable(varIndex, op, tokenType, expression); currentParent = currentParent.Next; return index; } return index; }
private int ParseStringAccess(CompilationContext compilationContext, Token currentToken, List<Token> tokens, int index, ref IActionNode currentParent) { int destIndex = currentToken.GetAccessIndex(m_Engine.HostBridge.EnumDefineTable); // Map enums to their index counterparts if (currentToken.Type == TokenType.LocalStringAccessEnum) currentToken.Type = TokenType.LocalStringAccess; if (currentToken.Type == TokenType.GlobalFlagAccessEnum) currentToken.Type = TokenType.GlobalFlagAccess; // Global or local? var destScope = VariableScope.Global; if (currentToken.Type == TokenType.LocalStringAccess) destScope = VariableScope.Local; currentToken = tokens[++index]; if (currentToken.Type != TokenType.Assignment) throw new GossipScriptException("Unexpected Token following string access", currentToken); Token op = currentToken; currentToken = tokens[++index]; if (currentToken.Type == TokenType.StringLiteral) { string stringValue = currentToken.Value.Substring(1, currentToken.Value.Length - 2); currentParent.Next = new AssignStringLiteral(destIndex, destScope, stringValue); currentParent = currentParent.Next; return index; } // If none of the above it must be an expression Expression expression; int skipTokens = ParseExpression(index, tokens, compilationContext, out expression); index += skipTokens; currentToken = tokens[index]; currentParent.Next = new AssignString(destIndex, destScope, expression); currentParent = currentParent.Next; return index; }
public ExtendedTypeAdapter DetermineReturnType(List<SemanticToken> tokens, CompilationContext context) { var stack = new Stack<ExtendedTypeAdapter>(tokens.Count); foreach (SemanticToken opcode in tokens) { if (opcode.IsNumber()) { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); } else { // Evaluate opcode switch (opcode.TokenType) { case TokenType.ModuleAccess: { var result = new ExtendedTypeAdapter(GossipType.ReferenceModule); result.Data = opcode.Data; // Module Id stack.Push(result); break; } case TokenType.Event: { var adapter = new ExtendedTypeAdapter(GossipType.ReferenceObject); adapter.Data = -2; // Reference type -2 indicates the event object // adapter.Target = opcode.Data; stack.Push(adapter); break; } case TokenType.Self: { var adapter = new ExtendedTypeAdapter(GossipType.ReferenceObject); adapter.Data = -1; // Reference type -1 indicates the script owner stack.Push(adapter); break; } case TokenType.Addition: { GossipType rhs = stack.Pop().TypeAdapter.GossipType; GossipType lhs = stack.Pop().TypeAdapter.GossipType; if (rhs == GossipType.String && lhs == GossipType.String) { opcode.TokenType = TokenType.StringStringConcatination; stack.Push(new ExtendedTypeAdapter(GossipType.String)); break; } if (rhs == GossipType.String && lhs == GossipType.Number) { opcode.TokenType = TokenType.DoubleStringConcatination; stack.Push(new ExtendedTypeAdapter(GossipType.String)); break; } if (rhs == GossipType.Number && lhs == GossipType.String) { opcode.TokenType = TokenType.StringDoubleConcatination; stack.Push(new ExtendedTypeAdapter(GossipType.String)); break; } if (rhs == GossipType.Number && lhs == GossipType.Number) { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } throw new GossipScriptException( String.Format("Invalid operands: {0},{1} on operator:{2}", rhs, lhs, opcode.TokenType)); break; } case TokenType.HostFunctionCall: { var functionId = (Int32) opcode.Data; HostCall function = context.HostCallTable.GetFunctionById(functionId); for (int i = 0; i < function.NumParameters; i++) { stack.Pop(); // TODO Check function parameter types are expected } stack.Push(new ExtendedTypeAdapter(function.ReturnType, function.HostType)); break; } case TokenType.Negation: { GossipType rhs = stack.Peek().TypeAdapter.GossipType; if (rhs != GossipType.Number) throw new GossipScriptException(String.Format("Invalid operator:{0} on string", opcode.TokenType)); break; } case TokenType.Subtract: case TokenType.Multiply: case TokenType.Divide: case TokenType.PowerOf: case TokenType.UnaryMinus: case TokenType.Modulo: case TokenType.LogicalAnd: case TokenType.LogicalOr: { GossipType rhs = stack.Pop().TypeAdapter.GossipType; GossipType lhs = stack.Pop().TypeAdapter.GossipType; if (rhs == GossipType.String || lhs == GossipType.String) throw new GossipScriptException(String.Format("Invalid operator:{0} on string", opcode.TokenType)); stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } case TokenType.Equal: case TokenType.NotEqual: case TokenType.GreaterThan: // TODO Implement for strings case TokenType.LessThan: // TODO Implement for strings case TokenType.GreaterThanOrEqualTo: // TODO Implement for strings case TokenType.LessThanOrEqualTo: // TODO Implement for strings { GossipType rhs = stack.Pop().TypeAdapter.GossipType; GossipType lhs = stack.Pop().TypeAdapter.GossipType; if (rhs == GossipType.String && lhs == GossipType.String) { opcode.TokenType = OperatorToStringOperator(opcode.TokenType); stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } if (rhs == GossipType.Number && lhs == GossipType.Number) { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } if (rhs == GossipType.ReferenceObject && lhs == GossipType.ReferenceObject) { opcode.TokenType = OperatorToReferenceOperator(opcode.TokenType); stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } throw new GossipScriptException( String.Format("Invalid operands: {0},{1} on operator:{2}", rhs, lhs, opcode.TokenType)); break; } case TokenType.LocalVariableAccess: case TokenType.GlobalVariableAccess: case TokenType.GlobalFlagAccess: case TokenType.LocalFlagAccess: { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } case TokenType.ReferenceObjectAccess: { var result = new ExtendedTypeAdapter(GossipType.ReferenceObject); result.Data = opcode.Data; stack.Push(result); break; } case TokenType.GlobalStringAccess: case TokenType.LocalStringAccess: case TokenType.StringLiteral: { stack.Push(new ExtendedTypeAdapter(GossipType.String)); break; } case TokenType.StringEquality: { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } case TokenType.StringInequality: { stack.Push(new ExtendedTypeAdapter(GossipType.Number)); break; } case TokenType.ReferenceAction: { ExtendedTypeAdapter stackObject = stack.Pop(); // The reference object Type objType = stackObject.TypeAdapter.HostType; var target = (Int32) TargetObjectType.TempReferenceObject; // Target Temp Object if (objType == null) // Unknown object, need to look at the assignment table { var id = (Int32) stackObject.Data; // Reference Index if (context.ReferenceAssignmentTable.HasAssignment(id) == false) throw new GossipScriptException( String.Format("Ref[{0}] has not been assigned any value", id)); ReferenceAssignment reference = context.ReferenceAssignmentTable.GetAssignment(id); objType = reference.Type; target = id; // Ref index } string name = opcode.StringData; // Should be name of Action ActionBinding action = context.TypeBindingTable.GetActionByName(name, objType); opcode.Data = action.Id; // write back the id // Pop off number of parameters from the stack for (int i = 0; i < action.NumParameters; i++) { TypeAdapter parameter = stack.Pop().TypeAdapter; if (action.ParameterInfo[i].GossipType != parameter.GossipType) { throw new GossipScriptException( String.Format( "Invalid parameter type:{0} for parameter #{1} expected type:{2}", parameter.GossipType, i + 1, action.ParameterInfo[i].GossipType)); } } var typeAdapter = new ExtendedTypeAdapter(GossipType.Action, objType) { Data = opcode.Data, // Action Id Target = target }; stack.Push(typeAdapter); break; } //case TokenType.ModulePropertyGet: // { // ExtendedTypeAdapter stackObject = stack.Pop(); // // This should be the object to call the method on // var moduleId = (Int32) stackObject.Data; // ModuleBinding module = context.ModuleBindingTable.GetModuleById(moduleId); // string name = opcode.StringData; // Should be name of method // PropertyGetBinding method = context.ModuleBindingTable.GetPropertyGetByName(name, // module // .ModuleType); // opcode.Data = method.Id; // write back the id // var typeAdapter = new ExtendedTypeAdapter(method.GossipType, method.HostType, // (Int32) opcode.Data); // stack.Push(typeAdapter); // break; // } //case TokenType.ModuleMethodCall: // { // ExtendedTypeAdapter stackObject = stack.Pop(); // // This should be the object to call the method on // var moduleId = (Int32) stackObject.Data; // ModuleBinding module = context.ModuleBindingTable.GetModuleById(moduleId); // string name = opcode.StringData; // Should be name of method // MethodBinding method = context.ModuleBindingTable.GetMethodBindingByName(name, // module // .ModuleType); // opcode.Data = method.Id; // write back the id // // Pop off number of parameters from the stack // for (int i = 0; i < method.NumParameters; i++) // { // TypeAdapter parameter = stack.Pop().TypeAdapter; // if (method.ParameterInfo[i].GossipType != parameter.GossipType) // throw new GossipScriptParameterException( // String.Format( // "Invalid parameter type:{0} for parameter #{1} expected type:{2}", // parameter.GossipType, i + 1, method.ParameterInfo[i].GossipType)); // } // var typeAdapter = new ExtendedTypeAdapter(method.GossipType, method.HostReturnType, // (Int32) opcode.Data); // stack.Push(typeAdapter); // break; // } case TokenType.MethodCall: { ExtendedTypeAdapter stackObject = stack.Pop(); // This should be the object to call the method on Type objType = stackObject.TypeAdapter.HostType; if (objType == null) // Unknown object, need to look at the assignment table { var id = (Int32) stackObject.Data; // Reference Index if (id == -1) { objType = context.ScriptOwnerType; } else { if (context.ReferenceAssignmentTable.HasAssignment(id) == false) throw new GossipScriptException( String.Format("Ref[{0}] has not been assigned any value", id)); ReferenceAssignment reference = context.ReferenceAssignmentTable.GetAssignment(id); objType = reference.Type; } } string name = opcode.StringData; // Should be name of method MethodBinding method = context.TypeBindingTable.GetMethodByName(name, objType); opcode.Data = method.Id; // write back the id // Pop off number of parameters from the stack for (int i = 0; i < method.NumParameters; i++) { TypeAdapter parameter = stack.Pop().TypeAdapter; if (method.ParameterInfo[i].GossipType != parameter.GossipType) { throw new GossipScriptParameterException( String.Format( "Invalid parameter type:{0} for parameter #{1} expected type:{2}", parameter.GossipType, i + 1, method.ParameterInfo[i].GossipType)); } } var typeAdapter = new ExtendedTypeAdapter(method.GossipType, method.HostReturnType) { Data = opcode.Data }; stack.Push(typeAdapter); break; } case TokenType.ReferencePropertyGet: { ExtendedTypeAdapter stackObject = stack.Pop(); Type objType = stackObject.TypeAdapter.HostType; if (objType == null) // Unknown object, need to look at the assignment table { var id = (Int32) stackObject.Data; // Reference Index if (id == -1) { objType = context.ScriptOwnerType; } else { if (id == -2) { objType = context.EventType; } else { if (context.ReferenceAssignmentTable.HasAssignment(id) == false) throw new GossipScriptException( String.Format("Ref[{0}] has not been assigned any value", id)); ReferenceAssignment reference = context.ReferenceAssignmentTable.GetAssignment(id); objType = reference.Type; } } } string index = opcode.StringData; // Property Index PropertyGetBinding property = context.TypeBindingTable.GetPropertyGetByName(index, objType); opcode.Data = property.Id; // write back the id stack.Push(new ExtendedTypeAdapter(property.GossipType, property.HostType)); break; } default: throw new Exception(String.Format("Unknown operator{0}", opcode.TokenType)); break; } } } if (stack.Count > 1) throw new Exception("Stack should only have one remaining entry"); ExtendedTypeAdapter r = stack.Pop(); return r; }
private int ParseParallelBlock(CompilationContext compilationContext, int index, List<Token> tokens, ref IActionNode currentParent) { Token currentToken; var blockStream = new List<Token>(); int skipTokens = GetInnerBlock(index, tokens, ref blockStream); // Compile consequence IActionNode consequnce = Compile(blockStream, compilationContext); index += skipTokens; currentToken = tokens[index]; var conditionalCommand = new ParallelNode(consequnce); currentParent.Next = conditionalCommand; currentParent = currentParent.Next; return index; }
private int ParseConditional(CompilationContext compilationContext, List<Token> tokens, int index, ref IActionNode currentParent) { Token currentToken; currentToken = tokens[++index]; // Open Bracket AssertExpectedTokenValue(currentToken, "(", "Expected Open Bracket"); currentToken = tokens[++index]; // Parameters // Parse logical expression Expression expression; int skipTokens = ParseExpression(index - 1, tokens, compilationContext, out expression); index += (skipTokens - 2); currentToken = tokens[index]; // Close Bracket AssertExpectedTokenValue(currentToken, ")", "Expected Close Bracket"); // Obtain consequence block var blockStream = new List<Token>(); skipTokens = GetInnerBlock(index, tokens, ref blockStream); // Compile consequence IActionNode consequnce = Compile(blockStream, compilationContext); // Jump ahead in the Token stream index += skipTokens; if (index >= tokens.Count) throw new GossipScriptException("Unexpected end of input"); currentToken = tokens[index]; // Obtain Else consequence IActionNode elseConsequnce = null; if (tokens[index + 1].Value == "else") { var elseBlockStream = new List<Token>(); skipTokens = GetInnerBlock(index + 1, tokens, ref elseBlockStream); elseConsequnce = Compile(elseBlockStream, compilationContext); // Jump ahead in the Token stream index += (skipTokens + 1); currentToken = tokens[index]; } var conditionalCommand = new ConditionalNode(expression, consequnce, elseConsequnce); currentParent.Next = conditionalCommand; currentParent = currentParent.Next; return index; }
private int ParsePropertySet(CompilationContext compilationContext, int index, List<Token> tokens, ref IActionNode currentParent, int enounterAssignmentIndex) { Expression lhsExpression; int lhsSkip = ParseExpression(index, tokens, compilationContext, out lhsExpression); Token propertyToken = tokens[enounterAssignmentIndex - 1]; if (propertyToken.Type != TokenType.ReferencePropertySet) throw new GossipScriptException("Expected property set"); // Get Set Method PropertySetBinding setMethod = m_Engine.HostBridge.TypeBindingTable.GetPropertySetByName( propertyToken.Value, lhsExpression.HostReturnType); // Skip Assignment Token currentToken = tokens[enounterAssignmentIndex]; if (currentToken.Type != TokenType.Assignment) throw new Exception("Exprected assignment"); index = enounterAssignmentIndex + 1; // Parse Right hand side Expression rhsExpression; int rhsSkip = ParseExpression(index, tokens, compilationContext, out rhsExpression); currentParent.Next = new AssignReferenceProperty(lhsExpression, setMethod.Id, rhsExpression); currentParent = currentParent.Next; return index + rhsSkip; }
private int ParseEvaluateExpression(CompilationContext compilationContext, List<Token> tokens, int index, ref IActionNode currentParent) { Token currentToken = tokens[++index]; AssertExpectedTokenValue(currentToken, "(", "Expected Open Bracket"); currentToken = tokens[++index]; // Parameters // Parse logical expression Expression expression; int skipTokens = ParseExpression(index - 1, tokens, compilationContext, out expression); index += (skipTokens - 2); currentToken = tokens[index]; // Close Bracket AssertExpectedTokenValue(currentToken, ")", "Expected Close Bracket"); var conditionalCommand = new EvalAction(expression); currentParent.Next = conditionalCommand; currentParent = currentParent.Next; return index; }
private int ParseSelf(CompilationContext compilationContext, List<Token> tokens, int index, ref IActionNode currentParent) { Token currentToken = tokens[++index]; if (currentToken.Type == TokenType.ReferenceAction) { // Create an action ActionBinding action = compilationContext.TypeBindingTable.GetActionByName(currentToken.Value, compilationContext .ScriptOwnerType); ICustomActionNode node = action.CreateActionNode(); List<Expression> expressions; int skip = ParseActionParameters(index + 1, tokens, compilationContext, out expressions); if (action.NumParameters != expressions.Count) throw new GossipScriptParameterException(String.Format("Invalid number of parameters for action:{0} found:{1} expected:{2}", action.Name,expressions.Count, action.NumParameters)); var customActionContext = new CustomActionContext(expressions); currentParent.Next = new ExecuteCustomActionNode(node, customActionContext); currentParent = currentParent.Next; index += (skip + 1); currentToken = tokens[index]; return index; } if (currentToken.Type == TokenType.ReferencePropertySet) { Token operand = currentToken; Expression expression; currentToken = tokens[++index]; AssertExpectedTokenType(currentToken, TokenType.Assignment, "Unexpected token"); currentToken = tokens[++index]; int skip = ParseExpression(index, tokens, compilationContext, out expression); index += skip; currentToken = tokens[index]; if ( !m_Engine.HostBridge.TypeBindingTable.SetPropertyExists(operand.Value, compilationContext.ScriptOwnerType)) throw new GossipScriptException(String.Format("Property {0} not found or is read only.", operand.Value)); PropertySetBinding setter = m_Engine.HostBridge.TypeBindingTable.GetPropertySetByName(operand.Value, compilationContext .ScriptOwnerType); if (setter.ParameterType != expression.ReturnType) throw new GossipScriptException("Incompatile property set parameter"); currentParent.Next = new AssignReferenceProperty(-1, setter.Id, expression); currentParent = currentParent.Next; return index; } // Parse Action { // First check if we are going to encounter an assignment Int32 enounterAssignmentIndex = 0; if (EncounterAssignment(index, tokens, ref enounterAssignmentIndex)) { return ParsePropertySet(compilationContext, index - 1, tokens, ref currentParent, enounterAssignmentIndex); } Expression expression; int skip = ParseExpression(index - 1, tokens, compilationContext, out expression); index += (skip); currentToken = tokens[index - 1]; if (currentToken.Type == TokenType.ReferenceAction) { // Create an action ActionBinding action = compilationContext.TypeBindingTable.GetActionByName(currentToken.Value, expression.HostReturnType); ICustomActionNode node = action.CreateActionNode(); List<Expression> expressions; skip = ParseActionParameters(index, tokens, compilationContext, out expressions); if (action.NumParameters != expressions.Count) throw new GossipScriptParameterException( String.Format("Invalid number of parameters for action:{0} found:{1} expected:{2}", action.Name, expressions.Count, action.NumParameters)); var customActionContext = new CustomActionContext(expressions, expression); currentParent.Next = new ExecuteCustomActionNode(node, customActionContext); currentParent = currentParent.Next; index += (skip + 1); currentToken = tokens[index]; return index; } } throw new Exception("Unexpected token"); }
public IEnumerable<SemanticToken> ApplyExpressionSemantics(IEnumerable<Token> tokenStream, CompilationContext compilationContext) { List<Token> tokens = tokenStream.ToList(); // Preprocess semantics for (int i = 0; i < tokens.Count; i++) { if (tokens[i].Type == TokenType.Subtract) { if (tokens.Count - 1 < i + 1) throw new Exception("Unexpected end of token stream"); if (tokens[i + 1].Type == TokenType.OpenBracket) tokens[i].Type = TokenType.UnaryMinus; if (i > 0 && tokens[i - 1].OperationType == OperationType.Operator) tokens[i].Type = TokenType.UnaryMinus; if (i > 0 && tokens[i - 1].Type == TokenType.OpenBracket) tokens[i].Type = TokenType.UnaryMinus; if (i == 0) tokens[i].Type = TokenType.UnaryMinus; } // Missing Semi colon checks if (i > 0) { if (tokens[i].Type == TokenType.HostFunctionCall) { if (tokens[i - 1].Type != TokenType.OpenBracket && tokens[i - 1].OperationType != OperationType.Operator && tokens[i - 1].Type != TokenType.FunctionArgumentSeperator) { throw new GossipScriptMissingSemiColonException("Missing SemiColon", tokens[i]); } } } } // Convert to semantic tokens var rv = new List<SemanticToken>(tokens.Count()); for (int i = 0; i < tokens.Count; i++) { Token token = tokens[i]; if (token.Type == TokenType.EndStatement || token.Type == TokenType.EndOfProgram) continue; var t = new SemanticToken {TokenType = token.Type, OperationType = OperationType.Operator}; switch (token.Type) { case TokenType.DecimalLiteral: t.OperationType = OperationType.Operand; t.Data = Double.Parse(token.Value); break; case TokenType.Addition: t.Precedence = 6; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.Subtract: t.Precedence = 6; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.Multiply: t.Precedence = 7; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.Divide: t.Precedence = 7; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.PowerOf: t.Precedence = 7; t.OperatorAssociativity = OperatorAssociativity.Right; break; case TokenType.UnaryMinus: t.Precedence = 8; t.OperatorAssociativity = OperatorAssociativity.Right; break; case TokenType.Negation: t.Precedence = 8; t.OperatorAssociativity = OperatorAssociativity.Right; break; case TokenType.Modulo: t.Precedence = 7; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.GreaterThan: t.Precedence = 5; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.GreaterThanOrEqualTo: t.Precedence = 5; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.LessThan: t.Precedence = 5; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.LessThanOrEqualTo: t.Precedence = 5; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.Equal: t.Precedence = 4; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.NotEqual: t.Precedence = 4; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.LogicalAnd: t.Precedence = 3; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.LogicalOr: t.Precedence = 2; t.OperatorAssociativity = OperatorAssociativity.Left; break; case TokenType.HostFunctionCall: HostCall function = compilationContext.HostCallTable.GetFunctionByName(token.Value); t.OperationType = OperationType.FunctionCall; t.Precedence = 9; t.NumOperands = function.NumParameters; t.Data = function.Id; break; case TokenType.FunctionArgumentSeperator: t.OperationType = OperationType.None; break; case TokenType.LocalFlagAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.LocalVariableAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.GlobalFlagAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.GlobalVariableAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.TrueLiteral: t.TokenType = TokenType.DecimalLiteral; t.OperationType = OperationType.Operand; t.Data = 1; t.Precedence = 9; break; case TokenType.FalseLiteral: t.TokenType = TokenType.DecimalLiteral; t.OperationType = OperationType.Operand; t.Precedence = 9; break; case TokenType.OpenBracket: t.OperationType = OperationType.None; break; case TokenType.CloseBracket: t.OperationType = OperationType.None; break; case TokenType.Assignment: throw new GossipScriptException("Assignment operator not allowed within an expression"); break; case TokenType.DecrementAndAssign: throw new GossipScriptException("Decrement operator not allowed within an expression"); break; case TokenType.IncrementAndAssign: throw new GossipScriptException("Decrement operator not allowed within an expression"); break; case TokenType.LocalStringAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.GlobalStringAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.StringLiteral: t.TokenValue = token.Value; t.OperationType = OperationType.Operand; string data = token.Value.Substring(1, token.Value.Length - 2); // Remove brackets t.Data = compilationContext.StringTable.GetIdByString(data); break; case TokenType.ReferenceObjectAccess: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); break; case TokenType.GlobalStringAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.GlobalStringAccess; break; case TokenType.LocalStringAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.LocalStringAccess; break; case TokenType.LocalVariableAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.LocalVariableAccess; break; case TokenType.GlobalVariableAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.GlobalVariableAccess; break; case TokenType.LocalFlagAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.LocalFlagAccess; break; case TokenType.GlobalFlagAccessEnum: t.OperationType = OperationType.Operand; t.Precedence = 9; t.Data = token.GetAccessIndex(compilationContext.EnumTable); t.TokenType = TokenType.GlobalFlagAccess; break; case TokenType.Event: case TokenType.Self: { t.OperationType = OperationType.Operand; t.Precedence = 9; break; } case TokenType.ReferencePropertyGet: { t.OperationType = OperationType.PropertyCall; t.Precedence = 9; t.StringData = token.Value; t.Data = -666; // Unknown at this stage break; } case TokenType.MethodCall: { t.OperationType = OperationType.MethodCall; t.Precedence = 9; t.StringData = token.Value; t.Data = -666; // Unknown at this stage break; } case TokenType.ReferenceAction: { t.OperationType = OperationType.CreateAction; t.Precedence = 9; t.StringData = token.Value; t.Data = -666; // Unknown at this stage break; } //case TokenType.ModuleAccess: // { // t.OperationType = OperationType.Operand; // t.Precedence = 9; // t.StringData = token.Value; // t.Data = compilationContext.ModuleBindingTable.GetModuleByName(token.Value).Id; // break; // } case TokenType.ModuleMethodCall: { t.OperationType = OperationType.MethodCall; t.Precedence = 9; t.StringData = token.Value; t.Data = -666; // Unknown at this stage break; } case TokenType.ModulePropertyGet: { t.OperationType = OperationType.PropertyCall; t.Precedence = 9; t.StringData = token.Value; t.Data = -666; // Unknown at this stage break; } case TokenType.Enum: { t.TokenType = TokenType.DecimalLiteral; t.OperationType = OperationType.Operand; t.Data = compilationContext.EnumTable.DefinesByString[token.Value]; t.Precedence = 9; break; } default: throw new NotImplementedException(t.TokenType.ToString()); break; } rv.Add(t); } ValidateSemantics(rv); return rv; }