/// <summary> /// Parses a list of parameters into a list of script objects. /// </summary> internal SObject[] ParseParameters(string exp) { // When there is only empty space in the parameter expression, we can save the search and just return an empty array: if (string.IsNullOrWhiteSpace(exp)) { return new SObject[] { } } ; var parameters = new List <SObject>(); var index = 0; var depth = 0; string parameter; SObject parameterObject; var parameterStartIndex = 0; var escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth++; } else if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == ',' && depth == 0) { parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex); if (!string.IsNullOrWhiteSpace(parameter)) { parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter))); parameters.Add(parameterObject); } parameterStartIndex = index + 1; } } index++; } parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex); if (!string.IsNullOrWhiteSpace(parameter)) { parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter))); parameters.Add(parameterObject); } return(parameters.ToArray()); }
/// <summary> /// Returns positions of the given operator in the expression, sorted from left to right. /// </summary> private static int[] GetOperatorPositions(string exp, string op) { var operators = new List <int>(); StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0); var depth = 0; var index = 0; while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } if (t == op[0] && depth == 0) { if (op.Length > 1 && index + op.Length - 1 < exp.Length) { var correctOperator = true; for (var i = 1; i < op.Length; i++) { if (exp[index + i] != op[i]) { correctOperator = false; } } if (correctOperator) { operators.Add(index); } } else if (op.Length == 1) { operators.Add(index); } } } index++; } return(operators.ToArray()); }
private SObject ExecuteAssignment(ScriptStatement statement) { var exp = statement.Code; var leftSide = ""; var rightSide = ""; var assignmentOperator = ""; // Get left and right side of the assignment: { var depth = 0; var index = 0; StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length && assignmentOperator.Length == 0) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth++; } else if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == '=' && depth == 0) { var previous = ' '; if (index > 0) { previous = exp[index - 1]; } if (previous == '+' || previous == '-' || previous == '/' || previous == '*') { assignmentOperator = previous.ToString(); leftSide = exp.Substring(0, index - 1).TrimEnd(); } else { assignmentOperator = "="; leftSide = exp.Substring(0, index).TrimEnd(); } rightSide = exp.Substring(index + 1).TrimStart(); } } index++; } } // This means it's a function call, which cannot be assigned to: if (leftSide.EndsWith(")") || leftSide.Length == 0) { return(ErrorHandler.ThrowError(ErrorType.ReferenceError, ErrorHandler.MESSAGE_REFERENCE_INVALID_ASSIGNMENT_LEFT)); } SObject memberHost = null; SObject accessor = null; SObject value = null; var isIndexer = false; var host = ""; var member = ""; if (leftSide.EndsWith("]")) { var indexerStartIndex = 0; var index = leftSide.Length - 1; var depth = 0; StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index); while (index > 0 && !isIndexer) { var t = leftSide[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } else if (t == '[') { depth--; if (depth == 0 && index > 0) { isIndexer = true; indexerStartIndex = index; } } } index--; } if (isIndexer) { member = leftSide.Substring(indexerStartIndex + 1); member = member.Remove(member.Length - 1, 1); host = leftSide.Remove(indexerStartIndex); } } else { var foundMember = false; if (leftSide.Contains(".")) { var index = leftSide.Length - 1; var depth = 0; StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index); while (index > 0 && !foundMember) { var t = leftSide[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } else if (t == '.' && depth == 0) { foundMember = true; host = leftSide.Substring(0, index); member = leftSide.Remove(0, index + 1); } } index--; } } if (!foundMember) { host = SObject.LITERAL_THIS; member = leftSide; } } // When it's an indexer, we parse it as statement: accessor = isIndexer ? SObject.Unbox(ExecuteStatement(new ScriptStatement(member))) : CreateString(member); memberHost = ExecuteStatement(new ScriptStatement(host)); value = SObject.Unbox(ExecuteStatement(new ScriptStatement(rightSide))); if (assignmentOperator == "=") { memberHost.SetMember(this, accessor, isIndexer, value); } else { var memberContent = memberHost.GetMember(this, accessor, isIndexer); var result = ""; switch (assignmentOperator) { case "+": result = ObjectOperators.AddOperator(this, memberContent, value); break; case "-": result = ObjectOperators.SubtractOperator(this, memberContent, value); break; case "*": result = ObjectOperators.MultiplyOperator(this, memberContent, value); break; case "/": result = ObjectOperators.DivideOperator(this, memberContent, value); break; } memberHost.SetMember(this, accessor, isIndexer, ToScriptObject(result)); } return(value); }
/// <summary> /// Captures an element right from the starting index. /// </summary> private static ElementCapture CaptureRight(string exp, int index) { if (string.IsNullOrWhiteSpace(exp)) { return new ElementCapture() { Length = 0, StartIndex = 0, Identifier = "", Depth = 0 } } ; var identifier = ""; var foundSeparatorChar = false; var depth = 0; StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, index); while (index < exp.Length && !foundSeparatorChar) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == '(' || t == '[' || t == '{') { depth++; } } if (t == '-' && string.IsNullOrWhiteSpace(identifier)) { identifier += "-"; } else { if (!escaper.IsString && depth == 0) { if (t == '.') { // Check if the '.' is not a decimal separator: if (!Regex.IsMatch(identifier.Trim(), REGEX_NUMLEFTDOT)) { foundSeparatorChar = true; } } else if (IDENTIFIER_SEPARATORS.Contains(t)) { foundSeparatorChar = true; } } // Append the char to the identifier: if (!foundSeparatorChar) { identifier += t; } } index++; } if (foundSeparatorChar) { return new ElementCapture() { StartIndex = index - 1 - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth } } ; else { return new ElementCapture() { StartIndex = index - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth } }; }
/// <summary> /// Resolves parentheses and adds ".call" to direct function calls on variables. /// </summary> private string ResolveParentheses(string exp) { if (exp.Contains("(") && exp.Contains(")")) { var index = 0; var depth = 0; var parenthesesStartIndex = -1; var newExpression = new StringBuilder(); var escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '{' || t == '[') { depth++; if (parenthesesStartIndex == -1) { newExpression.Append(t); } } else if (t == '}' || t == ']') { depth--; if (parenthesesStartIndex == -1) { newExpression.Append(t); } } else if (t == '(') { if (depth == 0 && parenthesesStartIndex == -1) { var capture = CaptureLeft(newExpression.ToString(), newExpression.Length - 1); var identifier = capture.Identifier; if (capture.Depth == 0 || identifier == "") { if (identifier.Length == 0) { parenthesesStartIndex = index; } else { var testExp = newExpression.ToString().Remove(capture.StartIndex).Trim(); if (testExp.Length > 0 && testExp.Last() == '.' || StatementProcessor.ControlStatements.Contains(identifier) || identifier.StartsWith("new ")) { newExpression.Append(t); } else { newExpression.Append(".call" + t); } depth++; } } else { newExpression.Append(t); depth++; } } else { depth++; if (parenthesesStartIndex == -1) { newExpression.Append(t); } } } else if (t == ')') { if (depth == 0 && parenthesesStartIndex > -1) { var parenthesesCode = exp.Substring(parenthesesStartIndex + 1, index - parenthesesStartIndex - 1); if (parenthesesCode.Length > 0) { if (parenthesesCode.Contains("=>") && Regex.IsMatch(parenthesesCode, REGEX_LAMBDA)) { newExpression.Append(BuildLambdaFunction(parenthesesCode)); } else { var returnObject = ExecuteStatement(new ScriptStatement(parenthesesCode)); newExpression.Append(returnObject.ToScriptObject()); } } else { // check for lambda statement // if this turns out to be a lambda statement, then the whole expression is this lambda statement. // therefore, discard everything and just add the converted function code taken from the lambda statement. var nonParenthesesCode = exp.Substring(index + 1).Trim(); if (nonParenthesesCode.StartsWith("=>")) { return(BuildLambdaFunction(exp)); } } parenthesesStartIndex = -1; } else { depth--; if (parenthesesStartIndex == -1) { newExpression.Append(t); } } } else { if (parenthesesStartIndex == -1) { newExpression.Append(t); } } } else { if (parenthesesStartIndex == -1) { newExpression.Append(t); } } index++; } return(newExpression.ToString()); } return(exp); }
/// <summary> /// Parses a list of parameters into a list of script objects. /// </summary> internal SObject[] ParseParameters(string exp) { // When there is only empty space in the parameter expression, we can save the search and just return an empty array: if (string.IsNullOrWhiteSpace(exp)) return new SObject[] { }; var parameters = new List<SObject>(); var index = 0; var depth = 0; string parameter; SObject parameterObject; var parameterStartIndex = 0; var escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth++; } else if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == ',' && depth == 0) { parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex); if (!string.IsNullOrWhiteSpace(parameter)) { parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter))); parameters.Add(parameterObject); } parameterStartIndex = index + 1; } } index++; } parameter = exp.Substring(parameterStartIndex, index - parameterStartIndex); if (!string.IsNullOrWhiteSpace(parameter)) { parameterObject = SObject.Unbox(ExecuteStatement(new ScriptStatement(parameter))); parameters.Add(parameterObject); } return parameters.ToArray(); }
internal static ScriptStatement[] GetStatements(ScriptProcessor processor, string code) { var statements = new List<ScriptStatement>(); var statement = new StringBuilder(); var index = 0; var depth = 0; var lineNumber = 1; var lineNumberBuffer = 0; // buffers line counts from the start of a statement. var isComment = false; var isControlStatement = false; // If the current statement is a control statement. var isCompoundStatement = false; // If the current statement is bunch of statements wrapped in { ... } StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(code, 0); while (index < code.Length) { var t = code[index]; if (!isComment) escaper.CheckStartAt(index); else escaper.JumpTo(index); if (!escaper.IsString) { // Check if a block comment is starting (/*): if (!isComment && t == '/' && index + 1 < code.Length && code[index + 1] == '*') { isComment = true; index++; // Jump over * char. } if (!isComment) { // Check if a line comment is starting (//): if (t == '/' && index + 1 < code.Length && code[index + 1] == '/') { // We jump to the end of the line and ignore everything between the current index and the end of the line: if (code.IndexOf("\n", index + 1) > -1) index = code.IndexOf("\n", index + 1) + 1; else index = code.Length; continue; } statement.Append(t); if (t == '(') { depth++; } else if (t == ')') { depth--; if (isControlStatement) { var statementStr = statement.ToString(); var s = statementStr.Trim(); if (s.StartsWith("if") || s.StartsWith("else if") || s.StartsWith("function") || s.StartsWith("for") || s.StartsWith("while") || s.StartsWith("catch")) { var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines)); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; isControlStatement = false; } } } else if (t == '{') { depth++; if (depth == 1) { var statementStr = statement.ToString(); var s = statementStr.Trim(); if (isControlStatement) { var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines s = s.Remove(s.Length - 1, 1).Trim(); statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines)); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; statement.Clear(); statement.Append('{'); isCompoundStatement = true; isControlStatement = false; } else { if (s == "{") { isCompoundStatement = true; } } } } else if (t == '}') { depth--; if (depth == 0 && isCompoundStatement) { // This could also be an object declaration... // In the case that the statement started with "{" (example statement: {} + []), this will happen. // To check if this is in fact an object, we look right and see if there is: // - an operator => object ("+*-/&|<>.[(") // - nothing => statement var foundOperator = false; var charFindIndex = index + 1; while (!foundOperator && charFindIndex < code.Length) { var testChar = code[charFindIndex]; if (OBJECT_DISCOVER_TOKEN.Contains(testChar)) { if (testChar == '/' && // next statement is actually a comment, not a / operator (followed by / or *) charFindIndex + 1 < code.Length && (code[charFindIndex + 1] == '/' || code[charFindIndex + 1] == '*')) charFindIndex = code.Length; else foundOperator = true; } else if (!char.IsWhiteSpace(testChar)) // We found something that is not an operator or whitespace, so this is the end of a compound statement. { charFindIndex = code.Length; } charFindIndex++; } if (!foundOperator) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.Trim(); statements.Add(new ScriptStatement(s, StatementType.Executable, lineNumber + extraLines) { IsCompoundStatement = true }); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } isCompoundStatement = false; } } else if (t == ';' && depth == 0) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.Trim().TrimEnd(new char[] { ';' }); statements.Add(new ScriptStatement(s, GetStatementType(s, false), lineNumber + extraLines)); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } else if (!isCompoundStatement && !isControlStatement) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.TrimStart(); var nextChar = 'X'; // Set to something that is not matching with the condition below. if (code.Length > index + 1) nextChar = code[index + 1]; // Check if it's actually a control statement by looking if the next char matches (whitespace, ";" or "(") if ((char.IsWhiteSpace(nextChar) || nextChar == ';' || nextChar == '(') && ControlStatements.Contains(s)) { isControlStatement = true; if (s.StartsWith("else")) { if (index + 3 < code.Length) { var check = code.Substring(index + 1, 3); if (check != " if") { statements.Add(new ScriptStatement("else", StatementType.Else, lineNumber + extraLines)); statement.Clear(); isControlStatement = false; lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } } } } } } else { // Check if a block comment is ending (/*): if (t == '*' && index + 1 < code.Length && code[index + 1] == '/') { isComment = false; index++; // Jump over / char. } } } else { statement.Append(t); } if (t == '\n') lineNumberBuffer++; index++; } if (isCompoundStatement) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_MISSING_END_OF_COMPOUND_STATEMENT); if (isComment) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_UNTERMINATED_COMMENT); if (isControlStatement) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_EXPECTED_EXPRESSION, "end of script"); // an executable statement not closed with ";" is getting added here: var leftOver = statement.ToString().Trim(); if (leftOver.Length > 0) { statements.Add(new ScriptStatement(leftOver, GetStatementType(leftOver, false), lineNumber)); } return statements.ToArray(); }
/// <summary> /// Captures an element right from the starting index. /// </summary> private static ElementCapture CaptureRight(string exp, int index) { if (string.IsNullOrWhiteSpace(exp)) return new ElementCapture() { Length = 0, StartIndex = 0, Identifier = "", Depth = 0 }; var identifier = ""; var foundSeparatorChar = false; var depth = 0; StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, index); while (index < exp.Length && !foundSeparatorChar) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == '(' || t == '[' || t == '{') { depth++; } } if (t == '-' && string.IsNullOrWhiteSpace(identifier)) { identifier += "-"; } else { if (!escaper.IsString && depth == 0) { if (t == '.') { // Check if the '.' is not a decimal separator: if (!Regex.IsMatch(identifier.Trim(), REGEX_NUMLEFTDOT)) { foundSeparatorChar = true; } } else if (IDENTIFIER_SEPARATORS.Contains(t)) { foundSeparatorChar = true; } } // Append the char to the identifier: if (!foundSeparatorChar) { identifier += t; } } index++; } if (foundSeparatorChar) return new ElementCapture() { StartIndex = index - 1 - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth }; else return new ElementCapture() { StartIndex = index - identifier.Length, Length = identifier.Length, Identifier = identifier.Trim(), Depth = depth }; }
/// <summary> /// Resolves parentheses and adds ".call" to direct function calls on variables. /// </summary> private string ResolveParentheses(string exp) { if (exp.Contains("(") && exp.Contains(")")) { var index = 0; var depth = 0; var parenthesesStartIndex = -1; var newExpression = new StringBuilder(); var escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '{' || t == '[') { depth++; if (parenthesesStartIndex == -1) newExpression.Append(t); } else if (t == '}' || t == ']') { depth--; if (parenthesesStartIndex == -1) newExpression.Append(t); } else if (t == '(') { if (depth == 0 && parenthesesStartIndex == -1) { var capture = CaptureLeft(newExpression.ToString(), newExpression.Length - 1); var identifier = capture.Identifier; if (capture.Depth == 0 || identifier == "") { if (identifier.Length == 0) { parenthesesStartIndex = index; } else { var testExp = newExpression.ToString().Remove(capture.StartIndex).Trim(); if (testExp.Length > 0 && testExp.Last() == '.' || StatementProcessor.ControlStatements.Contains(identifier) || identifier.StartsWith("new ")) { newExpression.Append(t); } else { newExpression.Append(".call" + t); } depth++; } } else { newExpression.Append(t); depth++; } } else { depth++; if (parenthesesStartIndex == -1) newExpression.Append(t); } } else if (t == ')') { if (depth == 0 && parenthesesStartIndex > -1) { var parenthesesCode = exp.Substring(parenthesesStartIndex + 1, index - parenthesesStartIndex - 1); if (parenthesesCode.Length > 0) { if (parenthesesCode.Contains("=>") && Regex.IsMatch(parenthesesCode, REGEX_LAMBDA)) { newExpression.Append(BuildLambdaFunction(parenthesesCode)); } else { var returnObject = ExecuteStatement(new ScriptStatement(parenthesesCode)); newExpression.Append(returnObject.ToScriptObject()); } } else { // check for lambda statement // if this turns out to be a lambda statement, then the whole expression is this lambda statement. // therefore, discard everything and just add the converted function code taken from the lambda statement. var nonParenthesesCode = exp.Substring(index + 1).Trim(); if (nonParenthesesCode.StartsWith("=>")) { return BuildLambdaFunction(exp); } } parenthesesStartIndex = -1; } else { depth--; if (parenthesesStartIndex == -1) newExpression.Append(t); } } else { if (parenthesesStartIndex == -1) newExpression.Append(t); } } else { if (parenthesesStartIndex == -1) newExpression.Append(t); } index++; } return newExpression.ToString(); } return exp; }
/// <summary> /// Returns positions of the given operator in the expression, sorted from left to right. /// </summary> private static int[] GetOperatorPositions(string exp, string op) { var operators = new List<int>(); StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0); var depth = 0; var index = 0; while (index < exp.Length) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } if (t == op[0] && depth == 0) { if (op.Length > 1 && index + op.Length - 1 < exp.Length) { var correctOperator = true; for (var i = 1; i < op.Length; i++) { if (exp[index + i] != op[i]) correctOperator = false; } if (correctOperator) operators.Add(index); } else if (op.Length == 1) { operators.Add(index); } } } index++; } return operators.ToArray(); }
internal static ScriptStatement[] GetStatements(ScriptProcessor processor, string code) { var statements = new List <ScriptStatement>(); var statement = new StringBuilder(); var index = 0; var depth = 0; var lineNumber = 1; var lineNumberBuffer = 0; // buffers line counts from the start of a statement. var isComment = false; var isControlStatement = false; // If the current statement is a control statement. var isCompoundStatement = false; // If the current statement is bunch of statements wrapped in { ... } StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(code, 0); while (index < code.Length) { var t = code[index]; if (!isComment) { escaper.CheckStartAt(index); } else { escaper.JumpTo(index); } if (!escaper.IsString) { // Check if a block comment is starting (/*): if (!isComment && t == '/' && index + 1 < code.Length && code[index + 1] == '*') { isComment = true; index++; // Jump over * char. } if (!isComment) { // Check if a line comment is starting (//): if (t == '/' && index + 1 < code.Length && code[index + 1] == '/') { // We jump to the end of the line and ignore everything between the current index and the end of the line: if (code.IndexOf("\n", index + 1) > -1) { index = code.IndexOf("\n", index + 1) + 1; } else { index = code.Length; } continue; } statement.Append(t); if (t == '(') { depth++; } else if (t == ')') { depth--; if (isControlStatement) { var statementStr = statement.ToString(); var s = statementStr.Trim(); if (s.StartsWith("if") || s.StartsWith("else if") || s.StartsWith("function") || s.StartsWith("for") || s.StartsWith("while") || s.StartsWith("catch")) { var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines)); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; isControlStatement = false; } } } else if (t == '{') { depth++; if (depth == 1) { var statementStr = statement.ToString(); var s = statementStr.Trim(); if (isControlStatement) { var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines s = s.Remove(s.Length - 1, 1).Trim(); statements.Add(new ScriptStatement(s, GetStatementType(s, true), lineNumber + extraLines)); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; statement.Clear(); statement.Append('{'); isCompoundStatement = true; isControlStatement = false; } else { if (s == "{") { isCompoundStatement = true; } } } } else if (t == '}') { depth--; if (depth == 0 && isCompoundStatement) { // This could also be an object declaration... // In the case that the statement started with "{" (example statement: {} + []), this will happen. // To check if this is in fact an object, we look right and see if there is: // - an operator => object ("+*-/&|<>.[(") // - nothing => statement var foundOperator = false; var charFindIndex = index + 1; while (!foundOperator && charFindIndex < code.Length) { var testChar = code[charFindIndex]; if (OBJECT_DISCOVER_TOKEN.Contains(testChar)) { if (testChar == '/' && // next statement is actually a comment, not a / operator (followed by / or *) charFindIndex + 1 < code.Length && (code[charFindIndex + 1] == '/' || code[charFindIndex + 1] == '*')) { charFindIndex = code.Length; } else { foundOperator = true; } } else if (!char.IsWhiteSpace(testChar)) // We found something that is not an operator or whitespace, so this is the end of a compound statement. { charFindIndex = code.Length; } charFindIndex++; } if (!foundOperator) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.Trim(); statements.Add(new ScriptStatement(s, StatementType.Executable, lineNumber + extraLines) { IsCompoundStatement = true }); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } isCompoundStatement = false; } } else if (t == ';' && depth == 0) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.Trim().TrimEnd(new char[] { ';' }); statements.Add(new ScriptStatement(s, GetStatementType(s, false), lineNumber + extraLines)); statement.Clear(); lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } else if (!isCompoundStatement && !isControlStatement) { var statementStr = statement.ToString(); var extraLines = statementStr.Replace("\r", "").TakeWhile(c => c == '\n').Count(); // count the starting lines var s = statementStr.TrimStart(); var nextChar = 'X'; // Set to something that is not matching with the condition below. if (code.Length > index + 1) { nextChar = code[index + 1]; } // Check if it's actually a control statement by looking if the next char matches (whitespace, ";" or "(") if ((char.IsWhiteSpace(nextChar) || nextChar == ';' || nextChar == '(') && ControlStatements.Contains(s)) { isControlStatement = true; if (s.StartsWith("else")) { if (index + 3 < code.Length) { var check = code.Substring(index + 1, 3); if (check != " if") { statements.Add(new ScriptStatement("else", StatementType.Else, lineNumber + extraLines)); statement.Clear(); isControlStatement = false; lineNumber += lineNumberBuffer; lineNumberBuffer = 0; } } } } } } else { // Check if a block comment is ending (/*): if (t == '*' && index + 1 < code.Length && code[index + 1] == '/') { isComment = false; index++; // Jump over / char. } } } else { statement.Append(t); } if (t == '\n') { lineNumberBuffer++; } index++; } if (isCompoundStatement) { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_MISSING_END_OF_COMPOUND_STATEMENT); } if (isComment) { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_UNTERMINATED_COMMENT); } if (isControlStatement) { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_EXPECTED_EXPRESSION, "end of script"); } // an executable statement not closed with ";" is getting added here: var leftOver = statement.ToString().Trim(); if (leftOver.Length > 0) { statements.Add(new ScriptStatement(leftOver, GetStatementType(leftOver, false), lineNumber)); } return(statements.ToArray()); }
private SObject ExecuteAssignment(ScriptStatement statement) { var exp = statement.Code; var leftSide = ""; var rightSide = ""; var assignmentOperator = ""; // Get left and right side of the assignment: { var depth = 0; var index = 0; StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(exp, 0); while (index < exp.Length && assignmentOperator.Length == 0) { var t = exp[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth++; } else if (t == ')' || t == ']' || t == '}') { depth--; } else if (t == '=' && depth == 0) { var previous = ' '; if (index > 0) previous = exp[index - 1]; if (previous == '+' || previous == '-' || previous == '/' || previous == '*') { assignmentOperator = previous.ToString(); leftSide = exp.Substring(0, index - 1).TrimEnd(); } else { assignmentOperator = "="; leftSide = exp.Substring(0, index).TrimEnd(); } rightSide = exp.Substring(index + 1).TrimStart(); } } index++; } } // This means it's a function call, which cannot be assigned to: if (leftSide.EndsWith(")") || leftSide.Length == 0) return ErrorHandler.ThrowError(ErrorType.ReferenceError, ErrorHandler.MESSAGE_REFERENCE_INVALID_ASSIGNMENT_LEFT); SObject memberHost = null; SObject accessor = null; SObject value = null; var isIndexer = false; var host = ""; var member = ""; if (leftSide.EndsWith("]")) { var indexerStartIndex = 0; var index = leftSide.Length - 1; var depth = 0; StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index); while (index > 0 && !isIndexer) { var t = leftSide[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } else if (t == '[') { depth--; if (depth == 0 && index > 0) { isIndexer = true; indexerStartIndex = index; } } } index--; } if (isIndexer) { member = leftSide.Substring(indexerStartIndex + 1); member = member.Remove(member.Length - 1, 1); host = leftSide.Remove(indexerStartIndex); } } else { var foundMember = false; if (leftSide.Contains(".")) { var index = leftSide.Length - 1; var depth = 0; StringEscapeHelper escaper = new RightToLeftStringEscapeHelper(leftSide, index); while (index > 0 && !foundMember) { var t = leftSide[index]; escaper.CheckStartAt(index); if (!escaper.IsString) { if (t == '(' || t == '[' || t == '{') { depth--; } else if (t == ')' || t == ']' || t == '}') { depth++; } else if (t == '.' && depth == 0) { foundMember = true; host = leftSide.Substring(0, index); member = leftSide.Remove(0, index + 1); } } index--; } } if (!foundMember) { host = SObject.LITERAL_THIS; member = leftSide; } } // When it's an indexer, we parse it as statement: accessor = isIndexer ? SObject.Unbox(ExecuteStatement(new ScriptStatement(member))) : CreateString(member); memberHost = ExecuteStatement(new ScriptStatement(host)); value = SObject.Unbox(ExecuteStatement(new ScriptStatement(rightSide))); if (assignmentOperator == "=") { memberHost.SetMember(this, accessor, isIndexer, value); } else { var memberContent = memberHost.GetMember(this, accessor, isIndexer); var result = ""; switch (assignmentOperator) { case "+": result = ObjectOperators.AddOperator(this, memberContent, value); break; case "-": result = ObjectOperators.SubtractOperator(this, memberContent, value); break; case "*": result = ObjectOperators.MultiplyOperator(this, memberContent, value); break; case "/": result = ObjectOperators.DivideOperator(this, memberContent, value); break; } memberHost.SetMember(this, accessor, isIndexer, ToScriptObject(result)); } return value; }