/// <summary> /// Adds an operand to the expression. /// </summary> /// <param name="token">Token to add as an operand</param> /// <param name="addToFirst">Whether to add operand as first operand</param> public void AddOperand(Token token, bool addToFirst) { if ( addToFirst ) { FirstOperand = token; } else { SecondOperand = token; } }
/// <summary> /// Adds a new semantic error /// </summary> /// <param name="token">Token</param> /// <param name="message">Message</param> public static void AddSemanticError(Token token, string message) { if ( token != null ) { SemanticErrors.Add( new Error(Lines[token.Line - 1], token.Line, token.StartColumn, message)); } else { SemanticErrors.Add( new Error(Lines[Lines.Count - 1], Lines.Count, Lines[Lines.Count - 1].Length, message)); } }
/// <summary> /// Adds a syntax error to the list of errors /// </summary> /// <param name="token">Current token</param> /// <param name="expectedLexeme">Expected lexeme</param> /// <param name="useDefault">Use default message: "Was expecting {expectedLexeme}."</param> private void AddSyntaxError(Token token, string expectedLexeme, bool useDefault = true) { if (token == null) { _syntaxErrors.Add( new Error(Scanner.Lines[Scanner.Lines.Count - 1], Scanner.Lines.Count - 1, Scanner.Lines[Scanner.Lines.Count - 1].Length, String.Format("Unexpected end of file, was expecting {0}.", expectedLexeme))); return; } if (useDefault) { _syntaxErrors.Add( new Error(Scanner.Lines[token.Line - 1], token.Line, token.StartColumn, String.Format("Was expecting {0}.", expectedLexeme))); } else { _syntaxErrors.Add( new Error(Scanner.Lines[token.Line - 1], token.Line, token.StartColumn, expectedLexeme)); } }
/// <summary> /// Checks the token for null reference and correct lexeme /// </summary> /// <param name="token">Current token</param> /// <param name="lexeme">Lexeme expected</param> /// <returns>True if ok</returns> public static bool CheckToken(Token token, string lexeme) { return token != null && token.Lexeme == lexeme; }
/// <summary> /// Extracts string value from a terminal token or variable. /// Basicly this is ToString() for tokenterminal and variables of type t /// </summary> /// <param name="token">Terminal or variable token</param> /// <returns>Value</returns> private static string ExtractString(Token token) { var tokenTerminal = token as TokenTerminal<string>; if ( tokenTerminal != null ) { return tokenTerminal.Value; } var tokenIdentifier = token as TokenIdentifier; if ( tokenIdentifier != null ) { var s = Statement.GetVariable(tokenIdentifier.Identifier) as VariableType<string>; if ( s != null ) { return s.Value; } var i = Statement.GetVariable(tokenIdentifier.Identifier) as VariableType<int>; if ( i != null ) { return i.Value.ToString(); } var b = Statement.GetVariable(tokenIdentifier.Identifier) as VariableType<bool>; if ( b != null ) { return b.Value.ToString().ToLower(); } } try { return ExtractInt(token).ToString(); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } try { return ExtractBool(token).ToString().ToLower(); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } Statements.AddSemanticError(token, "Was expecting a string value."); throw new AbstractSyntaxTreeException(""); }
/// <summary> /// Extracts integer value from terminal token or from variable token /// </summary> /// <param name="token">Terminal or variable token</param> /// <returns>Value</returns> private static int ExtractInt(Token token) { var tokenTerminal = token as TokenTerminal<int>; if ( tokenTerminal != null ) { return tokenTerminal.Value; } var tokenIdentifier = token as TokenIdentifier; if ( tokenIdentifier != null ) { var variable = Statement.GetVariable(tokenIdentifier.Identifier) as VariableType<int>; if ( variable != null ) { return variable.Value; } } Statements.AddSemanticError(token, "Was expecting an int value."); throw new AbstractSyntaxTreeException(""); }
/// <summary> /// Compares two string values /// </summary> /// <param name="token"> </param> /// <param name="firstValue">First value</param> /// <param name="op">Operator</param> /// <param name="secondValue">Second value</param> /// <returns>Comparison</returns> private static bool Compare(Token token, string firstValue, string op, string secondValue) { switch ( op ) { case Operators.Equal: return firstValue == secondValue; case Operators.NotEqual: return firstValue != secondValue; } Statements.AddSemanticError(token, "Can't compare values string and string with operator " + op); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Compares two integer values /// </summary> /// <param name="token"> </param> /// <param name="firstValue">First value</param> /// <param name="op">Operator</param> /// <param name="secondValue">Second value</param> /// <returns>Comparison</returns> private static bool Compare(Token token, int firstValue, string op, int secondValue) { switch (op) { case Operators.Equal: return firstValue == secondValue; case Operators.NotEqual: return firstValue != secondValue; case Operators.GreaterThan: return firstValue > secondValue; case Operators.GreaterOrEqualThan: return firstValue >= secondValue; case Operators.LesserThan: return firstValue < secondValue; case Operators.LesserOrEqualThan: return firstValue <= secondValue; } Statements.AddSemanticError(token, "Can't compare values int and int with operator " + op); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Calculates boolean value from expression and operand /// </summary> /// <param name="first">First expression</param> /// <param name="op">Operator</param> /// <param name="second">Second operand</param> /// <returns>Result</returns> private static bool CalculateBool(Expression first, string op, Token second) { try { var b1 = first.EvaluateBool(); var b2 = ExtractBool(second); return Calculate(first.GetFirstNotNullToken(), b1, op, b2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } try { var i1 = first.EvaluateInt(); var i2 = ExtractInt(second); return Compare(first.GetFirstNotNullToken(), i1, op, i2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } try { var s1 = first.EvaluateString(); var s2 = ExtractString(second); return Compare(first.GetFirstNotNullToken(), s1, op, s2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } Statements.AddSemanticError(first.GetFirstNotNullToken(), "Could not evaluate value."); throw new AbstractSyntaxTreeCalculateException("Couldn't evaluate."); }
/// <summary> /// Calculates boolean value from two operands /// </summary> /// <param name="first">First operand</param> /// <param name="op">Operator</param> /// <param name="second">Second operand</param> /// <returns>Result</returns> private static bool CalculateBool(Token first, string op, Token second) { try { var b1 = ExtractBool(first); var b2 = ExtractBool(second); return Calculate(first, b1, op, b2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } try { var i1 = ExtractInt(first); var i2 = ExtractInt(second); return Compare(first, i1, op, i2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } try { var s1 = ExtractString(first); var s2 = ExtractString(second); return Compare(first, s1, op, s2); } catch ( AbstractSyntaxTreeException ) { Statements.DeleteLastAddedError(); } Statements.AddSemanticError(first, "Could not evaluate value."); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Calculates string value from two operands /// </summary> /// <param name="token"> </param> /// <param name="firstValue">First value</param> /// <param name="op">Operator</param> /// <param name="secondValue">Second value</param> /// <returns>Result</returns> private static string Calculate(Token token, string firstValue, string op, string secondValue) { if (op == Operators.Plus) { return firstValue + secondValue; } Statements.AddSemanticError(token, String.Format("Can't apply operator '{0}' to a string operation.", op)); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Calculates integer value from two operands /// </summary> /// <param name="token"> </param> /// <param name="firstValue">First value</param> /// <param name="op">Operator</param> /// <param name="secondValue">Second value</param> /// <returns>Result</returns> private static int Calculate(Token token, int firstValue, string op, int secondValue) { switch ( op ) { case Operators.Plus: return firstValue + secondValue; case Operators.Minus: return firstValue - secondValue; case Operators.Multiply: return firstValue * secondValue; case Operators.Divide: return firstValue / secondValue; } Statements.AddSemanticError(token, String.Format("Can't apply operator '{0}' to operands 'int' and int'.", op)); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Calculates boolean value from two operands /// </summary> /// <param name="token"> </param> /// <param name="firstValue">First value</param> /// <param name="op">Operator</param> /// <param name="secondValue">Second value</param> /// <returns>Result</returns> private static bool Calculate(Token token, bool firstValue, string op, bool secondValue) { switch (op) { case Operators.Equal: return firstValue == secondValue; case Operators.NotEqual: return firstValue != secondValue; case Operators.And: return firstValue != secondValue; } Statements.AddSemanticError(token, String.Format("Can't apply operator '{0}' to operands 'bool' and 'bool'.", op)); throw new AbstractSyntaxTreeCalculateException(""); }
/// <summary> /// Produces tokens for the parser. /// </summary> /// <param name="lines">Source code</param> /// <returns>Tokens</returns> public List<Token> Tokenize(List<string> lines) { if ( lines == null ) { throw new ScannerException("Lines were null"); } Lines = lines; var tokens = new List<Token>(); for (_row = 0; _row < lines.Count; _row++) { _column = 0; var line = lines[_row]; while (_column < line.Length) { var stop = SkipWhiteSpace(line); if (stop) { break; } if ( _column < line.Length - 1 && line[_column] == '/' && line[_column + 1] == '*' ) // Multiline comment { SkipMultilineComment(lines); continue; } if (_column < line.Length - 1 && line[_column] == '/' && line[_column + 1] == '/') // line comment { break; } Token token; if ( (token = CreateTypeToken(line)) != null ) { tokens.Add(token); _previousToken = token; continue; } if ( (token = CreateReservedKeywordToken(line)) != null ) { tokens.Add(token); _previousToken = token; continue; } if ( (token = CreateOperatorToken(line)) != null ) { tokens.Add(token); _previousToken = token; continue; } if ( (token = CreateTerminalToken(line)) != null ) { tokens.Add(token); _previousToken = token; continue; } if ( (token = CreateIdentifierToken(line)) != null ) { tokens.Add(token); _previousToken = token; continue; } if ( (token = CreateErrorToken(line)) != null) { tokens.Add(token); _previousToken = token; } } } return tokens; }
/// <summary> /// Creates a token if it matches the given symbol /// </summary> /// <param name="line">Current line</param> /// <param name="symbol">Symbol to match</param> /// <returns>Created token or null</returns> private Token CreateToken(string line, string symbol) { // One can make this to work a lot faster, but this is nicer try { if ( line.Substring(_column, symbol.Length) == symbol ) { var token = new Token(_row, _column, symbol); _column += symbol.Length; return token; } } catch (ArgumentOutOfRangeException) { } return null; }