public static SObject Eval(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { if (parameters.Length == 0) { return(processor.Undefined); } var parameterAsString = parameters[0] as SString; var code = parameterAsString != null ? parameterAsString.Value : parameters[0].ToString(processor).Value; var evalProcessor = new ScriptProcessor(processor.Context); return(evalProcessor.Run(code)); }
public static SObject NameOf(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { if (parameters.Length == 0) { return(processor.Undefined); } var parameterAsString = parameters[0] as SProtoObject; if (parameterAsString != null) { return(processor.CreateString(parameterAsString.IsProtoInstance ? parameterAsString.Prototype.Name : parameterAsString.TypeOf())); } return(processor.CreateString(parameters[0].TypeOf())); }
public static SObject ToPrimitive(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { if (parameters.Length == 0) { return(processor.Undefined); } if (parameters[0] is SString) { return(processor.CreateString(((SString)parameters[0]).Value)); } if (parameters[0] is SNumber) { return(processor.CreateNumber(((SNumber)parameters[0]).Value)); } if (parameters[0] is SBool) { return(processor.CreateBool(((SBool)parameters[0]).Value)); } return(parameters[0]); // returns the input object, if no conversion was conducted. }
public static SObject IsNaN(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { if (parameters.Length == 0) { return(processor.Undefined); } double dbl; if (parameters[0] is SNumber) { dbl = ((SNumber)parameters[0]).Value; } else { dbl = parameters[0].ToNumber(processor).Value; } return(processor.CreateBool(double.IsNaN(dbl))); }
public static SObject ToComplex(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { if (parameters.Length == 0) { return(processor.Undefined); } if (parameters[0] is SString) { return(processor.Context.CreateInstance("String", new[] { parameters[0] })); } if (parameters[0] is SNumber) { return(processor.Context.CreateInstance("Number", new[] { parameters[0] })); } if (parameters[0] is SBool) { return(processor.Context.CreateInstance("Boolean", new[] { parameters[0] })); } return(parameters[0]); // returns the input object, if no conversion was conducted. }
public static SObject DoSync(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) { string[] tasks = processor.Context.Parent.AsyncTasks.ToArray(); if (parameters.Length >= 1) { var param = SObject.Unbox(parameters[0]); if (param is SString) { tasks = new[] { (param as SString).Value } } ; else if (param is SArray) { tasks = (param as SArray).ArrayMembers.Select(m => m.ToString(processor).Value).ToArray(); } } Console.WriteLine($"Sync tasks: ({string.Join(",", tasks)})"); SpinWait.SpinUntil(() => tasks.All(t => !processor.Context.Parent.AsyncTasks.Contains(t))); return(processor.Undefined); }
private SObject ExecuteExecutable(ScriptStatement statement) { if (statement.IsCompoundStatement) { var processor = new ScriptProcessor(Context, GetLineNumber()); // Remove { and }: var code = statement.Code.Remove(0, 1); code = code.Remove(code.Length - 1, 1); var returnObject = processor.Run(code); _breakIssued = processor._breakIssued; _continueIssued = processor._continueIssued; _returnIssued = processor._returnIssued; return(returnObject); } else { var exp = ResolveParentheses(statement.Code).Trim(); #region QuickConvert // have quick conversions for small statements here // parameter statements are much faster that way: if (exp == SObject.LiteralBoolTrue) { return(CreateBool(true)); } else if (exp == SObject.LiteralBoolFalse) { return(CreateBool(false)); } else if (exp == SObject.LiteralUndefined || exp == "") { return(Undefined); } else if (exp == SObject.LiteralNull) { return(Null); } else if (exp.StartsWith("\"") && exp.EndsWith("\"") && !exp.Remove(exp.Length - 1, 1).Remove(0, 1).Contains("\"")) { return(CreateString(exp.Remove(exp.Length - 1, 1).Remove(0, 1))); } else if (exp.All(char.IsDigit)) { double num; SNumber.TryParse(exp, out num); return(CreateNumber(num)); } #endregion if (exp.Contains("=>")) { exp = EvaluateLambda(exp); } if (exp.Contains(".")) { exp = EvaluateOperator(exp, "."); } if (exp.Contains("++")) { exp = EvaluateOperator(exp, "++"); } if (exp.Contains("--")) { exp = EvaluateOperator(exp, "--"); } if (exp.Contains("!")) { exp = EvaluateReverseBool(exp); } if (exp.Contains("**")) { exp = EvaluateOperator(exp, "**"); } if (exp.Contains("*")) { exp = EvaluateOperator(exp, "*"); } if (exp.Contains("/")) { exp = EvaluateOperator(exp, "/"); } if (exp.Contains("%")) { exp = EvaluateOperator(exp, "%"); } if (exp.Contains("+")) { exp = EvaluateOperator(exp, "+"); } if (exp.Contains("-")) { exp = EvaluateOperator(exp, "-"); } if (exp.Contains("<=")) { exp = EvaluateOperator(exp, "<="); } if (exp.Contains(">=")) { exp = EvaluateOperator(exp, ">="); } if (exp.Contains("<")) { exp = EvaluateOperator(exp, "<"); } if (exp.Contains(">")) { exp = EvaluateOperator(exp, ">"); } if (exp.Contains("===")) { exp = EvaluateOperator(exp, "==="); } if (exp.Contains("!==")) { exp = EvaluateOperator(exp, "!=="); } if (exp.Contains("==")) { exp = EvaluateOperator(exp, "=="); } if (exp.Contains("!=")) { exp = EvaluateOperator(exp, "!="); } if (exp.Contains("&&")) { exp = EvaluateOperator(exp, "&&"); } if (exp.Contains("||")) { exp = EvaluateOperator(exp, "||"); } return(ToScriptObject(exp)); } }
private SObject ExecuteFor(ScriptStatement statement) { var exp = statement.Code; var forCode = exp.Remove(0, exp.IndexOf("for", StringComparison.Ordinal) + "for".Length).Trim().Remove(0, 1); // Remove "for" and "(". forCode = forCode.Remove(forCode.Length - 1, 1); // Remove ")". var forStatements = StatementProcessor.GetStatements(this, forCode); if (forStatements.Length == 0) { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, ")")); } if (forStatements.Length == 1) { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingForInitializer)); } if (forStatements.Length == 2) { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingForCondition)); } if (forStatements.Length > 3) { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingForControl)); } var processor = new ScriptProcessor(Context, GetLineNumber()); var forInitializer = forStatements[0]; var forCondition = forStatements[1]; var forControl = forStatements[2]; if (forInitializer.Code.Length > 0) { processor.ExecuteStatement(forInitializer); } _index++; if (_statements.Length > _index) { var stayInFor = true; var executeStatement = _statements[_index]; var returnObject = Undefined; while (stayInFor) { if (forCondition.Code.Length > 0) { var conditionResult = processor.ExecuteStatement(forCondition); var conditionAsBool = conditionResult as SBool; stayInFor = conditionAsBool?.Value ?? conditionResult.ToBool(this).Value; } if (stayInFor) { returnObject = processor.ExecuteStatement(executeStatement); if (processor._returnIssued || processor._breakIssued) { _breakIssued = false; _returnIssued = processor._returnIssued; stayInFor = false; } else if (forControl.Code.Length > 0) { processor.ExecuteStatement(forControl); } } } return(returnObject); } else { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "end of script")); } }
private SObject ExecuteTry(ScriptStatement statement) { var exp = statement.Code; if (exp != "try") { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingBeforeTry)); } _index++; if (_statements.Length > _index) { var executeStatement = _statements[_index]; var encounteredError = false; var foundCatch = false; var foundFinally = false; var foundMatchingCatch = false; SObject errorObject = null; var returnObject = Undefined; try { returnObject = ExecuteStatement(executeStatement); } catch (ScriptException ex) { encounteredError = true; errorObject = ex.ErrorObject; } if (encounteredError) { var endedCatchSearch = false; var findCatchIndex = _index + 1; while (findCatchIndex < _statements.Length && !endedCatchSearch) { if (_statements[findCatchIndex].StatementType == StatementType.Catch) { _index = findCatchIndex + 1; if (_statements.Length > _index) { if (!foundMatchingCatch) { var catchExecuteStatement = _statements[_index]; foundCatch = true; var catchCode = _statements[findCatchIndex].Code; var errorVarName = ""; if (catchCode != "catch") { catchCode = catchCode.Remove(0, "catch".Length).Trim().Remove(0, 1); catchCode = catchCode.Remove(catchCode.Length - 1, 1); errorVarName = catchCode.Trim(); } if (Regex.IsMatch(catchCode, RegexCatchcondition)) { errorVarName = catchCode.Remove(catchCode.IndexOf(" ", StringComparison.Ordinal)); var conditionCode = catchCode.Remove(0, catchCode.IndexOf("if", StringComparison.Ordinal) + 3); var processor = new ScriptProcessor(Context, GetLineNumber()); processor.Context.AddVariable(errorVarName, errorObject); var conditionResult = processor.ExecuteStatement(new ScriptStatement(conditionCode)); var conditionAsBool = conditionResult as SBool; foundMatchingCatch = conditionAsBool?.Value ?? conditionResult.ToBool(this).Value; if (foundMatchingCatch) { returnObject = processor.ExecuteStatement(catchExecuteStatement); } } else { foundMatchingCatch = true; var processor = new ScriptProcessor(Context, GetLineNumber()); processor.Context.AddVariable(errorVarName, errorObject); returnObject = processor.ExecuteStatement(catchExecuteStatement); } } } else { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "end of script")); } } else { // end search if different statement type appears: endedCatchSearch = true; } findCatchIndex += 2; } } else { var findCatchIndex = _index + 1; while (findCatchIndex < _statements.Length) { if (_statements[findCatchIndex].StatementType == StatementType.Catch) { foundCatch = true; _index = findCatchIndex + 1; } else { findCatchIndex = _statements.Length; } findCatchIndex += 2; } } // if no matching catch was found when an error occurred, it was not caught: throw it! if (encounteredError && !foundMatchingCatch) { return(ErrorHandler.ThrowError(errorObject)); } // now, try to find finally statement: var findFinallyIndex = _index + 1; while (findFinallyIndex < _statements.Length && !foundFinally) { if (_statements[findFinallyIndex].StatementType == StatementType.Finally) { _index = findFinallyIndex + 1; if (_statements.Length > _index) { var finallyExecuteStatement = _statements[_index]; foundFinally = true; returnObject = ExecuteStatement(finallyExecuteStatement); } else { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "end of script")); } } else { findFinallyIndex = _statements.Length; } findFinallyIndex += 2; } if (!foundCatch && !foundFinally) // when no catch or finally block has been found, throw an error. { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxMissingCatchOrFinally)); } else { return(returnObject); } } else { return(ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "end of script")); } }
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, StringComparison.Ordinal) > -1) { index = code.IndexOf("\n", index + 1, StringComparison.Ordinal) + 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 (ObjectDiscoverToken.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(';'); 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.MessageSyntaxMissingEndOfCompoundStatement); } if (isComment) { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxUnterminatedComment); } if (isControlStatement) { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MessageSyntaxExpectedExpression, "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()); }
public static SObject TypeOf(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) => parameters.Length == 0 ? processor.Undefined : processor.CreateString(parameters[0].TypeOf());
public static SObject SizeOf(ScriptProcessor processor, SObject instance, SObject This, SObject[] parameters) => parameters.Length == 0 ? processor.Undefined : processor.CreateNumber(parameters[0].SizeOf());
public ErrorHandler(ScriptProcessor processor) { _processor = processor; }