Example #1
0
        private static ExpressionBase ParseArray(PositionalTokenizer tokenizer)
        {
            SkipWhitespace(tokenizer);

            var array = new ArrayExpression();

            while (tokenizer.NextChar != ']')
            {
                var value = Parse(tokenizer);
                if (value.Type == ExpressionType.ParseError)
                {
                    return(value);
                }

                array.Entries.Add(value);

                SkipWhitespace(tokenizer);
                if (tokenizer.NextChar == ']')
                {
                    break;
                }

                if (tokenizer.NextChar != ',')
                {
                    return(ParseError(tokenizer, "Expecting comma between entries"));
                }
                tokenizer.Advance();
                SkipWhitespace(tokenizer);
            }

            tokenizer.Advance();
            return(array);
        }
Example #2
0
        internal static ExpressionBase ParseStatementBlock(PositionalTokenizer tokenizer, ICollection <ExpressionBase> expressions)
        {
            ExpressionBase.SkipWhitespace(tokenizer);

            if (tokenizer.NextChar != '{')
            {
                var statement = ExpressionBase.Parse(tokenizer);
                if (statement.Type == ExpressionType.ParseError)
                {
                    return(statement);
                }

                expressions.Add(statement);
            }
            else
            {
                var line   = tokenizer.Line;
                var column = tokenizer.Column;

                tokenizer.Advance();
                do
                {
                    ExpressionBase.SkipWhitespace(tokenizer);
                    if (tokenizer.NextChar == '}')
                    {
                        break;
                    }

                    if (tokenizer.NextChar == '\0')
                    {
                        return(ParseError(tokenizer, "No matching closing brace found", line, column));
                    }

                    var statement = ExpressionBase.Parse(tokenizer);
                    if (statement.Type == ExpressionType.ParseError)
                    {
                        return(statement);
                    }

                    if (statement.Type == ExpressionType.Variable)
                    {
                        return(new ParseErrorExpression("standalone variable has no meaning", statement));
                    }

                    expressions.Add(statement);
                } while (true);

                tokenizer.Advance();
            }

            return(null);
        }
Example #3
0
        internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0)
        {
            SkipWhitespace(tokenizer);

            var dict = new DictionaryExpression();

            while (tokenizer.NextChar != '}')
            {
                var key = ExpressionBase.ParseClause(tokenizer);
                if (key.Type == ExpressionType.ParseError)
                {
                    return(key);
                }

                SkipWhitespace(tokenizer);
                if (tokenizer.NextChar != ':')
                {
                    ParseError(tokenizer, "Expecting colon following key expression");
                    break;
                }
                tokenizer.Advance();
                SkipWhitespace(tokenizer);

                var value = ExpressionBase.ParseClause(tokenizer);
                if (value.Type == ExpressionType.ParseError)
                {
                    break;
                }

                dict.Entries.Add(new DictionaryExpression.DictionaryEntry {
                    Key = key, Value = value
                });

                SkipWhitespace(tokenizer);
                if (tokenizer.NextChar == '}')
                {
                    break;
                }

                if (tokenizer.NextChar != ',')
                {
                    ParseError(tokenizer, "Expecting comma between entries");
                    break;
                }
                tokenizer.Advance();
                SkipWhitespace(tokenizer);
            }

            tokenizer.Advance();
            return(dict);
        }
Example #4
0
        /// <summary>
        /// Determines if the tokenizer is pointing at a parameter list for an anonymous function.
        /// </summary>
        internal static bool IsAnonymousParameterList(PositionalTokenizer tokenizer)
        {
            var result = false;

            tokenizer.PushState();

            if (tokenizer.Match("("))
            {
                tokenizer.SkipWhitespace();

                do
                {
                    tokenizer.ReadIdentifier();
                    tokenizer.SkipWhitespace();

                    if (tokenizer.NextChar != ',')
                    {
                        break;
                    }

                    tokenizer.Advance();
                    tokenizer.SkipWhitespace();
                } while (true);

                if (tokenizer.Match(")"))
                {
                    tokenizer.SkipWhitespace();
                    result = (tokenizer.NextChar == '{' || tokenizer.Match("=>"));
                }
            }

            tokenizer.PopState();

            return(result);
        }
Example #5
0
        private static ExpressionBase ParseParameters(PositionalTokenizer tokenizer, ICollection <ExpressionBase> parameters)
        {
            int line   = tokenizer.Line;
            int column = tokenizer.Column;

            SkipWhitespace(tokenizer);

            if (tokenizer.NextChar != ')')
            {
                do
                {
                    var parameter = ExpressionBase.Parse(tokenizer);
                    if (parameter.Type == ExpressionType.ParseError)
                    {
                        return(ParseError(tokenizer, "Invalid expression", parameter));
                    }

                    parameters.Add(parameter);

                    SkipWhitespace(tokenizer);

                    var commaLine   = tokenizer.Line;
                    var commaColumn = tokenizer.Column;
                    if (tokenizer.NextChar != ',')
                    {
                        break;
                    }

                    tokenizer.Advance();
                    SkipWhitespace(tokenizer);
                } while (true);
            }

            if (tokenizer.NextChar == ')')
            {
                tokenizer.Advance();
                return(null);
            }

            if (tokenizer.NextChar == '\0')
            {
                return(ParseError(tokenizer, "No closing parenthesis found", line, column));
            }

            return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar));
        }
