Пример #1
0
        internal static CodeNode Parse(ParseInfo state, ref int index)
        {
            if (state.Code[index] != '{')
            {
                throw new ArgumentException("Invalid JSON definition");
            }

            var flds = new Dictionary <string, Expression>();
            var computedProperties = new List <KeyValuePair <Expression, Expression> >();
            int i = index;

            while (state.Code[i] != '}')
            {
                i++;
                Tools.SkipSpaces(state.Code, ref i);
                int s = i;
                if (state.Code[i] == '}')
                {
                    break;
                }

                bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i);
                Tools.SkipSpaces(state.Code, ref i);
                if (getOrSet && state.Code[i] == '(')  // function with name 'get' or 'set'
                {
                    getOrSet = false;
                    i        = s;
                }

                var asterisk = state.Code[i] == '*';
                Tools.SkipSpaces(state.Code, ref i);

                var async = false;
                if (!asterisk)
                {
                    async = Parser.Validate(state.Code, "async", ref i);
                    Tools.SkipSpaces(state.Code, ref i);
                }

                if (Parser.Validate(state.Code, "[", ref i))
                {
                    var name = ExpressionTree.Parse(state, ref i, false, false, false, true, false);
                    while (Tools.IsWhiteSpace(state.Code[i]))
                    {
                        i++;
                    }
                    if (state.Code[i] != ']')
                    {
                        ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i);
                    }
                    do
                    {
                        i++;
                    }while (Tools.IsWhiteSpace(state.Code[i]));

                    Tools.SkipSpaces(state.Code, ref i);
                    CodeNode initializer;
                    if (state.Code[i] == '(')
                    {
                        initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : async ? FunctionKind.AsyncAnonymousFunction : FunctionKind.AnonymousFunction);
                    }
                    else
                    {
                        if (!Parser.Validate(state.Code, ":", ref i))
                        {
                            ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, state.Code, i);
                        }
                        initializer = ExpressionTree.Parse(state, ref i);
                    }

                    switch (state.Code[s])
                    {
                    case 'g':
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair((Expression)initializer, null)));
                        break;
                    }

                    case 's':
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair(null, (Expression)initializer)));
                        break;
                    }

                    default:
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, (Expression)initializer));
                        break;
                    }
                    }
                }
                else if (getOrSet && state.Code[i] != ':')
                {
                    i = s;
                    var mode             = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter;
                    var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition;
                    var accessorName     = propertyAccessor._name;
                    if (!flds.ContainsKey(accessorName))
                    {
                        var propertyPair = new PropertyPair
                                           (
                            mode == FunctionKind.Getter ? propertyAccessor : null,
                            mode == FunctionKind.Setter ? propertyAccessor : null
                                           );
                        flds.Add(accessorName, propertyPair);
                    }
                    else
                    {
                        var vle = flds[accessorName] as PropertyPair;

                        if (vle == null)
                        {
                            ExceptionHelper.ThrowSyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field", state.Code, s);
                        }

                        do
                        {
                            if (mode == FunctionKind.Getter)
                            {
                                if (vle.Getter == null)
                                {
                                    vle.Getter = propertyAccessor;
                                    break;
                                }
                            }
                            else
                            {
                                if (vle.Setter == null)
                                {
                                    vle.Setter = propertyAccessor;
                                    break;
                                }
                            }

                            ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, s);
                        }while (false);
                    }
                }
                else
                {
                    if (asterisk)
                    {
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));
                    }

                    i = s;
                    var fieldName = "";
                    if (Parser.ValidateName(state.Code, ref i, false, true, state.Strict))
                    {
                        fieldName = Tools.Unescape(state.Code.Substring(s, i - s), state.Strict);
                    }
                    else if (Parser.ValidateValue(state.Code, ref i))
                    {
                        if (state.Code[s] == '-')
                        {
                            ExceptionHelper.Throw(new SyntaxError("Invalid char \"-\" at " + CodeCoordinates.FromTextPosition(state.Code, s, 1)));
                        }
                        double d = 0.0;
                        int    n = s;
                        if (Tools.ParseNumber(state.Code, ref n, out d))
                        {
                            fieldName = Tools.DoubleToString(d);
                        }
                        else if (state.Code[s] == '\'' || state.Code[s] == '"')
                        {
                            fieldName = Tools.Unescape(state.Code.Substring(s + 1, i - s - 2), state.Strict);
                        }
                        else if (flds.Count != 0)
                        {
                            ExceptionHelper.Throw((new SyntaxError("Invalid field name at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s))));
                        }
                        else
                        {
                            return(null);
                        }
                    }
                    else
                    {
                        return(null);
                    }

                    while (Tools.IsWhiteSpace(state.Code[i]))
                    {
                        i++;
                    }

                    Expression initializer = null;

                    if (state.Code[i] == '(')
                    {
                        i           = s;
                        initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.MethodGenerator : async ? FunctionKind.AsyncMethod : FunctionKind.Method);
                    }
                    else
                    {
                        if (asterisk || (async && state.Code[i] != ':'))
                        {
                            ExceptionHelper.ThrowSyntaxError("Unexpected token", state.Code, i);
                        }

                        if (state.Code[i] != ':' && state.Code[i] != ',' && state.Code[i] != '}')
                        {
                            ExceptionHelper.ThrowSyntaxError("Expected ',', ';' or '}'", state.Code, i);
                        }

                        Expression aei = null;
                        if (flds.TryGetValue(fieldName, out aei))
                        {
                            if (state.Strict ? (!(aei is Constant) || (aei as Constant).value != JSValue.undefined) : aei is PropertyPair)
                            {
                                ExceptionHelper.ThrowSyntaxError("Try to redefine field \"" + fieldName + "\"", state.Code, s, i - s);
                            }

                            if (state.Message != null)
                            {
                                state.Message(MessageLevel.Warning, i, 0, "Duplicate key \"" + fieldName + "\"");
                            }
                        }

                        if (state.Code[i] == ',' || state.Code[i] == '}')
                        {
                            if (!Parser.ValidateName(fieldName, 0))
                            {
                                ExceptionHelper.ThrowSyntaxError("Invalid variable name", state.Code, i);
                            }

                            initializer = new Variable(fieldName, state.LexicalScopeLevel);
                        }
                        else
                        {
                            do
                            {
                                i++;
                            }while (Tools.IsWhiteSpace(state.Code[i]));
                            initializer = ExpressionTree.Parse(state, ref i, false, false);
                        }
                    }
                    flds[fieldName] = initializer;
                }

                while (Tools.IsWhiteSpace(state.Code[i]))
                {
                    i++;
                }

                if ((state.Code[i] != ',') && (state.Code[i] != '}'))
                {
                    return(null);
                }
            }

            i++;
            var pos = index;

            index = i;
            return(new ObjectDefinition(flds, computedProperties.ToArray())
            {
                Position = pos,
                Length = index - pos
            });
        }
