github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/parser/utils.go (about) 1 /* 2 * Copyright (c) 2023-present unTill Pro, Ltd. 3 * @author Michael Saigachenko 4 */ 5 6 package parser 7 8 import ( 9 "fmt" 10 "reflect" 11 "strings" 12 13 "github.com/voedger/voedger/pkg/appdef" 14 ) 15 16 func extractStatement(s any) interface{} { 17 v := reflect.ValueOf(s) 18 for i := 0; i < v.NumField(); i++ { 19 field := v.Field(i) 20 if field.Kind() == reflect.Ptr && !field.IsNil() { 21 return field.Interface() 22 } 23 } 24 panic("undefined statement") 25 } 26 27 func CompareParam(left, right FunctionParam) bool { 28 var lt, rt DataTypeOrDef 29 if left.NamedParam != nil { 30 lt = left.NamedParam.Type 31 } else { 32 lt = *left.UnnamedParamType 33 } 34 if right.NamedParam != nil { 35 rt = right.NamedParam.Type 36 } else { 37 rt = *right.UnnamedParamType 38 } 39 return lt == rt 40 } 41 42 func CompareParams(params []FunctionParam, f *FunctionStmt) error { 43 if len(params) != len(f.Params) { 44 return ErrFunctionParamsIncorrect 45 } 46 for i := 0; i < len(params); i++ { 47 if !CompareParam(params[i], f.Params[i]) { 48 return ErrFunctionParamsIncorrect 49 } 50 } 51 return nil 52 } 53 54 func iterate(c IStatementCollection, callback func(stmt interface{})) { 55 c.Iterate(func(stmt interface{}) { 56 callback(stmt) 57 if collection, ok := stmt.(IStatementCollection); ok { 58 iterate(collection, callback) 59 } 60 }) 61 } 62 63 func resolveInCtx[stmtType *TableStmt | *TypeStmt | *FunctionStmt | *CommandStmt | *ProjectorStmt | 64 *RateStmt | *TagStmt | *WorkspaceStmt | *StorageStmt | *ViewStmt | *LimitStmt | *QueryStmt | *RoleStmt | *DeclareStmt](fn DefQName, ictx *iterateCtx, cb func(f stmtType, schema *PackageSchemaAST) error) error { 65 var err error 66 var item stmtType 67 var p *PackageSchemaAST 68 item, p, err = lookupInCtx[stmtType](fn, ictx) 69 if err != nil { 70 return err 71 } 72 73 if item == nil { 74 var value interface{} = item 75 switch value.(type) { 76 case *TableStmt: 77 return ErrUndefinedTable(fn) 78 case *CommandStmt: 79 return ErrUndefinedCommand(fn) 80 case *QueryStmt: 81 return ErrUndefinedQuery(fn) 82 case *TagStmt: 83 return ErrUndefinedTag(fn) 84 case *RoleStmt: 85 return ErrUndefinedRole(fn) 86 case *TypeStmt: 87 return ErrUndefinedType(fn) 88 case *WorkspaceStmt: 89 return ErrUndefinedWorkspace(fn) 90 case *RateStmt: 91 return ErrUndefinedRate(fn) 92 default: 93 return ErrUndefined(fn.String()) 94 } 95 } 96 return cb(item, p) 97 } 98 99 func lookupInSysPackage[stmtType *WorkspaceStmt](ctx *basicContext, fn DefQName) (stmtType, error) { 100 sysSchema := ctx.app.Packages[appdef.SysPackage] 101 if sysSchema == nil { 102 return nil, ErrCouldNotImport(appdef.SysPackage) 103 } 104 ictx := &iterateCtx{ 105 basicContext: ctx, 106 collection: sysSchema.Ast, 107 pkg: sysSchema, 108 parent: nil, 109 } 110 s, _, e := lookupInCtx[stmtType](fn, ictx) 111 return s, e 112 } 113 114 func getCurrentAlterWorkspace(ictx *iterateCtx) *AlterWorkspaceStmt { 115 var ic *iterateCtx = ictx 116 for ic != nil { 117 if aw, isAlterWorkspace := ic.collection.(*AlterWorkspaceStmt); isAlterWorkspace { 118 return aw 119 } 120 ic = ic.parent 121 } 122 return nil 123 } 124 125 func getCurrentWorkspace(ictx *iterateCtx) *WorkspaceStmt { 126 var ic *iterateCtx = ictx 127 var ws *WorkspaceStmt = nil 128 for ic != nil { 129 if _, isWorkspace := ic.collection.(*WorkspaceStmt); isWorkspace { 130 ws = ic.collection.(*WorkspaceStmt) 131 break 132 } 133 ic = ic.parent 134 } 135 return ws 136 } 137 138 func lookupInCtx[stmtType *TableStmt | *TypeStmt | *FunctionStmt | *CommandStmt | *RateStmt | *TagStmt | *ProjectorStmt | 139 *WorkspaceStmt | *ViewStmt | *StorageStmt | *LimitStmt | *QueryStmt | *RoleStmt | *WsDescriptorStmt | *DeclareStmt](fn DefQName, ictx *iterateCtx) (stmtType, *PackageSchemaAST, error) { 140 schema, err := getTargetSchema(fn, ictx) 141 if err != nil { 142 return nil, nil, err 143 } 144 145 var item stmtType 146 var lookupCallback func(stmt interface{}) 147 lookupCallback = func(stmt interface{}) { 148 if f, ok := stmt.(stmtType); ok && item == nil { 149 named := any(f).(INamedStatement) 150 if named.GetName() == string(fn.Name) { 151 item = f 152 } 153 } 154 if collection, ok := stmt.(IStatementCollection); ok && item == nil { 155 if _, isWorkspace := stmt.(*WorkspaceStmt); !isWorkspace { // do not go into workspaces 156 collection.Iterate(lookupCallback) 157 } 158 } 159 if t, ok := stmt.(*TableStmt); ok && item == nil { 160 for i := range t.Items { 161 if t.Items[i].NestedTable != nil { 162 lookupCallback(&t.Items[i].NestedTable.Table) 163 } 164 } 165 } 166 } 167 168 if schema == ictx.pkg { 169 ws := getCurrentWorkspace(ictx) 170 // First look in the current workspace 171 if ws != nil { 172 ws.Iterate(lookupCallback) 173 if item == nil { 174 var value interface{} = item 175 if _, ok := value.(*WorkspaceStmt); !ok { // when looking for something else than a workspace, look in the inherited workspaces 176 for _, dq := range ws.Inherits { 177 err := resolveInCtx[*WorkspaceStmt](dq, ictx, func(f *WorkspaceStmt, schema *PackageSchemaAST) error { 178 f.Iterate(lookupCallback) 179 return nil 180 }) 181 if err != nil { 182 return nil, nil, err 183 } 184 } 185 } 186 } 187 } 188 189 // Look in the package 190 if item == nil { 191 schema.Ast.Iterate(lookupCallback) 192 } 193 194 // Look in the sys package 195 if item == nil && maybeSysPkg(fn.Package) { // Look in sys pkg 196 schema = ictx.app.Packages[appdef.SysPackage] 197 if schema == nil { 198 return nil, nil, ErrCouldNotImport(appdef.SysPackage) 199 } 200 iterPkg := func(coll IStatementCollection) { 201 coll.Iterate(lookupCallback) 202 } 203 iterPkg(schema.Ast) 204 } 205 return item, schema, nil 206 } 207 208 schema.Ast.Iterate(lookupCallback) 209 return item, schema, nil 210 } 211 212 func iteratePackage(pkg *PackageSchemaAST, ctx *basicContext, callback func(stmt interface{}, ctx *iterateCtx)) { 213 ictx := &iterateCtx{ 214 basicContext: ctx, 215 collection: pkg.Ast, 216 pkg: pkg, 217 parent: nil, 218 } 219 iterateContext(ictx, callback) 220 } 221 222 func iteratePackageStmt[stmtType *TableStmt | *TypeStmt | *ViewStmt | *CommandStmt | *QueryStmt | 223 *WorkspaceStmt | *AlterWorkspaceStmt | *ProjectorStmt | *RateStmt](pkg *PackageSchemaAST, ctx *basicContext, callback func(stmt stmtType, ctx *iterateCtx)) { 224 iteratePackage(pkg, ctx, func(stmt interface{}, ctx *iterateCtx) { 225 if s, ok := stmt.(stmtType); ok { 226 callback(s, ctx) 227 } 228 }) 229 } 230 231 func iterateContext(ictx *iterateCtx, callback func(stmt interface{}, ctx *iterateCtx)) { 232 ictx.collection.Iterate(func(stmt interface{}) { 233 callback(stmt, ictx) 234 if collection, ok := stmt.(IStatementCollection); ok { 235 iNestedCtx := &iterateCtx{ 236 basicContext: ictx.basicContext, 237 collection: collection, 238 pkg: ictx.pkg, 239 parent: ictx, 240 wsCtxs: ictx.wsCtxs, 241 } 242 iterateContext(iNestedCtx, callback) 243 } 244 }) 245 } 246 247 func isInternalName(pkgName Ident, pkgAst *PackageSchemaAST) bool { 248 pkg := strings.TrimSpace(string(pkgName)) 249 return pkg == "" || pkg == pkgAst.Name 250 } 251 252 func getPackageName(pkgQN string) string { 253 parts := strings.Split(pkgQN, "/") 254 if len(parts) == 0 { 255 return "" 256 } 257 return parts[len(parts)-1] 258 } 259 260 func GetQualifiedPackageName(pkgName Ident, schema *SchemaAST) string { 261 for i := 0; i < len(schema.Imports); i++ { 262 imp := schema.Imports[i] 263 if imp.Alias != nil && *imp.Alias == pkgName { 264 return imp.Name 265 } 266 } 267 suffix := fmt.Sprintf("/%s", pkgName) 268 for i := 0; i < len(schema.Imports); i++ { 269 imp := schema.Imports[i] 270 if strings.HasSuffix(imp.Name, suffix) || imp.Name == string(pkgName) { 271 return imp.Name 272 } 273 } 274 return "" 275 } 276 277 func findPackage(pnkName Ident, c *iterateCtx) (*PackageSchemaAST, error) { 278 var targetPkgSch *PackageSchemaAST 279 if isInternalName(pnkName, c.pkg) { 280 return c.pkg, nil 281 } 282 283 if pnkName == appdef.SysPackage { 284 sysSchema := c.app.Packages[appdef.SysPackage] 285 if sysSchema == nil { 286 return nil, ErrCouldNotImport(appdef.SysPackage) 287 } 288 return sysSchema, nil 289 } 290 291 pkgQN := GetQualifiedPackageName(pnkName, c.pkg.Ast) 292 if pkgQN == "" { 293 return nil, ErrUndefined(string(pnkName)) 294 } 295 targetPkgSch = c.app.Packages[pkgQN] 296 if targetPkgSch == nil { 297 return nil, ErrCouldNotImport(pkgQN) 298 } 299 return targetPkgSch, nil 300 301 } 302 303 func getTargetSchema(n DefQName, c *iterateCtx) (*PackageSchemaAST, error) { 304 return findPackage(n.Package, c) 305 } 306 307 func maybeSysPkg(pkg Ident) bool { 308 return (pkg == "" || pkg == appdef.SysPackage) 309 } 310 311 func getNestedTableKind(rootTableKind appdef.TypeKind) (appdef.TypeKind, error) { 312 switch rootTableKind { 313 case appdef.TypeKind_CDoc, appdef.TypeKind_CRecord: 314 return appdef.TypeKind_CRecord, nil 315 case appdef.TypeKind_ODoc, appdef.TypeKind_ORecord: 316 return appdef.TypeKind_ORecord, nil 317 case appdef.TypeKind_WDoc, appdef.TypeKind_WRecord: 318 return appdef.TypeKind_WRecord, nil 319 default: 320 return appdef.TypeKind_null, ErrUnexpectedRootTableKind(int(rootTableKind)) 321 } 322 } 323 324 func dataTypeToDataKind(t DataType) appdef.DataKind { 325 if t.Blob { 326 return appdef.DataKind_RecordID 327 } 328 if t.Bool { 329 return appdef.DataKind_bool 330 } 331 if t.Bytes != nil { 332 return appdef.DataKind_bytes 333 } 334 if t.Currency { 335 return appdef.DataKind_int64 336 } 337 if t.Float32 { 338 return appdef.DataKind_float32 339 } 340 if t.Float64 { 341 return appdef.DataKind_float64 342 } 343 if t.Int32 { 344 return appdef.DataKind_int32 345 } 346 if t.Int64 { 347 return appdef.DataKind_int64 348 } 349 if t.QName { 350 return appdef.DataKind_QName 351 } 352 if t.Varchar != nil { 353 return appdef.DataKind_string 354 } 355 if t.Timestamp { 356 return appdef.DataKind_int64 357 } 358 if t.Record { 359 return appdef.DataKind_Record 360 } 361 return appdef.DataKind_null 362 } 363 364 func buildQname(ctx *iterateCtx, pkg Ident, name Ident) appdef.QName { 365 if pkg == "" { 366 pkg = Ident(ctx.pkg.Name) 367 } 368 return appdef.NewQName(string(pkg), string(name)) 369 } 370 371 func contains(s []Identifier, e Ident) bool { 372 for _, a := range s { 373 if a.Value == e { 374 return true 375 } 376 } 377 return false 378 }