Example #6
0
        private ParseErrorExpression ProcessRichPresenceDisplay(StringConstantExpression stringExpression, InterpreterScope scope, out string displayString)
        {
            displayString = null;

            ExpressionBase result;
            var            varargs = GetParameter(scope, "varargs", out result) as ArrayExpression;

            if (varargs == null)
            {
                var error = result as ParseErrorExpression;
                if (error == null)
                {
                    error = new ParseErrorExpression("unexpected varargs", stringExpression);
                }
                return(error);
            }

            var context = scope.GetContext <RichPresenceDisplayContext>();
            var builder = context.DisplayString;

            var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(stringExpression.Value));

            while (tokenizer.NextChar != '\0')
            {
                var token = tokenizer.ReadTo('{');
                builder.Append(token.ToString());

                if (tokenizer.NextChar == '\0')
                {
                    break;
                }

                var positionalTokenColumn = tokenizer.Column;
                tokenizer.Advance();
                var index = tokenizer.ReadNumber();
                if (tokenizer.NextChar != '}')
                {
                    return(new ParseErrorExpression("Invalid positional token",
                                                    stringExpression.Line, stringExpression.Column + positionalTokenColumn,
                                                    stringExpression.Line, stringExpression.Column + tokenizer.Column - 1));
                }
                tokenizer.Advance();

                var parameterIndex = Int32.Parse(index.ToString());
                if (parameterIndex >= varargs.Entries.Count)
                {
                    return(new ParseErrorExpression("Invalid parameter index: " + parameterIndex,
                                                    stringExpression.Line, stringExpression.Column + positionalTokenColumn,
                                                    stringExpression.Line, stringExpression.Column + tokenizer.Column - 1));
                }

                var functionCall = varargs.Entries[parameterIndex] as FunctionCallExpression;

                var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name);
                if (functionDefinition == null)
                {
                    return(new ParseErrorExpression("Unknown function: " + functionCall.FunctionName.Name));
                }

                var richPresenceFunction = functionDefinition as FunctionDefinition;
                if (richPresenceFunction == null)
                {
                    return(new ParseErrorExpression(functionCall.FunctionName.Name + " cannot be called as a rich_presence_display parameter", functionCall));
                }

                var error = richPresenceFunction.BuildMacro(context, scope, functionCall);
                if (error != null)
                {
                    return(new ParseErrorExpression(error, functionCall));
                }
            }

            displayString = builder.ToString();
            return(null);
        }
Example #7
0
        public string GetFormattedErrorMessage(Tokenizer tokenizer)
        {
            var neededLines = new List <int>();
            var error       = Error;

            while (error != null)
            {
                for (int i = error.Location.Start.Line; i <= error.Location.End.Line; i++)
                {
                    if (!neededLines.Contains(i))
                    {
                        neededLines.Add(i);
                    }
                }

                error = error.InnerError;
            }

            neededLines.Sort();

            var lineDictionary      = new TinyDictionary <int, string>();
            var positionalTokenizer = new PositionalTokenizer(tokenizer);
            int lineIndex           = 0;

            while (lineIndex < neededLines.Count)
            {
                while (positionalTokenizer.NextChar != '\0' && positionalTokenizer.Line != neededLines[lineIndex])
                {
                    positionalTokenizer.ReadTo('\n');
                    positionalTokenizer.Advance();
                }

                lineDictionary[neededLines[lineIndex]] = positionalTokenizer.ReadTo('\n').TrimRight().ToString();
                lineIndex++;
            }

            var builder = new StringBuilder();

            error = Error;
            while (error != null)
            {
                builder.AppendFormat("{0}:{1} {2}", error.Location.Start.Line, error.Location.Start.Column, error.Message);
                builder.AppendLine();
                //for (int i = error.Line; i <= error.EndLine; i++)
                int i = error.Location.Start.Line; // TODO: show all lines associated to error?
                {
                    var line = lineDictionary[error.Location.Start.Line];

                    builder.Append(":: ");
                    var startColumn = 0;
                    while (Char.IsWhiteSpace(line[startColumn]))
                    {
                        startColumn++;
                    }

                    if (i == error.Location.Start.Line)
                    {
                        builder.Append("{{color|#C0C0C0|");
                        builder.Append(line.Substring(startColumn, error.Location.Start.Column - startColumn - 1));
                        builder.Append("}}");
                        startColumn = error.Location.Start.Column - 1;
                    }

                    if (i == error.Location.End.Line)
                    {
                        builder.Append(line.Substring(startColumn, error.Location.End.Column - startColumn));
                        builder.Append("{{color|#C0C0C0|");
                        builder.Append(line.Substring(error.Location.End.Column));
                        builder.Append("}}");
                    }
                    else
                    {
                        builder.Append(line.Substring(startColumn));
                    }
                    builder.AppendLine();
                }
                builder.AppendLine();
                error = error.InnerError;
            }

            while (builder.Length > 0 && Char.IsWhiteSpace(builder[builder.Length - 1]))
            {
                builder.Length--;
            }

            return(builder.ToString());
        }
