int asCBuilder::ParseVariableDeclaration(const char *decl, asCProperty *var) { numErrors = 0; numWarnings = 0; preMessage.isSet = false; asCScriptCode source; source.SetCode(TXT_VARIABLE_DECL, decl, true); asCParser parser(this); int r = parser.ParsePropertyDeclaration(&source); if( r < 0 ) return asINVALID_DECLARATION; asCScriptNode *node = parser.GetScriptNode(); // Find name asCScriptNode *n = node->firstChild->next; var->name.Assign(&source.code[n->tokenPos], n->tokenLength); // Initialize a script variable object for registration var->type = CreateDataTypeFromNode(node->firstChild, &source); if( numErrors > 0 || numWarnings > 0 ) return asINVALID_DECLARATION; return 0; }
int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &name, asCDataType &type) { numErrors = 0; numWarnings = 0; preMessage.isSet = false; if( dt ) { // Verify that the object type exist if( dt->GetObjectType() == 0 ) return asINVALID_OBJECT; } // Check property declaration and type asCScriptCode source; source.SetCode(TXT_PROPERTY, decl, true); asCParser parser(this); int r = parser.ParsePropertyDeclaration(&source); if( r < 0 ) return asINVALID_DECLARATION; // Get data type and property name asCScriptNode *dataType = parser.GetScriptNode()->firstChild; asCScriptNode *nameNode = dataType->next; type = CreateDataTypeFromNode(dataType, &source); name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); // Verify property name if( dt ) if( CheckNameConflictMember(*dt, name.AddressOf(), nameNode, &source) < 0 ) return asINVALID_NAME; else if( CheckNameConflict(name.AddressOf(), nameNode, &source) < 0 ) return asINVALID_NAME; if( numErrors > 0 ) return asINVALID_DECLARATION; return asSUCCESS; }
int asCBuilder::ParseDataType(const char *datatype, asCDataType *result) { numErrors = 0; numWarnings = 0; asCScriptCode source; source.SetCode("", datatype, true); asCParser parser(this); int r = parser.ParseDataType(&source); if( r < 0 ) return asINVALID_TYPE; // Get data type and property name asCScriptNode *dataType = parser.GetScriptNode()->firstChild; *result = CreateDataTypeFromNode(dataType, &source); if( numErrors > 0 ) return asINVALID_TYPE; return asSUCCESS; }
void asCBuilder::CompileClasses() { asUINT n; asCArray<sClassDeclaration*> toValidate; // Go through each of the classes and register the object type descriptions for( n = 0; n < classDeclarations.GetLength(); n++ ) { sClassDeclaration *decl = classDeclarations[n]; // Enumerate each of the declared properties asCScriptNode *node = decl->node->firstChild->next; // Skip list of classes and interfaces while( node && node->nodeType == snIdentifier ) node = node->next; while( node ) { if( node->nodeType == snDeclaration ) { asCScriptCode *file = decl->script; asCDataType dt = CreateDataTypeFromNode(node->firstChild, file); GETSTRING(name, &file->code[node->lastChild->tokenPos], node->lastChild->tokenLength); if( dt.IsReadOnly() ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteError(file->name.AddressOf(), TXT_PROPERTY_CANT_BE_CONST, r, c); } asCDataType st; st.SetObjectType(decl->objType); CheckNameConflictMember(st, name.AddressOf(), node->lastChild, file); // Store the properties in the object type descriptor asCProperty *prop = new asCProperty; prop->name = name; prop->type = dt; int propSize; if( dt.IsObject() ) { propSize = dt.GetSizeOnStackDWords()*4; if( !dt.IsObjectHandle() ) { if( dt.GetSizeInMemoryBytes() == 0 ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); asCString str; str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); WriteError(file->name.AddressOf(), str.AddressOf(), r, c); } prop->type.MakeReference(true); } } else { propSize = dt.GetSizeInMemoryBytes(); if( propSize == 0 ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); asCString str; str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format()); WriteError(file->name.AddressOf(), str.AddressOf(), r, c); } } // Add extra bytes so that the property will be properly aligned if( propSize == 2 && (decl->objType->size & 1) ) decl->objType->size += 1; if( propSize > 2 && (decl->objType->size & 3) ) decl->objType->size += 3 - (decl->objType->size & 3); prop->byteOffset = decl->objType->size; decl->objType->size += propSize; decl->objType->properties.PushLast(prop); // Make sure the module holds a reference to the config group where the object is registered module->RefConfigGroupForObjectType(dt.GetObjectType()); } else if( node->nodeType == snFunction ) { // TODO: Register the method and add it to the list of functions to compile later } else assert(false); node = node->next; } toValidate.PushLast(decl); } // Verify that the declared structures are valid, e.g. that the structure // doesn't contain a member of its own type directly or indirectly while( toValidate.GetLength() > 0 ) { asUINT numClasses = (asUINT)toValidate.GetLength(); asCArray<sClassDeclaration*> toValidateNext; while( toValidate.GetLength() > 0 ) { sClassDeclaration *decl = toValidate[toValidate.GetLength()-1]; int validState = 1; for( asUINT n = 0; n < decl->objType->properties.GetLength(); n++ ) { // A valid structure is one that uses only primitives or other valid objects asCProperty *prop = decl->objType->properties[n]; asCDataType dt = prop->type; if( dt.IsScriptArray() ) { asCDataType sub = dt; while( sub.IsScriptArray() && !sub.IsObjectHandle() ) sub = sub.GetSubType(); dt = sub; } if( dt.IsObject() && !dt.IsObjectHandle() ) { // Find the class declaration sClassDeclaration *pdecl = 0; for( asUINT p = 0; p < classDeclarations.GetLength(); p++ ) { if( classDeclarations[p]->objType == dt.GetObjectType() ) { pdecl = classDeclarations[p]; break; } } if( pdecl ) { if( pdecl->objType == decl->objType ) { int r, c; decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c); WriteError(decl->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c); validState = 2; break; } else if( pdecl->validState != 1 ) { validState = pdecl->validState; break; } } } } if( validState == 1 ) { decl->validState = 1; toValidate.PopLast(); } else if( validState == 2 ) { decl->validState = 2; toValidate.PopLast(); } else { toValidateNext.PushLast(toValidate.PopLast()); } } toValidate = toValidateNext; toValidateNext.SetLength(0); if( numClasses == toValidate.GetLength() ) { int r, c; toValidate[0]->script->ConvertPosToRowCol(toValidate[0]->node->tokenPos, &r, &c); WriteError(toValidate[0]->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c); break; } } if( numErrors > 0 ) return; // TODO: The declarations form a graph, all circles in // the graph must be flagged as potential circles // Verify potential circular references for( n = 0; n < classDeclarations.GetLength(); n++ ) { sClassDeclaration *decl = classDeclarations[n]; asCObjectType *ot = decl->objType; // Is there some path in which this structure is involved in circular references? for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) { asCDataType dt = ot->properties[p]->type; if( dt.IsObject() ) { // Any structure that contains an any type can generate circular references if( dt.GetObjectType()->flags & asOBJ_CONTAINS_ANY ) { ot->flags |= asOBJ_POTENTIAL_CIRCLE | asOBJ_CONTAINS_ANY; } if( dt.IsObjectHandle() ) { // TODO: // Can this handle really generate a circular reference ot->flags |= asOBJ_POTENTIAL_CIRCLE; } else if( dt.GetObjectType()->flags & asOBJ_POTENTIAL_CIRCLE ) { // TODO: // Just because the member type is a potential circle doesn't mean that this one is ot->flags |= asOBJ_POTENTIAL_CIRCLE; } if( dt.IsArrayType() ) { asCDataType sub = dt.GetSubType(); while( sub.IsObject() ) { if( sub.IsObjectHandle() || (sub.GetObjectType()->flags & asOBJ_POTENTIAL_CIRCLE) ) { decl->objType->flags |= asOBJ_POTENTIAL_CIRCLE; // Make sure the array object is also marked as potential circle sub = dt; while( sub.IsScriptArray() ) { sub.GetObjectType()->flags |= asOBJ_POTENTIAL_CIRCLE; sub = sub.GetSubType(); } break; } if( sub.IsScriptArray() ) sub = sub.GetSubType(); else break; } } } } } // Verify that the class implements all the methods from the interfaces it implements for( n = 0; n < classDeclarations.GetLength(); n++ ) { sClassDeclaration *decl = classDeclarations[n]; asCScriptCode *file = decl->script; // Enumerate each of the implemented interfaces asCScriptNode *node = decl->node->firstChild->next; while( node && node->nodeType == snIdentifier ) { // Get the interface name from the node GETSTRING(name, &file->code[node->tokenPos], node->tokenLength); // Find the object type for the interface asCObjectType *objType = GetObjectType(name.AddressOf()); if( decl->objType->Implements(objType) ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteWarning(file->name.AddressOf(), TXT_INTERFACE_ALREADY_IMPLEMENTED, r, c); } else { decl->objType->interfaces.PushLast(objType); // Make sure all the methods of the interface are implemented for( asUINT i = 0; i < objType->methods.GetLength(); i++ ) { if( !DoesMethodExist(decl->objType, objType->methods[i]) ) { int r, c; file->ConvertPosToRowCol(decl->node->tokenPos, &r, &c); asCString str; str.Format(TXT_MISSING_IMPLEMENTATION_OF_s, engine->GetFunctionDeclaration(objType->methods[i]).AddressOf()); WriteError(file->name.AddressOf(), str.AddressOf(), r, c); } } } node = node->next; } } }
int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file) { // What data type is it? asCDataType type = CreateDataTypeFromNode(node->firstChild, file); if( type.GetSizeOnStackDWords() == 0 || (type.IsObject() && !type.IsObjectHandle() && type.GetSizeInMemoryBytes() == 0) ) { asCString str; // TODO: Change to "'type' cannot be declared as variable" str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf()); int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteError(file->name.AddressOf(), str.AddressOf(), r, c); } asCScriptNode *n = node->firstChild->next; while( n ) { // Verify that the name isn't taken GETSTRING(name, &file->code[n->tokenPos], n->tokenLength); CheckNameConflict(name.AddressOf(), n, file); // Register the global variable sGlobalVariableDescription *gvar = new sGlobalVariableDescription; globVariables.PushLast(gvar); gvar->script = file; gvar->name = name; gvar->isCompiled = false; gvar->datatype = type; // TODO: Give error message if wrong assert(!gvar->datatype.IsReference()); // Allocate space on the global memory stack gvar->index = module->AllocGlobalMemory(gvar->datatype.GetSizeOnStackDWords()); gvar->node = 0; if( n->next && (n->next->nodeType == snAssignment || n->next->nodeType == snArgList || n->next->nodeType == snInitList ) ) { gvar->node = n->next; n->next->DisconnectParent(); } // Add script variable to engine asCProperty *prop = new asCProperty; prop->index = gvar->index; prop->name = name; prop->type = gvar->datatype; module->scriptGlobals.PushLast(prop); gvar->property = prop; n = n->next; } delete node; return 0; }
int asCBuilder::ParseFunctionDeclaration(const char *decl, asCScriptFunction *func, asCArray<bool> *paramAutoHandles, bool *returnAutoHandle) { numErrors = 0; numWarnings = 0; preMessage.isSet = false; asCScriptCode source; source.SetCode(TXT_SYSTEM_FUNCTION, decl, true); asCParser parser(this); int r = parser.ParseFunctionDefinition(&source); if( r < 0 ) return asINVALID_DECLARATION; asCScriptNode *node = parser.GetScriptNode(); // Find name asCScriptNode *n = node->firstChild->next->next; func->name.Assign(&source.code[n->tokenPos], n->tokenLength); // Initialize a script function object for registration bool autoHandle; func->returnType = CreateDataTypeFromNode(node->firstChild, &source); func->returnType = ModifyDataTypeFromNode(func->returnType, node->firstChild->next, &source, 0, &autoHandle); if( autoHandle && (!func->returnType.IsObjectHandle() || func->returnType.IsReference()) ) return asINVALID_DECLARATION; if( returnAutoHandle ) *returnAutoHandle = autoHandle; n = n->next->firstChild; while( n ) { int inOutFlags; asCDataType type = CreateDataTypeFromNode(n, &source); type = ModifyDataTypeFromNode(type, n->next, &source, &inOutFlags, &autoHandle); // Store the parameter type func->parameterTypes.PushLast(type); func->inOutFlags.PushLast(inOutFlags); if( autoHandle && (!type.IsObjectHandle() || type.IsReference()) ) return asINVALID_DECLARATION; if( paramAutoHandles ) paramAutoHandles->PushLast(autoHandle); // Move to next parameter n = n->next->next; if( n && n->nodeType == snIdentifier ) n = n->next; } // Set the read-only flag if const is declared after parameter list if( node->lastChild->nodeType == snUndefined && node->lastChild->tokenType == ttConst ) func->isReadOnly = true; else func->isReadOnly = false; if( numErrors > 0 || numWarnings > 0 ) return asINVALID_DECLARATION; return 0; }
int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCScriptCode *file) { // Find name asCScriptNode *f = node->firstChild; asCScriptNode *n = f->firstChild->next->next; // Check for name conflicts GETSTRING(name, &file->code[n->tokenPos], n->tokenLength); CheckNameConflict(name.AddressOf(), n, file); // Initialize a script function object for registration asCDataType returnType; returnType = CreateDataTypeFromNode(f->firstChild, file); returnType = ModifyDataTypeFromNode(returnType, f->firstChild->next, file, 0, 0); asCArray<asCDataType> parameterTypes; asCArray<int> inOutFlags; n = n->next->firstChild; while( n ) { int inOutFlag; asCDataType type = CreateDataTypeFromNode(n, file); type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0); // Store the parameter type n = n->next->next; parameterTypes.PushLast(type); inOutFlags.PushLast(inOutFlag); // Move to next parameter if( n && n->nodeType == snIdentifier ) n = n->next; } // Check that the same function hasn't been registered already asCArray<int> funcs; GetFunctionDescriptions(name.AddressOf(), funcs); if( funcs.GetLength() ) { for( asUINT n = 0; n < funcs.GetLength(); ++n ) { asCScriptFunction *func = GetFunctionDescription(funcs[n]); // TODO: Isn't the name guaranteed to be equal, because of GetFunctionDescriptions()? if( name == func->name && parameterTypes.GetLength() == func->parameterTypes.GetLength() ) { bool match = true; for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) { if( parameterTypes[p] != func->parameterTypes[p] ) { match = false; break; } } if( match ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c); break; } } } } // Read the module name as well n = node->firstChild->next; int moduleNameString = module->AddConstantString(&file->code[n->tokenPos+1], n->tokenLength-2); delete node; // Register the function module->AddImportedFunction(importID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), moduleNameString); return 0; }
int asCBuilder::RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface) { // Find name bool isConstructor = false; asCScriptNode *n = 0; if( node->firstChild->nodeType == snDataType ) n = node->firstChild->next->next; else { n = node->firstChild; isConstructor = true; } // Check for name conflicts GETSTRING(name, &file->code[n->tokenPos], n->tokenLength); if( !isConstructor ) CheckNameConflict(name.AddressOf(), n, file); else { // Verify that the name of the function is the same as the class if( name != objType->name ) { int r, c; file->ConvertPosToRowCol(n->tokenPos, &r, &c); WriteError(file->name.AddressOf(), TXT_CONSTRUCTOR_NAME_ERROR, r, c); } } if( !isInterface ) { sFunctionDescription *func = new sFunctionDescription; functions.PushLast(func); func->script = file; func->node = node; func->name = name; func->objType = objType; func->funcId = funcID; } // Initialize a script function object for registration asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false); if( !isConstructor ) { returnType = CreateDataTypeFromNode(node->firstChild, file); returnType = ModifyDataTypeFromNode(returnType, node->firstChild->next, file, 0, 0); module->RefConfigGroupForObjectType(returnType.GetObjectType()); } asCArray<asCDataType> parameterTypes; asCArray<int> inOutFlags; n = n->next->firstChild; while( n ) { int inOutFlag; asCDataType type = CreateDataTypeFromNode(n, file); type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0); module->RefConfigGroupForObjectType(type.GetObjectType()); // Store the parameter type parameterTypes.PushLast(type); inOutFlags.PushLast(inOutFlag); // Move to next parameter n = n->next->next; if( n && n->nodeType == snIdentifier ) n = n->next; } // Check that the same function hasn't been registered already asCArray<int> funcs; GetFunctionDescriptions(name.AddressOf(), funcs); if( funcs.GetLength() ) { for( asUINT n = 0; n < funcs.GetLength(); ++n ) { asCScriptFunction *func = GetFunctionDescription(funcs[n]); if( parameterTypes.GetLength() == func->parameterTypes.GetLength() ) { bool match = true; for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) { if( parameterTypes[p] != func->parameterTypes[p] ) { match = false; break; } } if( match ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c); break; } } } } // Register the function module->AddScriptFunction(file->idx, funcID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), isInterface, objType); if( objType ) { if( isConstructor ) { if( parameterTypes.GetLength() == 0 ) { // Overload the default constructor objType->beh.construct = funcID; objType->beh.constructors[0] = funcID; } else objType->beh.constructors.PushLast(funcID); } else objType->methods.PushLast(funcID); } // We need to delete the node already if this is an interface method if( isInterface && node ) delete node; return 0; }
int asCBuilder::RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file) { // Find name asCScriptNode *n = node->firstChild->next->next; // Check for name conflicts GETSTRING(name, &file->code[n->tokenPos], n->tokenLength); CheckNameConflict(name.AddressOf(), n, file); sFunctionDescription *func = new sFunctionDescription; functions.PushLast(func); func->script = file; func->node = node; func->name = name; // Initialize a script function object for registration asCDataType returnType; returnType = CreateDataTypeFromNode(node->firstChild, file); returnType = ModifyDataTypeFromNode(returnType, node->firstChild->next, 0, 0); module->RefConfigGroupForObjectType(returnType.GetObjectType()); asCArray<asCDataType> parameterTypes; asCArray<int> inOutFlags; n = n->next->firstChild; while( n ) { int inOutFlag; asCDataType type = CreateDataTypeFromNode(n, file); type = ModifyDataTypeFromNode(type, n->next, &inOutFlag, 0); module->RefConfigGroupForObjectType(type.GetObjectType()); // Store the parameter type parameterTypes.PushLast(type); inOutFlags.PushLast(inOutFlag); // Move to next parameter n = n->next->next; if( n && n->nodeType == snIdentifier ) n = n->next; } // Check that the same function hasn't been registered already asCArray<int> funcs; GetFunctionDescriptions(name.AddressOf(), funcs); if( funcs.GetLength() ) { for( asUINT n = 0; n < funcs.GetLength(); ++n ) { asCScriptFunction *func = GetFunctionDescription(funcs[n]); if( parameterTypes.GetLength() == func->parameterTypes.GetLength() ) { bool match = true; for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) { if( parameterTypes[p] != func->parameterTypes[p] ) { match = false; break; } } if( match ) { int r, c; file->ConvertPosToRowCol(node->tokenPos, &r, &c); WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c); break; } } } } // Register the function module->AddScriptFunction(file->idx, funcID, func->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), parameterTypes.GetLength()); return 0; }