move and fix baobab for generation

This commit is contained in:
timurgordon
2025-01-02 01:42:39 -05:00
parent bada9508ef
commit cbdc0fd313
60 changed files with 1743 additions and 1064 deletions

View File

@@ -0,0 +1,413 @@
module generator
import freeflowuniverse.herolib.baobab.specification {BaseObject}
import freeflowuniverse.herolib.core.code { VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
import freeflowuniverse.herolib.core.texttools
import os
const id_param = Param{
name: 'id'
typ: Type{
symbol: 'u32'
}
}
pub fn generate_object_code(actor Struct, object BaseObject) VFile {
obj_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
mut items := []CodeItem{}
items = [generate_new_method(actor, object), generate_get_method(actor, object),
generate_set_method(actor, object), generate_delete_method(actor, object),
generate_list_result_struct(actor, object), generate_list_method(actor, object)]
items << generate_object_methods(actor, object)
mut file := code.new_file(
mod: texttools.name_fix(actor.name)
name: obj_name
imports: [
Import{
mod: object.structure.mod
types: [object_type]
},
Import{
mod: 'freeflowuniverse.herolib.baobab.backend'
types: ['FilterParams']
},
]
items: items
)
if object.structure.fields.any(it.attrs.any(it.name == 'index')) {
// can't filter without indices
filter_params := generate_filter_params(actor, object)
file.items << filter_params.map(CodeItem(it))
file.items << generate_filter_method(actor, object)
}
return file
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_get_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
get_method := Function{
name: 'get_${object_name}'
description: 'gets the ${object_name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [generator.id_param]
result: Param{
typ: Type{
symbol: object.structure.name
}
is_result: true
}
body: 'return actor.backend.get[${object_type}](id)!'
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_set_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: true
)
body := 'actor.backend.set[${object_type}](${object_name})!'
get_method := Function{
name: 'set_${object_name}'
description: 'updates the ${object.structure.name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [
Param{
name: object_name
typ: Type{
symbol: object_type
}
},
]
result: Param{
is_result: true
}
body: body
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_delete_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
body := 'actor.backend.delete[${object_type}](id)!'
get_method := Function{
name: 'delete_${object_name}'
description: 'deletes the ${object.structure.name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [generator.id_param]
result: Param{
is_result: true
}
body: body
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_new_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
body := 'return actor.backend.new[${object_type}](${object_name})!'
new_method := Function{
name: 'new_${object_name}'
description: 'news the ${object.structure.name} with the given object id'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: [
Param{
name: object_name
typ: Type{
symbol: object_type
}
},
]
result: Param{
is_result: true
typ: Type{
symbol: 'u32'
}
}
body: body
}
return new_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_list_result_struct(actor Struct, object BaseObject) Struct {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
return Struct{
name: '${object_type}List'
is_pub: true
fields: [
StructField{
name: 'items'
typ: Type{
symbol: '[]${object_type}'
}
},
]
}
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_list_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
list_struct := Struct{
name: '${object_type}List'
fields: [
StructField{
name: 'items'
typ: Type{
symbol: '[]${object_type}'
}
},
]
}
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
body := 'return ${object_type}List{items:actor.backend.list[${object_type}]()!}'
result_struct := generate_list_result_struct(actor, object)
mut result := Param{}
result.typ.symbol = result_struct.name
result.is_result = true
new_method := Function{
name: 'list_${object_name}'
description: 'lists all of the ${object_name} objects'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: []
result: result
body: body
}
return new_method
}
fn generate_filter_params(actor Struct, object BaseObject) []Struct {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
return [
Struct{
name: 'Filter${object_type}Params'
fields: [
StructField{
name: 'filter'
typ: Type{
symbol: '${object_type}Filter'
}
},
StructField{
name: 'params'
typ: Type{
symbol: 'FilterParams'
}
},
]
},
Struct{
name: '${object_type}Filter'
fields: object.structure.fields.filter(it.attrs.any(it.name == 'index'))
},
]
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_filter_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
params_type := 'Filter${object_type}Params'
body := 'return actor.backend.filter[${object_type}, ${object_type}Filter](filter.filter, filter.params)!'
return Function{
name: 'filter_${object_name}'
description: 'lists all of the ${object_name} objects'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: [
Param{
name: 'filter'
typ: Type{
symbol: params_type
}
},
]
result: Param{
typ: Type{
symbol: '[]${object_type}'
}
is_result: true
}
body: body
}
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_object_methods(actor Struct, object BaseObject) []Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
mut funcs := []Function{}
for method in object.methods {
mut params := [Param{
name: 'id'
typ: Type{
symbol: 'u32'
}
}]
params << method.params
funcs << Function{
name: method.name
description: method.description
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: params
result: method.result
body: 'obj := actor.backend.get[${method.receiver.typ.symbol}](id)!
obj.${method.name}(${method.params.map(it.name).join(',')})
actor.backend.set[${method.receiver.typ.symbol}](obj)!
'
}
}
return funcs
}
@[params]
struct GenerateParamGetters {
structure Struct
prefix string
only_mutable bool // if true generates param.get methods for only mutable struct fields. Used for updating.
}
fn generate_param_getters(params GenerateParamGetters) []string {
mut param_getters := []string{}
fields := if params.only_mutable {
params.structure.fields.filter(it.is_mut && it.is_pub)
} else {
params.structure.fields.filter(it.is_pub)
}
for field in fields {
if field.typ.symbol.starts_with_capital() {
subgetters := generate_param_getters(GenerateParamGetters{
...params
structure: field.structure
prefix: '${field.name}_'
})
// name of the tested object, used for param declaration
// ex: fruits []Fruit becomes fruit_name
nested_name := field.structure.name.to_lower()
if field.typ.is_map {
param_getters.insert(0, '${nested_name}_key := params.get(\'${nested_name}_key\')!')
param_getters << '${field.name}: {${nested_name}_key: ${field.structure.name}}{'
} else if field.typ.is_array {
param_getters << '${field.name}: [${field.structure.name}{'
} else {
param_getters << '${field.name}: ${field.structure.name}{'
}
param_getters << subgetters
param_getters << if field.typ.is_array { '}]' } else { '}' }
continue
}
mut get_method := '${field.name}: params.get'
if field.typ.symbol != 'string' {
// TODO: check if params method actually exists
'get_${field.typ.symbol}'
}
if field.default != '' {
get_method += '_default'
}
get_method = get_method + "('${params.prefix}${field.name}')!"
param_getters << get_method
}
return param_getters
}
@[params]
struct GetChildField {
parent Struct @[required]
child Struct @[required]
}
fn get_child_field(params GetChildField) StructField {
fields := params.parent.fields.filter(it.typ.symbol == 'map[string]&${params.child.name}')
if fields.len != 1 {
panic('this should never happen')
}
return fields[0]
}