Example #8
0
        private static ExpressionBase ParseClauseCore(PositionalTokenizer tokenizer)
        {
            ExpressionBase clause;

            switch (tokenizer.NextChar)
            {
            case '!':
                tokenizer.Advance();
                clause = ParseClause(tokenizer);
                if (clause.Type == ExpressionType.ParseError)
                {
                    return(clause);
                }

                return(new ConditionalExpression(null, ConditionalOperation.Not, clause));

            case '(':
                if (AnonymousUserFunctionDefinitionExpression.IsAnonymousParameterList(tokenizer))
                {
                    return(AnonymousUserFunctionDefinitionExpression.ParseAnonymous(tokenizer));
                }

                tokenizer.Advance();
                clause = ExpressionBase.Parse(tokenizer);
                if (clause.Type == ExpressionType.ParseError)
                {
                    return(clause);
                }

                if (tokenizer.NextChar != ')')
                {
                    if (tokenizer.NextChar == '\0')
                    {
                        return(ParseError(tokenizer, "No closing parenthesis found"));
                    }

                    return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar));
                }

                clause.IsLogicalUnit = true;
                tokenizer.Advance();

                return(clause);

            case '"':
                try
                {
                    var stringValue = tokenizer.ReadQuotedString().ToString();
                    return(new StringConstantExpression(stringValue));
                }
                catch (InvalidOperationException ex)
                {
                    return(ParseError(tokenizer, ex.Message));
                }

            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return(ParseNumber(tokenizer, true));

            case '-':
                var tokenStart = tokenizer.Location;
                tokenizer.Advance();
                if (tokenizer.NextChar >= '0' && tokenizer.NextChar <= '9')
                {
                    var result   = ParseNumber(tokenizer, false);
                    var tokenEnd = result.Location.End;
                    switch (result.Type)
                    {
                    case ExpressionType.IntegerConstant:
                        result = new IntegerConstantExpression(-((IntegerConstantExpression)result).Value);
                        break;

                    case ExpressionType.FloatConstant:
                        result = new FloatConstantExpression(-((FloatConstantExpression)result).Value);
                        break;

                    default:
                        return(result);
                    }

                    result.Location = new TextRange(tokenStart, tokenEnd);
                    return(result);
                }
                return(ParseError(tokenizer, "Minus without value", tokenStart.Line, tokenStart.Column));

            case '{':
                tokenizer.Advance();
                return(DictionaryExpression.Parse(tokenizer));

            case '[':
                tokenizer.Advance();
                return(ParseArray(tokenizer));

            default:
                var line       = tokenizer.Line;
                var column     = tokenizer.Column;
                var identifier = tokenizer.ReadIdentifier();
                if (identifier.IsEmpty)
                {
                    var error = ParseError(tokenizer, "Unexpected character: " + tokenizer.NextChar);
                    tokenizer.Advance();
                    return(error);
                }

                SkipWhitespace(tokenizer);

                if (identifier == "return")
                {
                    clause = ExpressionBase.Parse(tokenizer);
                    if (clause.Type == ExpressionType.ParseError)
                    {
                        return(clause);
                    }

                    return(new ReturnExpression(new KeywordExpression(identifier.ToString(), line, column), clause));
                }

                if (identifier == "function")
                {
                    return(UserFunctionDefinitionExpression.Parse(tokenizer, line, column));
                }
                if (identifier == "for")
                {
                    return(ForExpression.Parse(tokenizer, line, column));
                }
                if (identifier == "if")
                {
                    return(IfExpression.Parse(tokenizer, line, column));
                }

                if (identifier == "true")
                {
                    return(new BooleanConstantExpression(true, line, column));
                }
                if (identifier == "false")
                {
                    return(new BooleanConstantExpression(false, line, column));
                }

                if (tokenizer.NextChar == '(')
                {
                    tokenizer.Advance();

                    var parameters = new List <ExpressionBase>();
                    ParseParameters(tokenizer, parameters);

                    var functionCall = new FunctionCallExpression(new FunctionNameExpression(identifier.ToString(), line, column), parameters);
                    functionCall.Location = new TextRange(line, column, tokenizer.Line, tokenizer.Column - 1);
                    return(functionCall);
                }

                if (tokenizer.NextChar == '[')
                {
                    IndexedVariableExpression parent = null;

                    do
                    {
                        tokenizer.Advance();

                        var index = ExpressionBase.Parse(tokenizer);
                        if (index.Type == ExpressionType.ParseError)
                        {
                            return(index);
                        }

                        SkipWhitespace(tokenizer);
                        if (tokenizer.NextChar != ']')
                        {
                            return(ParseError(tokenizer, "Expecting closing bracket after index"));
                        }
                        tokenizer.Advance();
                        SkipWhitespace(tokenizer);

                        if (parent != null)
                        {
                            parent = new IndexedVariableExpression(parent, index);
                        }
                        else
                        {
                            parent = new IndexedVariableExpression(new VariableExpression(identifier.ToString(), line, column), index);
                        }
                    } while (tokenizer.NextChar == '[');

                    return(parent);
                }

                return(new VariableExpression(identifier.ToString(), line, column));
            }
        }
