/* Generate a runtime RType object for the type declaration. If the parent types are not already generated and are in this package, recurse to generate the parent type before finishing generating this one. */ func (g *Generator) generateTypeWithoutAttributes(typeName string, typeDeclaration *ast.TypeDecl, allTypeDecls map[string]*ast.TypeDecl, types map[*data.RType]bool, typesBeingGenerated map[string]bool, typeDeclFile map[string]string) { g.SetCodeFile(g.astFiles[typeDeclFile[typeName]]) _, found := typesBeingGenerated[typeName] if found { rterr.Stopf1(g, typeDeclaration, "Type '%s' declaration is involved in a type inheritance loop!", typeDeclaration.Spec.Name.Name) } typesBeingGenerated[typeName] = true typeSpec := typeDeclaration.Spec slashPos := strings.LastIndex(typeSpec.Name.Name, "/") typeLocalName := typeSpec.Name.Name[slashPos+1:] typeShortName := g.pkg.ShortName + "/" + typeLocalName var parentTypeNames []string for _, parentTypeSpec := range typeSpec.SuperTypes { parentTypeName := g.qualifyTypeName(parentTypeSpec.Name.Name) parentType, parentFound := data.RT.Types[parentTypeName] if parentFound { if parentType.IsPrivate && parentType.Package != g.pkg { rterr.Stopf1(g, typeDeclaration, "Error creating type %s: Supertype %s is private (visible only inside its package).", typeName, parentTypeName) } } else { parentTypeDecl, parentDeclaredInPackage := allTypeDecls[parentTypeName] if parentDeclaredInPackage { g.generateTypeWithoutAttributes(parentTypeName, parentTypeDecl, allTypeDecls, types, typesBeingGenerated, typeDeclFile) } // If the parent type was not declared in this package but is not already declared in some other package, then the // parent type is missing in action. // Ignore this problem for the moment here. Let the rt.CreateType method report this error. } parentTypeNames = append(parentTypeNames, parentTypeName) } theNewType, err := data.RT.CreateType(typeName, typeShortName, parentTypeNames) if err != nil { rterr.Stopf1(g, typeDeclaration, "Error creating type %s: %s", typeName, err.Error()) } if !theNewType.Less(data.CollectionType) { theNewType.IsStruct = true } theNewType.Package = g.pkg if strings.HasSuffix(typeDeclFile[typeName], "private") { theNewType.IsPrivate = true } types[theNewType] = true // record that this is one of the new RTypes we generated ! delete(typesBeingGenerated, typeName) // remove from the inheritance loop detection map }
/* Processes the TypeDecls from a set of ast.File objects (which have been created by the parser.) Generates the runtime environment's objects for datatypes and attributes, and also ensures that db tables exist for these. Runtime *data.RType's are placed into the argument hashtable once created here. Note. This function has to operate recursively, since a given supertype (parent type) might not yet exist as a generated RType object, but might be in this same package so needs to be generated now anyway. Note. This function does not create the attribute specification part of the runtime RType objects. Attribute generation has to wait til all of the RTtypes for the package are generated and put in the RT.types map, so attribute generation will be done as a separate pass. allTypeDecls := make(map[string]*ast.TypeDecl) */ func (g *Generator) generateTypesWithoutAttributes(allTypeDecls map[string]*ast.TypeDecl, types map[*data.RType]bool, typeDeclFile map[string]string) { typesBeingGenerated := make(map[string]bool) // full names of types in the middle of being generated. Use to avoid inheritance loops. // Collect all type declarations from all files into a map by full type name // for file, fileRoot := range g.files { g.SetCodeFile(file) for _, typeDeclaration := range file.TypeDecls { // egh Nov 12, 12 //typeName := g.packagePath + typeDeclaration.Spec.Name.Name typeName := typeDeclaration.Spec.Name.Name if allTypeDecls[typeName] != nil { slashPos := strings.LastIndex(typeName, "/") typeLocalName := typeName[slashPos+1:] rterr.Stopf1(g, typeDeclaration, "Type '%s' declaration is 2nd declaration of this type found in package.", typeLocalName) } allTypeDecls[typeName] = typeDeclaration typeDeclFile[typeName] = fileRoot } } // Generate the runtime RType objects, recursively. for typeName, typeDecl := range allTypeDecls { _, found := data.RT.Types[typeName] if found { continue // the recursion has already generated this type. } g.generateTypeWithoutAttributes(typeName, typeDecl, allTypeDecls, types, typesBeingGenerated, typeDeclFile) } }
/* Returns the fully qualified name of the type. If the type spec is of a collection type, then ensures that the composite type exists in the runtime, and if not, creates it. TODO Handle Maps TODO Handle nested collection types, like [] [] String TypeSpec struct { Doc *CommentGroup // associated documentation; or nil Name *Ident // type name (or type variable name) Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes Comment *CommentGroup // line comments; or nil Params []*TypeSpec // Type parameters (egh) SuperTypes []*TypeSpec // Only valid if this is a type variable (egh) CollectionSpec *CollectionTypeSpec // nil or a collection specification typ interface{} // The actual *RType - associated at generation time. } type CollectionTypeSpec struct { Kind token.Token LDelim token.Pos RDelim token.Pos IsSorting bool IsAscending bool OrderFunc string } */ func (g *Generator) ensureTypeName(typeSpec *ast.TypeSpec) string { typ, err := g.Interp.EnsureType(g.packagePath, typeSpec) if err != nil { // rterr.Stopf("Error in file %s: %s", fileNameRoot +".rel", err.Error()) rterr.Stop1(g, typeSpec, err) } if typ.IsPrivate && typ.Package != g.pkg { rterr.Stopf1(g, typeSpec, "Type %s is private to its package. Not visible.", typ.Name) } return typ.Name /* var typ *data.RType var err error baseTypeName := g.qualifyTypeName(typeSpec.Name.Name) baseType, baseTypeFound := data.RT.Types[baseTypeName] if ! baseTypeFound { rterr.Stopf("Error in file %s: Type %s not found.", fileNameRoot +".rel", baseTypeName) // panic(fmt.Sprintf("Error in file %s: Type %s not found.", fileNameRoot +".rel", baseTypeName)) } if typeSpec.CollectionSpec == nil { typ = baseType } else { switch typeSpec.CollectionSpec.Kind { case token.LIST: typ, err = data.RT.GetListType(baseType) if err != nil { rterr.Stopf("Error in file %s: %s", fileNameRoot +".rel", err.Error()) } case token.SET: typ, err = data.RT.GetSetType(baseType) if err != nil { rterr.Stopf("Error in file %s: %s", fileNameRoot +".rel", err.Error()) } case token.MAP: panic("I don't handle Map types yet.") } } return typ.Name */ }
/* Go through the (already loaded) packages that the newly generated package is dependent on, and update the new package's multimethod map to incorporate multimethods and methods from the dependency packages. Now does this all at once for all files in the current package being loaded. IMPORTANT NOTE: That means every file in the current package has access to method-implementations declared in packages which are not explicitly imported into that particular code file of the current package, but are imported into another file of the current package. Anyway, currently, the particular code file also has access (through dispatch) to method-implementations declared even in indirect dependency packages of any of the packages explicitly imported into the current package. */ func (g *Generator) updatePackageDependenciesAndMultiMethodMap() { for file := range g.files { g.SetCodeFile(file) imports := file.RelishImports // package specifications for _, importedPackageSpec := range imports { dependencyPackageName := importedPackageSpec.OriginAndArtifactName + "/pkg/" + importedPackageSpec.PackageName dependencyPackage, dependencyAlreadyProcessed := g.pkg.Dependencies[dependencyPackageName] if !dependencyAlreadyProcessed { var dependencyPackageExists bool dependencyPackage, dependencyPackageExists = data.RT.Packages[dependencyPackageName] if !dependencyPackageExists { rterr.Stopf1(g, importedPackageSpec, "Imported package %s is an incorrectly defined native extension package.", dependencyPackageName) } g.pkg.Dependencies[dependencyPackageName] = dependencyPackage g.updatePackageMultiMethodMap(dependencyPackage) } } } }
/* Processes the RelationDecls list of a ast.File object (which has been created by the parser.) Generates the runtime environment's objects for relations between datatypes, and also ensures that db tables exist for these. */ func (g *Generator) generateRelations(types map[*data.RType]bool, orderings map[string]*data.AttributeSpec) { for file := range g.files { g.SetCodeFile(file) for _, relationDeclaration := range file.RelationDecls { end1 := relationDeclaration.End1 end2 := relationDeclaration.End2 var minCard1 int32 = 1 var maxCard1 int32 = 1 attributeName1 := end1.Name.Name multiValuedAttribute1 := (end1.Arity != nil) if multiValuedAttribute1 { minCard1 = int32(end1.Arity.MinCard) maxCard1 = int32(end1.Arity.MaxCard) // -1 means N } var collectionType1 string var orderFuncOrAttrName1 string = "" var isAscending1 bool if end1.Type.CollectionSpec != nil { switch end1.Type.CollectionSpec.Kind { case token.SET: if end1.Type.CollectionSpec.IsSorting { collectionType1 = "sortedset" orderFuncOrAttrName1 = end1.Type.CollectionSpec.OrderFunc isAscending1 = end1.Type.CollectionSpec.IsAscending } else { collectionType1 = "set" } case token.LIST: if end1.Type.CollectionSpec.IsSorting { collectionType1 = "sortedlist" orderFuncOrAttrName1 = end1.Type.CollectionSpec.OrderFunc isAscending1 = end1.Type.CollectionSpec.IsAscending } else { collectionType1 = "list" } case token.MAP: if end1.Type.CollectionSpec.IsSorting { collectionType1 = "sortedmap" orderFuncOrAttrName1 = end1.Type.CollectionSpec.OrderFunc isAscending1 = end1.Type.CollectionSpec.IsAscending } else { collectionType1 = "map" } } } var minCard2 int32 = 1 var maxCard2 int32 = 1 attributeName2 := end2.Name.Name multiValuedAttribute2 := (end2.Arity != nil) if multiValuedAttribute2 { minCard2 = int32(end2.Arity.MinCard) maxCard2 = int32(end2.Arity.MaxCard) // -1 means N } var collectionType2 string var orderFuncOrAttrName2 string = "" var isAscending2 bool if end2.Type.CollectionSpec != nil { switch end2.Type.CollectionSpec.Kind { case token.SET: if end2.Type.CollectionSpec.IsSorting { collectionType2 = "sortedset" orderFuncOrAttrName2 = end2.Type.CollectionSpec.OrderFunc isAscending2 = end2.Type.CollectionSpec.IsAscending } else { collectionType2 = "set" } case token.LIST: if end2.Type.CollectionSpec.IsSorting { collectionType2 = "sortedlist" orderFuncOrAttrName2 = end2.Type.CollectionSpec.OrderFunc isAscending2 = end2.Type.CollectionSpec.IsAscending } else { collectionType2 = "list" } case token.MAP: if end2.Type.CollectionSpec.IsSorting { collectionType2 = "sortedmap" orderFuncOrAttrName2 = end2.Type.CollectionSpec.OrderFunc isAscending2 = end2.Type.CollectionSpec.IsAscending } else { collectionType2 = "map" } } } typeName1 := g.qualifyTypeName(end1.Type.Name.Name) typeName2 := g.qualifyTypeName(end2.Type.Name.Name) type1, type2, err := data.RT.CreateRelation(typeName1, attributeName1, minCard1, maxCard1, collectionType1, orderFuncOrAttrName1, isAscending1, typeName2, attributeName2, minCard2, maxCard2, collectionType2, orderFuncOrAttrName2, isAscending2, false, orderings, end1.PublicReadable, end1.PackageReadable, end1.SubtypeReadable, end1.PublicWriteable, end1.PackageWriteable, end1.SubtypeWriteable, end1.Reassignable, end1.CollectionMutable, end1.Mutable, end1.DeeplyMutable, end2.PublicReadable, end2.PackageReadable, end2.SubtypeReadable, end2.PublicWriteable, end2.PackageWriteable, end2.SubtypeWriteable, end2.Reassignable, end2.CollectionMutable, end2.Mutable, end2.DeeplyMutable) if err != nil { // panic(err) rterr.Stopf1(g, relationDeclaration, "Error creating relation %s %s -- %s %s: %s", typeName1, attributeName1, attributeName2, typeName2, err.Error()) } if type1.IsPrivate && g.pkg != type1.Package { rterr.Stopf1(g, relationDeclaration, "Error creating relation %s %s -- %s %s: Type %s is private and not visible in this package.", typeName1, attributeName1, attributeName2, typeName2, typeName1) } if type2.IsPrivate && g.pkg != type2.Package { rterr.Stopf1(g, relationDeclaration, "Error creating attribute %s.%s (%s): Type %s is private and not visible in this package.", typeName1, attributeName1, attributeName2, typeName2, typeName2) } types[type1] = true types[type2] = true } } }
func (g *Generator) generateMethods() { for file, fileNameRoot := range g.files { g.SetCodeFile(file) privateCode := g.isPrivateCodeFile(fileNameRoot) for _, methodDeclaration := range file.MethodDecls { methodName := methodDeclaration.Name.Name // main functions and init<TypeName> functions and web dialog handler functions are explicitly package name qualified. // // TODO OOPS Which package are we executing when looking for web handler methods????? // TODO TODO Do we really need to prepend package name to init... method names? // This implies we cannot add constructors of type package1.Type1 in package2 if (methodName == "main") || /* Now done at parser.go: 1877 (strings.HasPrefix(methodName,"init") && len(methodName) > 5 && 'A' <= methodName[4] && methodName[4] <= 'Z' && !BuiltinTypeName[methodName[4:]]) || */ (g.isWebDialogHandlerMethod(fileNameRoot)) { methodName = g.packagePath + methodName } var parameterNames []string var nilArgAllowed []bool var parameterTypes []string var wildcardKeywordsParameterName string var wildcardKeywordsParameterType string var variadicParameterName string var variadicParameterType string var returnValTypes []string var returnArgsAreNamed bool for _, inputArgDecl := range methodDeclaration.Type.Params { nilArgAllowed = append(nilArgAllowed, inputArgDecl.Type.NilAllowed) if inputArgDecl.IsVariadic { if inputArgDecl.Type.CollectionSpec.Kind == token.LIST { variadicParameterName = inputArgDecl.Name.Name variadicParameterType = g.ensureTypeName(inputArgDecl.Type) // variadicParameterType = g.qualifyTypeName(inputArgDecl.Type.Name.Name) // g.ensureTypeName(inputArgDecl.Type, fileNameRoot) ??? } else { // inputArgDecl.Type..CollectionSpec.Kind == token.MAP wildcardKeywordsParameterName = inputArgDecl.Name.Name wildcardKeywordsParameterType = g.ensureTypeName(inputArgDecl.Type) // wildcardKeywordsParameterType = g.qualifyTypeName(inputArgDecl.Type.Name.Name) // g.ensureTypeName(inputArgDecl.Type, fileNameRoot) } } else { parameterNames = append(parameterNames, inputArgDecl.Name.Name) parameterTypes = append(parameterTypes, g.ensureTypeName(inputArgDecl.Type)) } } for _, returnArgDecl := range methodDeclaration.Type.Results { if returnArgDecl.Name != nil { returnArgsAreNamed = true } returnValTypes = append(returnValTypes, g.ensureTypeName(returnArgDecl.Type)) } var rMethod *data.RMethod var err error if methodDeclaration.IsClosureMethod { rMethod, err = data.RT.CreateClosureMethod(g.packageName, file, methodName, parameterNames, nilArgAllowed, parameterTypes, returnValTypes, returnArgsAreNamed, methodDeclaration.NumLocalVars, methodDeclaration.NumFreeVars) } else { allowRedefinition := false isTraitAbstractMethod := methodDeclaration.Body == nil && g.pkg.IsTrait // FuncType. Params []*InputArgDecl // input parameter declarations. Can be empty list. // Results []*ReturnArgDecl // (outgoing) result declarations; Can be empty list. rMethod, err = data.RT.CreateMethodV(g.packageName, file, methodName, parameterNames, nilArgAllowed, parameterTypes, wildcardKeywordsParameterName, wildcardKeywordsParameterType, variadicParameterName, variadicParameterType, returnValTypes, returnArgsAreNamed, methodDeclaration.NumLocalVars, isTraitAbstractMethod, allowRedefinition, !privateCode) } if err != nil { // panic(err) rterr.Stopf1(g, methodDeclaration, "Error creating method %s: %s", methodName, err.Error()) } rMethod.Code = methodDeclaration // abstract syntax tree Logln(GENERATE__, rMethod) } } }
/* Processes the TypeDecls list of a ast.File object (which has been created by the parser.) Generates the runtime environment's objects for attributes of datatypes. Assumes the RType objects have already been created in the runtime for each datatype by a previous pass over the intermediate-code files. */ func (g *Generator) generateAttributes(allTypeDecls map[string]*ast.TypeDecl, types map[*data.RType]bool, typeDeclFile map[string]string, orderings map[string]*data.AttributeSpec) { for theNewType := range types { typeName := theNewType.Name typeDeclaration := allTypeDecls[typeName] g.SetCodeFile(g.astFiles[typeDeclFile[typeName]]) for _, attrDecl := range typeDeclaration.Attributes { var minCard int32 = 1 var maxCard int32 = 1 attributeName := attrDecl.Name.Name multiValuedAttribute := (attrDecl.Arity != nil) if multiValuedAttribute { minCard = int32(attrDecl.Arity.MinCard) maxCard = int32(attrDecl.Arity.MaxCard) // -1 means N } else if attrDecl.Type.NilAllowed { minCard = 0 } var collectionType string var orderFuncOrAttrName string = "" var isAscending bool if multiValuedAttribute { // && attrDecl.Type.CollectionSpec != nil { switch attrDecl.Type.CollectionSpec.Kind { case token.SET: if attrDecl.Type.CollectionSpec.IsSorting { collectionType = "sortedset" orderFuncOrAttrName = attrDecl.Type.CollectionSpec.OrderFunc isAscending = attrDecl.Type.CollectionSpec.IsAscending } else { collectionType = "set" } case token.LIST: if attrDecl.Type.CollectionSpec.IsSorting { collectionType = "sortedlist" orderFuncOrAttrName = attrDecl.Type.CollectionSpec.OrderFunc isAscending = attrDecl.Type.CollectionSpec.IsAscending } else { collectionType = "list" } case token.MAP: if attrDecl.Type.CollectionSpec.IsSorting { collectionType = "sortedmap" orderFuncOrAttrName = attrDecl.Type.CollectionSpec.OrderFunc isAscending = attrDecl.Type.CollectionSpec.IsAscending } else { collectionType = "map" } } } /* type CollectionTypeSpec struct { Kind token.Token LDelim token.Pos RDelim token.Pos IsSorting bool IsAscending bool OrderFunc string } */ // If the attribute is a non-owned collection, we should use g.ensureTypeName(...) here. // But if it is a multivalued attribute (owned collection with cardinality specified), // we should check if there is a Type.Name. If so, use qualifyTypeName on it. // If not, use the Name of Type.params[0] and use qualifyTypeName on that. // If there is no Name on the params[0], that is the error of using a non-simple type at end of a multi-valued // attribute and should be reported as such. var attributeTypeName string if multiValuedAttribute { if attrDecl.Type.Name == nil { if attrDecl.Type.Params[0].Name == nil { rterr.Stopf1(g, attrDecl, "Error creating attribute %s.%s: %s", typeName, attributeName, "A Multi-valued attribute (with arity specification) must have a simple type, not a collection of collections.") } else { attributeTypeName = g.qualifyTypeName(attrDecl.Type.Params[0].Name.Name) } } else { attributeTypeName = g.qualifyTypeName(attrDecl.Type.Name.Name) } } else { attributeTypeName = g.ensureTypeName(attrDecl.Type) } /*"vector" RelEnd ... CollectionType string // "list", "sortedlist","set", "sortedset", "map", "stringmap", "sortedmap","sortedstringmap","" OrderAttrName string // which primitive attribute of other is it ordered by when retrieving? "" if none OrderMethod *RMultiMethod */ attr, err := data.RT.CreateAttribute(typeName, attributeTypeName, attributeName, minCard, maxCard, // Is the -1 meaning N respected in here???? TODO collectionType, orderFuncOrAttrName, isAscending, false, false, false, orderings, attrDecl.PublicReadable, attrDecl.PackageReadable, attrDecl.SubtypeReadable, attrDecl.PublicWriteable, attrDecl.PackageWriteable, attrDecl.SubtypeWriteable, attrDecl.Reassignable, attrDecl.CollectionMutable, attrDecl.Mutable, attrDecl.DeeplyMutable) if err != nil { rterr.Stopf1(g, attrDecl, "Error creating attribute %s.%s (%s): %s", typeName, attributeName, err.Error()) } if attr.Part.Type.IsPrivate && g.pkg != attr.Part.Type.Package { rterr.Stopf1(g, attrDecl, "Error creating attribute %s.%s (%s): Type %s is private and not visible in this package.", typeName, attributeName, attributeTypeName) } } } }