Пример #2
0
        internal static CodeNode Parse(ParseInfo state, ref int index)
        {
            int i = index;

            if (!Parser.Validate(state.Code, "class", ref i))
            {
                return(null);
            }

            Tools.SkipSpaces(state.Code, ref i);

            string     name     = null;
            Expression baseType = null;

            if (!Parser.Validate(state.Code, "extends ", i))
            {
                var n = i;
                if (Parser.ValidateName(state.Code, ref i, true))
                {
                    name = state.Code.Substring(n, i - n);
                }

                while (Tools.IsWhiteSpace(state.Code[i]))
                {
                    i++;
                }
            }
            if (Parser.Validate(state.Code, "extends ", ref i))
            {
                var n = i;
                if (!Parser.ValidateName(state.Code, ref i, true) && !Parser.Validate(state.Code, "null", ref i))
                {
                    ExceptionHelper.ThrowSyntaxError("Invalid base class name", state.Code, i);
                }

                var baseClassName = state.Code.Substring(n, i - n);
                if (baseClassName == "null")
                {
                    baseType = new Constant(JSValue.@null);
                }
                else
                {
                    baseType = new Variable(baseClassName, 1);
                }

                baseType.Position = n;
                baseType.Length   = i - n;

                while (Tools.IsWhiteSpace(state.Code[i]))
                {
                    i++;
                }
            }

            if (state.Code[i] != '{')
            {
                ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, state.Code, i);
            }

            FunctionDefinition ctor   = null;
            ClassDefinition    result = null;

            var flds = new Dictionary <string, MemberDescriptor>();
            var computedProperties = new List <MemberDescriptor>();

            while (state.Code[i] != '}')
            {
                using (state.WithCodeContext(CodeContext.Strict | CodeContext.InExpression))
                {
                    do
                    {
                        i++;
                    } while (Tools.IsWhiteSpace(state.Code[i]) || state.Code[i] == ';');

                    int s = i;
                    if (state.Code[i] == '}')
                    {
                        break;
                    }

                    bool @static = Parser.Validate(state.Code, "static", ref i);
                    if (@static)
                    {
                        Tools.SkipSpaces(state.Code, ref i);
                        s = i;
                    }

                    bool @async = Parser.Validate(state.Code, "async", ref i);
                    if (@async)
                    {
                        Tools.SkipSpaces(state.Code, ref i);
                        s = i;
                    }

                    bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i);
                    if (getOrSet)
                    {
                        Tools.SkipSpaces(state.Code, ref i);
                    }

                    var asterisk = state.Code[i] == '*';
                    if (asterisk)
                    {
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));
                    }

                    if (Parser.Validate(state.Code, "[", ref i))
                    {
                        var propertyName = ExpressionTree.Parse(state, ref i, false, false, false, true, false);
                        while (Tools.IsWhiteSpace(state.Code[i]))
                        {
                            i++;
                        }
                        if (state.Code[i] != ']')
                        {
                            ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i);
                        }
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));

                        CodeNode initializer;
                        if (state.Code[i] == '(')
                        {
                            initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : FunctionKind.AnonymousFunction);
                        }
                        else
                        {
                            initializer = ExpressionTree.Parse(state, ref i);
                        }

                        switch (state.Code[s])
                        {
                        case 'g':
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair((Expression)initializer, null), @static));
                            break;
                        }

                        case 's':
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair(null, (Expression)initializer), @static));
                            break;
                        }

                        default:
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, (Expression)initializer, @static));
                            break;
                        }
                        }
                    }
                    else if (getOrSet)
                    {
                        i = s;
                        var mode             = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter;
                        var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition;
                        var accessorName     = (@static ? "static " : "") + propertyAccessor._name;
                        if (!flds.ContainsKey(accessorName))
                        {
                            var propertyPair = new PropertyPair
                                               (
                                mode == FunctionKind.Getter ? propertyAccessor : null,
                                mode == FunctionKind.Setter ? propertyAccessor : null
                                               );
                            flds.Add(accessorName, new MemberDescriptor(new Constant(propertyAccessor._name), propertyPair, @static));
                        }
                        else
                        {
                            var vle = flds[accessorName].Value as PropertyPair;

                            if (vle == null)
                            {
                                ExceptionHelper.Throw((new SyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field at " + CodeCoordinates.FromTextPosition(state.Code, s, 0))));
                            }

                            do
                            {
                                if (mode == FunctionKind.Getter)
                                {
                                    if (vle.Getter == null)
                                    {
                                        vle.Getter = propertyAccessor;
                                        break;
                                    }
                                }
                                else
                                {
                                    if (vle.Setter == null)
                                    {
                                        vle.Setter = propertyAccessor;
                                        break;
                                    }
                                }

                                ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, s);
                            }while (false);
                        }
                    }
                    else
                    {
                        i = s;
                        string fieldName = null;
                        if (state.Code[i] == '*')
                        {
                            do
                            {
                                i++;
                            }while (Tools.IsWhiteSpace(state.Code[i]));
                        }

                        if (Parser.ValidateName(state.Code, ref i, false, true, state.Strict))
                        {
                            fieldName = Tools.Unescape(state.Code.Substring(s, i - s), state.Strict);
                        }
                        else if (Parser.ValidateValue(state.Code, ref i))
                        {
                            double d = 0.0;
                            int    n = s;
                            if (Tools.ParseNumber(state.Code, ref n, out d))
                            {
                                fieldName = Tools.DoubleToString(d);
                            }
                            else if (state.Code[s] == '\'' || state.Code[s] == '"')
                            {
                                fieldName = Tools.Unescape(state.Code.Substring(s + 1, i - s - 2), state.Strict);
                            }
                        }

                        if (fieldName == null)
                        {
                            ExceptionHelper.Throw((new SyntaxError("Invalid member name at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s))));
                        }

                        if (fieldName == "constructor")
                        {
                            if (@static)
                            {
                                ExceptionHelper.ThrowSyntaxError(Strings.ConstructorCannotBeStatic, state.Code, s);
                            }
                            if (ctor != null)
                            {
                                ExceptionHelper.ThrowSyntaxError("Trying to redefinition constructor", state.Code, s);
                            }

                            state.CodeContext |= CodeContext.InClassConstructor;
                        }
                        else if (@static)
                        {
                            fieldName          = "static " + fieldName;
                            state.CodeContext |= CodeContext.InStaticMember;
                        }
                        if (flds.ContainsKey(fieldName))
                        {
                            ExceptionHelper.Throw(new SyntaxError("Trying to redefinition member \"" + fieldName + "\" at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s)));
                        }

                        state.CodeContext |= CodeContext.InClassDefinition;
                        state.CodeContext &= ~CodeContext.InGenerator;

                        if (async)
                        {
                            state.CodeContext |= CodeContext.InAsync;
                        }

                        i = s;
                        var method = FunctionDefinition.Parse(state, ref i, async ? FunctionKind.AsyncMethod : FunctionKind.Method) as FunctionDefinition;
                        if (method == null)
                        {
                            ExceptionHelper.ThrowSyntaxError("Unable to parse method", state.Code, i);
                        }

                        if (fieldName == "constructor")
                        {
                            ctor = method;
                        }
                        else
                        {
                            flds[fieldName] = new MemberDescriptor(new Constant(method._name), method, @static);
                        }
                    }
                }
            }

            if (ctor == null)
            {
                string ctorCode;
                int    ctorIndex = 0;
                if (baseType != null && !(baseType is Constant))
                {
                    ctorCode = "constructor(...args) { super(...args); }";
                }
                else
                {
                    ctorCode = "constructor(...args) { }";
                }

                var nestedParseInfo = state.AlternateCode(ctorCode);
                using (nestedParseInfo.WithCodeContext(CodeContext.InClassConstructor | CodeContext.InClassDefinition))
                    ctor = (FunctionDefinition)FunctionDefinition.Parse(nestedParseInfo, ref ctorIndex, FunctionKind.Method);
            }

            result = new ClassDefinition(name, baseType, new List <MemberDescriptor>(flds.Values).ToArray(), ctor as FunctionDefinition, computedProperties.ToArray());

            if ((state.CodeContext & CodeContext.InExpression) == 0)
            {
                if (string.IsNullOrEmpty(name))
                {
                    ExceptionHelper.ThrowSyntaxError("Class must have name", state.Code, index);
                }
                if (state.Strict && state.FunctionScopeLevel != state.LexicalScopeLevel)
                {
                    ExceptionHelper.ThrowSyntaxError("In strict mode code, class can only be declared at top level or immediately within other function.", state.Code, index);
                }

                state.Variables.Add(result.reference._descriptor);
            }
            index = i + 1;
            return(result);
        }