Example #9
0
        private static ExpressionBase ParseNumber(PositionalTokenizer tokenizer, bool isUnsigned)
        {
            int    line      = tokenizer.Line;
            int    column    = tokenizer.Column;
            int    endLine   = line;
            int    endColumn = column;
            uint   value;
            string number = "";

            if (tokenizer.Match("0x"))
            {
                while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f'))
                {
                    number += tokenizer.NextChar;

                    endLine   = tokenizer.Line;
                    endColumn = tokenizer.Column;
                    tokenizer.Advance();
                }

                if (!UInt32.TryParse(number, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out value))
                {
                    return(new ParseErrorExpression("Number too large"));
                }
            }
            else
            {
                while (Char.IsDigit(tokenizer.NextChar))
                {
                    number += tokenizer.NextChar;

                    endLine   = tokenizer.Line;
                    endColumn = tokenizer.Column;
                    tokenizer.Advance();
                }

                if (tokenizer.NextChar == '.')
                {
                    number += tokenizer.NextChar;
                    tokenizer.Advance();

                    while (Char.IsDigit(tokenizer.NextChar))
                    {
                        number += tokenizer.NextChar;

                        endLine   = tokenizer.Line;
                        endColumn = tokenizer.Column;
                        tokenizer.Advance();
                    }

                    float floatValue;
                    if (!float.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture, out floatValue))
                    {
                        return(new ParseErrorExpression("Number too large"));
                    }

                    var floatExpression = new FloatConstantExpression(floatValue);
                    floatExpression.Location = new TextRange(line, column, endLine, endColumn);
                    return(floatExpression);
                }

                if (!UInt32.TryParse(number, out value))
                {
                    return(new ParseErrorExpression("Number too large"));
                }
            }

            if (value > Int32.MaxValue && !isUnsigned)
            {
                return(new ParseErrorExpression("Number too large"));
            }

            var integerExpression = new IntegerConstantExpression((int)value);

            integerExpression.Location = new TextRange(line, column, endLine, endColumn);
            return(integerExpression);
        }
Example #10
0
        private static ExpressionBase ParseClauseExtension(ExpressionBase clause, PositionalTokenizer tokenizer, OperationPriority priority)
        {
            do
            {
                var clauseEndLine   = tokenizer.Line;
                var clauseEndColumn = tokenizer.Column;

                SkipWhitespace(tokenizer);

                var joinerLine   = tokenizer.Line;
                var joinerColumn = tokenizer.Column;

                switch (tokenizer.NextChar)
                {
                case '+':
                    if (priority == OperationPriority.AddSubtract && clause.Type == ExpressionType.StringConstant)
                    {
                        priority = OperationPriority.AppendString;
                    }

                    if (priority >= OperationPriority.AddSubtract)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    clause = ParseMathematic(tokenizer, clause, MathematicOperation.Add, joinerLine, joinerColumn);
                    break;

                case '-':
                    if (priority >= OperationPriority.AddSubtract)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    clause = ParseMathematic(tokenizer, clause, MathematicOperation.Subtract, joinerLine, joinerColumn);
                    break;

                case '*':
                    if (priority >= OperationPriority.MulDivMod)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    clause = ParseMathematic(tokenizer, clause, MathematicOperation.Multiply, joinerLine, joinerColumn);
                    break;

                case '/':
                    if (priority >= OperationPriority.MulDivMod)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    clause = ParseMathematic(tokenizer, clause, MathematicOperation.Divide, joinerLine, joinerColumn);
                    break;

                case '%':
                    if (priority >= OperationPriority.MulDivMod)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    clause = ParseMathematic(tokenizer, clause, MathematicOperation.Modulus, joinerLine, joinerColumn);
                    break;

                case '=':
                    if (tokenizer.MatchSubstring("==") == 2)
                    {
                        if (priority >= OperationPriority.Compare)
                        {
                            return(clause);
                        }

                        tokenizer.Advance(2);
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.Equal, joinerLine, joinerColumn);
                    }
                    else
                    {
                        if (priority > OperationPriority.Assign)
                        {
                            return(clause);
                        }

                        tokenizer.Advance();
                        if (tokenizer.NextChar == '>')
                        {
                            tokenizer.Advance();
                            clause = AnonymousUserFunctionDefinitionExpression.ParseAnonymous(tokenizer, clause);
                        }
                        else
                        {
                            clause = ParseAssignment(tokenizer, clause, joinerLine, joinerColumn);
                        }
                    }
                    break;

                case '!':
                    if (priority >= OperationPriority.Compare)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    if (tokenizer.NextChar != '=')
                    {
                        ParseError(tokenizer, "= expected following !", joinerLine, joinerColumn);
                    }
                    else
                    {
                        tokenizer.Advance();
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.NotEqual, joinerLine, joinerColumn);
                    }
                    break;

                case '<':
                    if (priority >= OperationPriority.Compare)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    if (tokenizer.NextChar == '=')
                    {
                        tokenizer.Advance();
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.LessThanOrEqual, joinerLine, joinerColumn);
                    }
                    else
                    {
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.LessThan, joinerLine, joinerColumn);
                    }
                    break;

                case '>':
                    if (priority >= OperationPriority.Compare)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    if (tokenizer.NextChar == '=')
                    {
                        tokenizer.Advance();
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.GreaterThanOrEqual, joinerLine, joinerColumn);
                    }
                    else
                    {
                        clause = ParseComparison(tokenizer, clause, ComparisonOperation.GreaterThan, joinerLine, joinerColumn);
                    }
                    break;

                case '&':
                    if (tokenizer.MatchSubstring("&&") == 2)
                    {
                        if (priority >= OperationPriority.And)
                        {
                            return(clause);
                        }

                        tokenizer.Advance(2);
                        clause = ParseConditional(tokenizer, clause, ConditionalOperation.And, joinerLine, joinerColumn);
                    }
                    else
                    {
                        if (priority >= OperationPriority.BitwiseAnd)
                        {
                            return(clause);
                        }

                        tokenizer.Advance();
                        clause = ParseMathematic(tokenizer, clause, MathematicOperation.BitwiseAnd, joinerLine, joinerColumn);
                    }
                    break;

                case '|':
                    if (priority >= OperationPriority.Or)
                    {
                        return(clause);
                    }

                    tokenizer.Advance();
                    if (tokenizer.NextChar != '|')
                    {
                        ParseError(tokenizer, "| expected following |", joinerLine, joinerColumn);
                    }
                    else
                    {
                        tokenizer.Advance();
                        clause = ParseConditional(tokenizer, clause, ConditionalOperation.Or, joinerLine, joinerColumn);
                    }
                    break;

                default:
                    if (clause.Location.End.Column == 0)
                    {
                        clause.Location = new TextRange(clause.Location.Start, new TextLocation(clauseEndLine, clauseEndColumn));
                    }

                    return(clause);
                }

                if (clause.Type == ExpressionType.ParseError)
                {
                    return(clause);
                }
            } while (true);
        }
