/// <summary> /// Creates an instance of the given prototype. /// </summary> internal SObject CreateInstance(Prototype prototype, SObject[] parameters) { if (!prototype.IsAbstract) return prototype.CreateInstance(_processor, parameters, true); else return _processor.ErrorHandler.ThrowError(ErrorType.TypeError, ErrorHandler.MESSAGE_TYPE_ABSTRACT_NO_INSTANCE); }
internal void AddPrototype(Prototype prototype) { if (_prototypes.ContainsKey(prototype.Name)) _prototypes[prototype.Name] = prototype; else _prototypes.Add(prototype.Name, prototype); }
/// <summary> /// Parses an anonymous object. /// </summary> internal static SProtoObject Parse(ScriptProcessor processor, string source) { var prototype = new Prototype(LITERAL_OBJECT); source = source.Trim(); // Format: { member1 : content1, member2 : content2 } // Remove "{" and "}": source = source.Remove(source.Length - 1, 1).Remove(0, 1).Trim(); if (source.Length == 0) return prototype.CreateInstance(processor, null, false); var index = 0; var identifier = ""; var content = ""; SObject contentObj = null; while (index < source.Length) { var nextSeperatorIndex = source.IndexOf(",", index); if (nextSeperatorIndex == -1) { nextSeperatorIndex = source.Length; } else { //Let's find the correct ",": nextSeperatorIndex = index; var depth = 0; StringEscapeHelper escaper = new LeftToRightStringEscapeHelper(source, nextSeperatorIndex, true); var foundSeperator = false; while (!foundSeperator && nextSeperatorIndex < source.Length) { var t = source[nextSeperatorIndex]; escaper.CheckStartAt(nextSeperatorIndex); if (!escaper.IsString) { if (t == '{' || t == '[' || t == '(') { depth++; } else if (t == '}' || t == ']' || t == ')') { depth--; } else if (t == ',' && depth == 0) { foundSeperator = true; nextSeperatorIndex--; // it adds one to the index at the end of the while loop, but we need the index where we stopped searching, so we subtract 1 here to accommodate for that. } } nextSeperatorIndex++; } } var member = source.Substring(index, nextSeperatorIndex - index); if (member.Contains(":")) { identifier = member.Remove(member.IndexOf(":")).Trim(); content = member.Remove(0, member.IndexOf(":") + 1); contentObj = processor.ExecuteStatement(new ScriptStatement(content)); } else { identifier = member.Trim(); contentObj = processor.Undefined; } if (!ScriptProcessor.IsValidIdentifier(identifier)) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_MISSING_VAR_NAME); prototype.AddMember(processor, new PrototypeMember(identifier, contentObj)); index = nextSeperatorIndex + 1; } return prototype.CreateInstance(processor, null, false); }
internal new static SObject Parse(ScriptProcessor processor, string code) { code = code.Trim(); if (Regex.IsMatch(code, REGEX_CLASS_SIGNATURE, RegexOptions.Singleline)) { var signature = code.Remove(code.IndexOf("{")).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var extends = ""; var identifier = ""; var isAbstract = false; // Read extends: if (signature.Contains(CLASS_SIGNATURE_EXTENDS)) { var extendsIndex = signature.IndexOf(CLASS_SIGNATURE_EXTENDS); if (extendsIndex + 1 == signature.Count) // when extends is the last element in the signature, throw error: processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_CLASS_EXTENDS_MISSING); extends = signature[extendsIndex + 1]; // The extended class name is after the "extends" keyword. signature.RemoveAt(extendsIndex); // Remove at the extends index twice, to remove the "extends" keyword and the identifier of the extended class. signature.RemoveAt(extendsIndex); } // Read abstract: if (signature.Contains(CLASS_SIGNATURE_ABSTRACT)) { isAbstract = true; signature.Remove(CLASS_SIGNATURE_ABSTRACT); } if (signature.Count != 2) // The signature must only have "class" and the identifier left. processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_INVALID_CLASS_SIGNATURE); // Read class name: identifier = signature[1]; if (!ScriptProcessor.IsValidIdentifier(identifier)) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_CLASS_IDENTIFIER_MISSING); // Create instance: var prototype = new Prototype(identifier) { IsAbstract = isAbstract }; // Handle extends: if (extends.Length > 0) { if (isAbstract) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_TYPE_ABSTRACT_NO_EXTENDS); var extendedPrototype = processor.Context.GetPrototype(extends); if (extendedPrototype == null) processor.ErrorHandler.ThrowError(ErrorType.ReferenceError, ErrorHandler.MESSAGE_REFERENCE_NO_PROTOTYPE, extends); prototype.Extends = extendedPrototype; } else { // Set default prototype: prototype.Extends = processor.Context.GetPrototype("Object"); } var body = code.Remove(0, code.IndexOf("{") + 1); body = body.Remove(body.Length - 1, 1).Trim(); var additionalCtorCode = ""; var staticCtorCode = ""; var statements = StatementProcessor.GetStatements(processor, body); for (var i = 0; i < statements.Length; i++) { var statement = statements[i]; if (statement.StatementType == StatementType.Var) { var parsed = ParseVarStatement(processor, statement); if (parsed.Item1.Identifier == CLASS_METHOD_CTOR) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_MISSING_VAR_NAME); prototype.AddMember(processor, parsed.Item1); if (parsed.Item2.Length > 0) { if (parsed.Item1.IsStatic) { staticCtorCode += string.Format(FORMAT_VAR_ASSIGNMENT, parsed.Item1.Identifier, parsed.Item2); } else { additionalCtorCode += string.Format(FORMAT_VAR_ASSIGNMENT, parsed.Item1.Identifier, parsed.Item2); } } } else if (statement.StatementType == StatementType.Function) { i++; if (statements.Length > i) { var bodyStatement = statements[i]; var parsed = ParseFunctionStatement(processor, statement, bodyStatement); if (parsed.Identifier == CLASS_METHOD_CTOR) { if (prototype.Constructor != null) processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_CLASS_DUPLICATE_DEFINITION, parsed.Identifier, identifier); prototype.Constructor = parsed; } else { prototype.AddMember(processor, parsed); } } else { return processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_EXPECTED_EXPRESSION, "end of script"); } } else { processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_CLASS_INVALID_STATEMENT); } } // Add additional constructor code & static constructor code to prototype: if (staticCtorCode.Length > 0) { prototype._staticConstructor = new SFunction(staticCtorCode, new string[] { }); prototype._staticConstructorProcessor = processor; } if (additionalCtorCode.Length > 0) { if (prototype.Constructor == null) { // Create new ctor if no one has been defined: prototype.Constructor = new PrototypeMember(CLASS_METHOD_CTOR, new SFunction("", new string[] { }), false, true, false, false); } prototype.Constructor.ToFunction().Body = additionalCtorCode + prototype.Constructor.ToFunction().Body; } return prototype; } else { return processor.ErrorHandler.ThrowError(ErrorType.SyntaxError, ErrorHandler.MESSAGE_SYNTAX_INVALID_CLASS_SIGNATURE); } }
internal static Prototype TranslatePrototype(ScriptProcessor processor, Type t) { var name = t.Name; var customNameAttribute = t.GetCustomAttribute<ScriptPrototypeAttribute>(); if (!string.IsNullOrWhiteSpace(customNameAttribute?.VariableName)) name = customNameAttribute.VariableName; var prototype = new Prototype(name); object typeInstance = null; if (!t.IsAbstract) { typeInstance = Activator.CreateInstance(t); } var fields = t .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) .Where(f => f.GetCustomAttribute<CompilerGeneratedAttribute>() == null) .ToArray(); foreach (var field in fields) { var attributes = field.GetCustomAttributes(false); foreach (var attr in attributes) { if (attr.GetType() == typeof(ScriptVariableAttribute)) { var memberAttr = (ScriptMemberAttribute)attr; var identifier = field.Name; if (!string.IsNullOrEmpty(memberAttr.VariableName)) identifier = memberAttr.VariableName; var fieldContent = field.GetValue(typeInstance); if (fieldContent == null) prototype.AddMember(processor, new PrototypeMember(identifier, processor.Undefined, field.IsStatic, field.IsInitOnly, false, false)); else prototype.AddMember(processor, new PrototypeMember(identifier, Translate(processor, fieldContent), field.IsStatic, field.IsInitOnly, false, false)); } else if (attr.GetType() == typeof(ScriptFunctionAttribute)) { var memberAttr = (ScriptFunctionAttribute)attr; var identifier = field.Name; if (!string.IsNullOrEmpty(memberAttr.VariableName)) identifier = memberAttr.VariableName; var fieldContent = field.GetValue(typeInstance); if (fieldContent == null) { if (memberAttr.FunctionType != ScriptFunctionType.Standard) throw new InvalidOperationException("A member function marked with Indexer Set, Indexer Get or Constructor has to be defined."); prototype.AddMember(processor, new PrototypeMember(identifier, processor.Undefined, field.IsStatic, field.IsInitOnly, false, false)); } else { switch (memberAttr.FunctionType) { case ScriptFunctionType.Standard: prototype.AddMember(processor, new PrototypeMember(identifier, new SFunction(processor, fieldContent.ToString()), field.IsStatic, field.IsInitOnly, false, false)); break; case ScriptFunctionType.IndexerGet: prototype.IndexerGetFunction = new SFunction(processor, fieldContent.ToString()); break; case ScriptFunctionType.IndexerSet: prototype.IndexerSetFunction = new SFunction(processor, fieldContent.ToString()); break; case ScriptFunctionType.Constructor: prototype.Constructor = new PrototypeMember(Prototype.CLASS_METHOD_CTOR, new SFunction(processor, fieldContent.ToString()), false, true, false, false); break; } } } } } var methods = t .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) .Where(f => f.GetCustomAttribute<CompilerGeneratedAttribute>() == null) .ToArray(); foreach (var method in methods) { var attr = method.GetCustomAttribute<ScriptFunctionAttribute>(false); if (attr != null) { var identifier = method.Name; if (!string.IsNullOrEmpty(attr.VariableName)) identifier = attr.VariableName; if (attr.FunctionType == ScriptFunctionType.Getter) identifier = SProtoObject.PROPERTY_GET_PREFIX + identifier; if (attr.FunctionType == ScriptFunctionType.Setter) identifier = SProtoObject.PROPERTY_SET_PREFIX + identifier; Delegate methodDelegate = null; if (method.GetParameters().Length == 3) { // two parameter means the method is a DotNetBuiltInMethod. methodDelegate = (DotNetBuiltInMethod)Delegate.CreateDelegate(typeof(DotNetBuiltInMethod), method); } else if (method.GetParameters().Length == 4) { // four parameters means that the method is a valid BuiltInMethod. methodDelegate = (BuiltInMethod)Delegate.CreateDelegate(typeof(BuiltInMethod), method); } switch (attr.FunctionType) { case ScriptFunctionType.Standard: case ScriptFunctionType.Getter: case ScriptFunctionType.Setter: prototype.AddMember(processor, new PrototypeMember(identifier, new SFunction(methodDelegate), attr.IsStatic, true, false, false)); break; case ScriptFunctionType.IndexerGet: prototype.IndexerGetFunction = new SFunction(methodDelegate); break; case ScriptFunctionType.IndexerSet: prototype.IndexerSetFunction = new SFunction(methodDelegate); break; case ScriptFunctionType.Constructor: prototype.Constructor = new PrototypeMember(Prototype.CLASS_METHOD_CTOR, new SFunction(methodDelegate), false, true, false, false); break; } } } prototype.MappedType = t; return prototype; }