internal static CodeNode Parse(ParseInfo state, ref int index) { string code = state.Code; int i = index; if (!Parser.Validate(code, "class", ref i)) { return(null); } while (Tools.IsWhiteSpace(code[i])) { i++; } string name = null; Expression baseType = null; if (!Parser.Validate(code, "extends ", i)) { var n = i; if (Parser.ValidateName(code, ref i, true)) { name = code.Substring(n, i - n); } while (Tools.IsWhiteSpace(code[i])) { i++; } } if (Parser.Validate(code, "extends ", ref i)) { var n = i; if (!Parser.ValidateName(code, ref i, true) && !Parser.Validate(code, "null", ref i)) { ExceptionHelper.ThrowSyntaxError("Invalid base class name", state.Code, i); } var baseClassName = 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(code[i])) { i++; } } if (code[i] != '{') { ExceptionHelper.ThrowSyntaxError(Messages.UnexpectedToken, code, i); } FunctionDefinition ctor = null; ClassDefinition result = null; var oldStrict = state.Strict; state.Strict = true; var flds = new Dictionary <string, MemberDescriptor>(); var computedProperties = new List <MemberDescriptor>(); var oldCodeContext = state.CodeContext; state.CodeContext |= CodeContext.InExpression; try { while (code[i] != '}') { do { i++; }while (Tools.IsWhiteSpace(code[i]) || 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 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])); var initializer = state.Code[i] == '(' ? FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : FunctionKind.AnonymousFunction) : ExpressionTree.Parse(state, ref i); switch (state.Code[s]) { case 'g': { computedProperties.Add(new MemberDescriptor(propertyName, new PropertyPair((Expression)initializer, null), @static)); break; } case 's': { computedProperties.Add(new MemberDescriptor(propertyName, new PropertyPair(null, (Expression)initializer), @static)); break; } default: { computedProperties.Add(new MemberDescriptor(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(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(Messages.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.InClassDefenition; state.CodeContext &= ~CodeContext.InGenerator; i = s; var method = FunctionDefinition.Parse(state, ref i, FunctionKind.Method) as FunctionDefinition; if (method == null) { ExceptionHelper.ThrowSyntaxError("Unable to parse constructor", state.Code, i); } if (fieldName == "constructor") { ctor = method; } else { flds[fieldName] = new MemberDescriptor(new Constant(method.Name), method, @static); } } code = state.Code; } if (ctor == null) { string ctorCode; int ctorIndex = 0; if (baseType != null && !(baseType is Constant)) { ctorCode = "constructor(...args) { super(...args); }"; } else { ctorCode = "constructor(...args) { }"; } ctor = (FunctionDefinition)FunctionDefinition.Parse( new ParseInfo(ctorCode, ctorCode, null) { Strict = true, CodeContext = CodeContext.InClassConstructor | CodeContext.InClassDefenition }, ref ctorIndex, FunctionKind.Method); } result = new ClassDefinition(name, baseType, new List <MemberDescriptor>(flds.Values).ToArray(), ctor, computedProperties.ToArray()); if ((oldCodeContext & 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); } } finally { state.CodeContext = oldCodeContext; state.Strict = oldStrict; } index = i + 1; return(result); }
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); 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); if (state.Code[s] != 'g' && state.Code[s] != 's') { if (!Parser.Validate(state.Code, ":", ref i)) { ExceptionHelper.ThrowSyntaxError(Messages.UnexpectedToken, state.Code, i); } Tools.SkipSpaces(state.Code, ref i); } CodeNode initializer; initializer = state.Code[i] == '(' ? FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : FunctionKind.AnonymousFunction) : 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 : FunctionKind.Method); } else { if (asterisk) { 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); } state.Message?.Invoke(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 }); }