Example #11
0
        protected new ExpressionBase Parse(PositionalTokenizer tokenizer)
        {
            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != '(')
            {
                return(ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", Name));
            }
            tokenizer.Advance();

            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != ')')
            {
                do
                {
                    var line   = tokenizer.Line;
                    var column = tokenizer.Column;

                    var parameter = tokenizer.ReadIdentifier();
                    if (parameter.IsEmpty)
                    {
                        return(ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column));
                    }

                    var variableDefinition = new VariableDefinitionExpression(parameter.ToString(), line, column);
                    Parameters.Add(variableDefinition);

                    ExpressionBase.SkipWhitespace(tokenizer);

                    if (tokenizer.NextChar == '=')
                    {
                        tokenizer.Advance();
                        ExpressionBase.SkipWhitespace(tokenizer);

                        var value = ExpressionBase.Parse(tokenizer);
                        if (value.Type == ExpressionType.ParseError)
                        {
                            return(ExpressionBase.ParseError(tokenizer, "Invalid default value for " + parameter.ToString(), value));
                        }

                        var scope = new InterpreterScope(AchievementScriptInterpreter.GetGlobalScope());
                        scope.Context = new TriggerBuilderContext(); // prevent errors passing memory references as default parameters

                        ExpressionBase evaluated;
                        if (!value.ReplaceVariables(scope, out evaluated))
                        {
                            return(ExpressionBase.ParseError(tokenizer, "Default value for " + parameter.ToString() + " is not constant", evaluated));
                        }

                        DefaultParameters[parameter.ToString()] = evaluated;
                    }
                    else if (DefaultParameters.Count > 0)
                    {
                        return(ExpressionBase.ParseError(tokenizer,
                                                         string.Format("Non-default parameter {0} appears after default parameters", parameter.ToString()), variableDefinition));
                    }

                    if (tokenizer.NextChar == ')')
                    {
                        break;
                    }

                    if (tokenizer.NextChar != ',')
                    {
                        return(ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar));
                    }

                    tokenizer.Advance();
                    ExpressionBase.SkipWhitespace(tokenizer);
                } while (true);
            }

            tokenizer.Advance(); // closing parenthesis
            ExpressionBase.SkipWhitespace(tokenizer);

            ExpressionBase expression;

            if (tokenizer.Match("=>"))
            {
                return(ParseShorthandBody(tokenizer));
            }

            if (tokenizer.NextChar != '{')
            {
                return(ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", Name));
            }

            tokenizer.Advance();
            ExpressionBase.SkipWhitespace(tokenizer);

            bool seenReturn = false;

            while (tokenizer.NextChar != '}')
            {
                expression = ExpressionBase.Parse(tokenizer);
                if (expression.Type == ExpressionType.ParseError)
                {
                    // the ExpressionTokenizer will capture the error, we should still return the incomplete FunctionDefinition
                    if (tokenizer is ExpressionTokenizer)
                    {
                        break;
                    }

                    // not an ExpressionTokenizer, just return the error
                    return(expression);
                }

                if (expression.Type == ExpressionType.Return)
                {
                    seenReturn = true;
                }
                else if (seenReturn)
                {
                    ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression);
                }

                Expressions.Add(expression);

                ExpressionBase.SkipWhitespace(tokenizer);
            }

            Location = new TextRange(Location.Start, tokenizer.Location);
            tokenizer.Advance();
            return(MakeReadOnly(this));
        }
Example #12
0
        /// <summary>
        /// Parses a function definition.
        /// </summary>
        /// <remarks>
        /// Assumes the 'function' keyword has already been consumed.
        /// </remarks>
        internal static ExpressionBase Parse(PositionalTokenizer tokenizer, int line = 0, int column = 0)
        {
            var function = new FunctionDefinitionExpression();

            function._keyword = new KeywordExpression("function", line, column);

            ExpressionBase.SkipWhitespace(tokenizer);

            line   = tokenizer.Line;
            column = tokenizer.Column;

            var functionName = tokenizer.ReadIdentifier();

            function.Name = new VariableDefinitionExpression(functionName.ToString(), line, column);
            if (functionName.IsEmpty)
            {
                ExpressionBase.ParseError(tokenizer, "Invalid function name");
                return(function);
            }

            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != '(')
            {
                ExpressionBase.ParseError(tokenizer, "Expected '(' after function name", function.Name);
                return(function);
            }
            tokenizer.Advance();

            ExpressionBase.SkipWhitespace(tokenizer);
            if (tokenizer.NextChar != ')')
            {
                do
                {
                    line   = tokenizer.Line;
                    column = tokenizer.Column;

                    var parameter = tokenizer.ReadIdentifier();
                    if (parameter.IsEmpty)
                    {
                        ExpressionBase.ParseError(tokenizer, "Invalid parameter name", line, column);
                        return(function);
                    }

                    function.Parameters.Add(new VariableDefinitionExpression(parameter.ToString(), line, column));

                    ExpressionBase.SkipWhitespace(tokenizer);
                    if (tokenizer.NextChar == ')')
                    {
                        break;
                    }

                    if (tokenizer.NextChar != ',')
                    {
                        ExpressionBase.ParseError(tokenizer, "Expected ',' or ')' after parameter name, found: " + tokenizer.NextChar);
                        return(function);
                    }

                    tokenizer.Advance();
                    ExpressionBase.SkipWhitespace(tokenizer);
                } while (true);
            }

            tokenizer.Advance(); // closing parenthesis
            ExpressionBase.SkipWhitespace(tokenizer);

            ExpressionBase expression;

            if (tokenizer.Match("=>"))
            {
                ExpressionBase.SkipWhitespace(tokenizer);

                expression = ExpressionBase.Parse(tokenizer);
                if (expression.Type == ExpressionType.ParseError)
                {
                    return(expression);
                }

                if (expression.Type == ExpressionType.Return)
                {
                    return(new ParseErrorExpression("Return statement is implied by =>", ((ReturnExpression)expression).Keyword));
                }

                var returnExpression = new ReturnExpression(expression);
                function.Expressions.Add(returnExpression);
                return(function);
            }

            if (tokenizer.NextChar != '{')
            {
                ExpressionBase.ParseError(tokenizer, "Expected '{' after function declaration", function.Name);
                return(function);
            }

            line   = tokenizer.Line;
            column = tokenizer.Column;
            tokenizer.Advance();
            ExpressionBase.SkipWhitespace(tokenizer);

            bool seenReturn = false;

            while (tokenizer.NextChar != '}')
            {
                expression = ExpressionBase.Parse(tokenizer);
                if (expression.Type == ExpressionType.ParseError)
                {
                    return(expression);
                }

                if (expression.Type == ExpressionType.Return)
                {
                    seenReturn = true;
                }
                else if (seenReturn)
                {
                    ExpressionBase.ParseError(tokenizer, "Expression after return statement", expression);
                }

                function.Expressions.Add(expression);

                ExpressionBase.SkipWhitespace(tokenizer);
            }

            tokenizer.Advance();
            return(function);
        }
Example #13
0
        public override bool Evaluate(InterpreterScope scope, out ExpressionBase result)
        {
            var stringExpression = GetStringParameter(scope, "format_string", out result);

            if (stringExpression == null)
            {
                return(false);
            }

            var varargs = GetParameter(scope, "varargs", out result) as ArrayExpression;

            if (varargs == null)
            {
                if (!(result is ParseErrorExpression))
                {
                    result = new ParseErrorExpression("unexpected varargs", stringExpression);
                }
                return(false);
            }

            var builder = new StringBuilder();

            var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(stringExpression.Value));

            while (tokenizer.NextChar != '\0')
            {
                var token = tokenizer.ReadTo('{');
                builder.Append(token.ToString());

                if (tokenizer.NextChar == '\0')
                {
                    break;
                }

                var positionalTokenColumn = tokenizer.Column;
                tokenizer.Advance();
                if (tokenizer.NextChar == '}')
                {
                    result = new ParseErrorExpression("Empty parameter index",
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + positionalTokenColumn,
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + tokenizer.Column - 1);
                    return(false);
                }
                var index = tokenizer.ReadNumber();
                if (tokenizer.NextChar != '}')
                {
                    result = new ParseErrorExpression("Invalid positional token",
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + positionalTokenColumn,
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + tokenizer.Column - 1);
                    return(false);
                }
                tokenizer.Advance();

                Int32 parameterIndex;
                if (!Int32.TryParse(index.ToString(), out parameterIndex) ||
                    parameterIndex < 0 || parameterIndex >= varargs.Entries.Count)
                {
                    result = new ParseErrorExpression("Invalid parameter index: " + index.ToString(),
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + positionalTokenColumn,
                                                      stringExpression.Location.Start.Line, stringExpression.Location.Start.Column + tokenizer.Column - 1);
                    return(false);
                }

                result = varargs.Entries[parameterIndex];
                var functionCall = result as FunctionCallExpression;
                if (functionCall != null)
                {
                    if (!functionCall.Evaluate(scope, out result))
                    {
                        return(false);
                    }
                }

                if (result.IsLiteralConstant)
                {
                    result.AppendStringLiteral(builder);
                }
                else
                {
                    result = new ParseErrorExpression("Cannot convert expression to string", result);
                    return(false);
                }
            }

            result = new StringConstantExpression(builder.ToString());
            return(true);
        }
Example #14
0
        private static ExpressionBase ParseClauseCore(PositionalTokenizer tokenizer)
        {
            ExpressionBase clause;

            switch (tokenizer.NextChar)
            {
            case '!':
                tokenizer.Advance();
                clause = ParseClause(tokenizer);
                if (clause.Type == ExpressionType.ParseError)
                {
                    return(clause);
                }

                return(new ConditionalExpression(null, ConditionalOperation.Not, clause));

            case '(':
                tokenizer.Advance();
                clause = ExpressionBase.Parse(tokenizer);
                if (clause.Type == ExpressionType.ParseError)
                {
                    return(clause);
                }

                if (tokenizer.NextChar != ')')
                {
                    if (tokenizer.NextChar == '\0')
                    {
                        return(ParseError(tokenizer, "No closing parenthesis found"));
                    }

                    return(ParseError(tokenizer, "Expected closing parenthesis, found: " + tokenizer.NextChar));
                }

                clause.IsLogicalUnit = true;
                tokenizer.Advance();
                return(clause);

            case '"':
                try
                {
                    var stringValue = tokenizer.ReadQuotedString().ToString();
                    return(new StringConstantExpression(stringValue));
                }
                catch (InvalidOperationException ex)
                {
                    return(ParseError(tokenizer, ex.Message));
                }

            case '0':
                if (tokenizer.Match("0x"))
                {
                    string hexNumber = "";
                    while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f'))
                    {
                        hexNumber += tokenizer.NextChar;
                        tokenizer.Advance();
                    }

                    int hexValue = 0;
                    Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue);
                    return(new IntegerConstantExpression(hexValue));
                }
                goto case '1';

            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            {
                var  number = tokenizer.ReadNumber();
                uint value;
                UInt32.TryParse(number.ToString(), out value);
                return(new IntegerConstantExpression((int)value));
            }

            case '-':
                tokenizer.Advance();
                if (tokenizer.NextChar >= '0' && tokenizer.NextChar <= '9')
                {
                    if (tokenizer.Match("0x"))
                    {
                        string hexNumber = "";
                        while (Char.IsDigit(tokenizer.NextChar) || (tokenizer.NextChar >= 'A' && tokenizer.NextChar <= 'F') || (tokenizer.NextChar >= 'a' && tokenizer.NextChar <= 'f'))
                        {
                            hexNumber += tokenizer.NextChar;
                            tokenizer.Advance();
                        }

                        int hexValue = 0;
                        Int32.TryParse(hexNumber, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.CurrentCulture, out hexValue);
                        return(new IntegerConstantExpression(-hexValue));
                    }

                    var number = tokenizer.ReadNumber();
                    int value;
                    Int32.TryParse(number.ToString(), out value);
                    return(new IntegerConstantExpression(-value));
                }
                return(ParseError(tokenizer, "Minus without value"));

            case '{':
                tokenizer.Advance();
                return(DictionaryExpression.Parse(tokenizer));

            case '[':
                tokenizer.Advance();
                return(ParseArray(tokenizer));

            default:
                var line       = tokenizer.Line;
                var column     = tokenizer.Column;
                var identifier = tokenizer.ReadIdentifier();
                if (identifier.IsEmpty)
                {
                    var error = ParseError(tokenizer, "Unexpected character: " + tokenizer.NextChar);
                    tokenizer.Advance();
                    return(error);
                }

                SkipWhitespace(tokenizer);

                if (identifier == "return")
                {
                    clause = ExpressionBase.Parse(tokenizer);
                    if (clause.Type == ExpressionType.ParseError)
                    {
                        return(clause);
                    }

                    return(new ReturnExpression(new KeywordExpression(identifier.ToString(), line, column), clause));
                }

                if (identifier == "function")
                {
                    return(FunctionDefinitionExpression.Parse(tokenizer, line, column));
                }
                if (identifier == "for")
                {
                    return(ForExpression.Parse(tokenizer, line, column));
                }
                if (identifier == "if")
                {
                    return(IfExpression.Parse(tokenizer, line, column));
                }

                if (tokenizer.NextChar == '(')
                {
                    tokenizer.Advance();

                    var parameters = new List <ExpressionBase>();
                    ParseParameters(tokenizer, parameters);

                    var functionCall = new FunctionCallExpression(new VariableExpression(identifier.ToString(), line, column), parameters);
                    functionCall.EndLine   = tokenizer.Line;
                    functionCall.EndColumn = tokenizer.Column - 1;
                    return(functionCall);
                }

                if (tokenizer.NextChar == '[')
                {
                    IndexedVariableExpression parent = null;

                    do
                    {
                        tokenizer.Advance();

                        var index = ExpressionBase.Parse(tokenizer);
                        if (index.Type == ExpressionType.ParseError)
                        {
                            return(index);
                        }

                        SkipWhitespace(tokenizer);
                        if (tokenizer.NextChar != ']')
                        {
                            return(ParseError(tokenizer, "Expecting closing bracket after index"));
                        }
                        tokenizer.Advance();
                        SkipWhitespace(tokenizer);

                        if (parent != null)
                        {
                            parent = new IndexedVariableExpression(parent, index);
                        }
                        else
                        {
                            parent = new IndexedVariableExpression(new VariableExpression(identifier.ToString(), line, column), index);
                        }
                    } while (tokenizer.NextChar == '[');

                    return(parent);
                }

                return(new VariableExpression(identifier.ToString(), line, column));
            }
        }
Example #15
0
        /// <summary>
        /// Gets the next expression from the input.
        /// </summary>
        /// <returns>The next expression, <c>null</c> if at end of file.</returns>
        public static ExpressionBase Parse(PositionalTokenizer tokenizer)
        {
            SkipWhitespace(tokenizer);

            if (tokenizer.NextChar == '\0')
            {
                return(new ParseErrorExpression("Unexpected end of script", tokenizer.Line, tokenizer.Column, tokenizer.Line, tokenizer.Column));
            }

            var line   = tokenizer.Line;
            var column = tokenizer.Column;

            var clause = ExpressionBase.ParseClause(tokenizer);

            if (clause.Type == ExpressionType.ParseError || clause.Type == ExpressionType.Comment)
            {
                return(clause);
            }

            var clauseEndLine   = tokenizer.Line;
            var clauseEndColumn = tokenizer.Column;

            SkipWhitespace(tokenizer);

            var joinerLine   = tokenizer.Line;
            var joinerColumn = tokenizer.Column;

            switch (tokenizer.NextChar)
            {
            case '+':
                tokenizer.Advance();
                clause = ParseMathematic(tokenizer, clause, MathematicOperation.Add, joinerLine, joinerColumn);
                break;

            case '-':
                tokenizer.Advance();
                clause = ParseMathematic(tokenizer, clause, MathematicOperation.Subtract, joinerLine, joinerColumn);
                break;

            case '*':
                tokenizer.Advance();
                clause = ParseMathematic(tokenizer, clause, MathematicOperation.Multiply, joinerLine, joinerColumn);
                break;

            case '/':
                tokenizer.Advance();
                clause = ParseMathematic(tokenizer, clause, MathematicOperation.Divide, joinerLine, joinerColumn);
                break;

            case '%':
                tokenizer.Advance();
                clause = ParseMathematic(tokenizer, clause, MathematicOperation.Modulus, joinerLine, joinerColumn);
                break;

            case '=':
                tokenizer.Advance();
                if (tokenizer.NextChar == '=')
                {
                    tokenizer.Advance();
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.Equal, joinerLine, joinerColumn);
                }
                else
                {
                    clause = ParseAssignment(tokenizer, clause, joinerLine, joinerColumn);
                }
                break;

            case '!':
                tokenizer.Advance();
                if (tokenizer.NextChar != '=')
                {
                    clause = ParseError(tokenizer, "= expected following !", joinerLine, joinerColumn);
                }
                else
                {
                    tokenizer.Advance();
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.NotEqual, joinerLine, joinerColumn);
                }
                break;

            case '<':
                tokenizer.Advance();
                if (tokenizer.NextChar == '=')
                {
                    tokenizer.Advance();
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.LessThanOrEqual, joinerLine, joinerColumn);
                }
                else
                {
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.LessThan, joinerLine, joinerColumn);
                }
                break;

            case '>':
                tokenizer.Advance();
                if (tokenizer.NextChar == '=')
                {
                    tokenizer.Advance();
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.GreaterThanOrEqual, joinerLine, joinerColumn);
                }
                else
                {
                    clause = ParseComparison(tokenizer, clause, ComparisonOperation.GreaterThan, joinerLine, joinerColumn);
                }
                break;

            case '&':
                tokenizer.Advance();
                if (tokenizer.NextChar != '&')
                {
                    clause = ParseError(tokenizer, "& expected following &", joinerLine, joinerColumn);
                }
                else
                {
                    tokenizer.Advance();
                    clause = ParseConditional(tokenizer, clause, ConditionalOperation.And, joinerLine, joinerColumn);
                    if (clause.Type == ExpressionType.ParseError)
                    {
                        return(clause);
                    }
                }
                break;

            case '|':
                tokenizer.Advance();
                if (tokenizer.NextChar != '|')
                {
                    clause = ParseError(tokenizer, "| expected following |", joinerLine, joinerColumn);
                }
                else
                {
                    tokenizer.Advance();
                    clause = ParseConditional(tokenizer, clause, ConditionalOperation.Or, joinerLine, joinerColumn);
                    if (clause.Type == ExpressionType.ParseError)
                    {
                        return(clause);
                    }
                }
                break;

            default:
                if (clause.EndColumn == 0)
                {
                    clause.EndLine   = clauseEndLine;
                    clause.EndColumn = clauseEndColumn;
                }
                return(clause);
            }

            clause = clause.Rebalance();

            Debug.Assert(clause.Line != 0);
            Debug.Assert(clause.Column != 0);
            Debug.Assert(clause.EndLine != 0);
            Debug.Assert(clause.EndColumn != 0);

            return(clause);
        }