Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import Boo.Lang.Compiler.Ast
- import Boo.Lang.Extensions
- import Boo.Lang.PatternMatching
- //это подпроцедура - для избжания дублирования кода в вложенных макросах
- //он для всех одинаков
- //принимает имя и тип переменной для генерации
- macro expander:
- case [| expander $name, $type4fld|]:
- yield[|
- name_ref = ReferenceExpression($name + '_ref') //внутренняя ссылка
- name_loaded = ReferenceExpression($name + '_loaded') //флаг загружено или нет
- block = Persist["data"] as StructDefinition //здесь формируем структуру обмена
- if block is null:
- block = StructDefinition(Name:'exchange_struct')
- Persist["data"] = block
- block.Members.Add(Field(Name: $name, Type : SimpleTypeReference($type4fld), Modifiers :TypeMemberModifiers.Public))
- save_block = Persist["save_data"] as Block //блок спецкода для сохранения строк
- if save_block is null:
- save_block = Block()
- Persist["save_data"] = save_block
- vars = Persist["vars"] as List of Field //сюда регистрируются поля для создания структуры
- if vars is null:
- vars = List of Field()
- Persist["vars"] = vars
- vars.Add(Field(Name: name_loaded.ToString(),Type : SimpleTypeReference('bool')))
- |]
- //тело сохраняемого класа
- macro Persist:
- case [| Persist $name |]:
- //блоки описанные выше
- struct_data = Persist["data"] as StructDefinition
- var_data = Persist["vars"] as List of Field
- save_block = Persist["save_data"] as Block
- x as ClassDefinition
- x = [|
- partial class $name:
- //метод получения множества классов из плоского массива в базе
- static def convert_up(datamap as datamap_file, arr as (exchange_struct), delta as int):
- //delta - смещение мем-блока arr
- empty_exchange = exchange_struct()
- size_exchange_struct = memblock.size_type(exchange_struct)
- for st in arr:
- if st != empty_exchange:
- yield $name(datamap, st, delta)
- else:
- yield null
- delta += size_exchange_struct
- //преобразование произвольного IEnumerable[X] в массив его структур для записи в базу
- static def to_array(datamap_ as datamap_file, seq as $name*):
- lst = List of exchange_struct()
- for o in seq:
- if o:
- o.datamap = datamap_
- o.save_strings()
- lst.Add(o.exchange)
- else:
- lst.Add(exchange_struct())
- return lst.ToArray()
- //загрузка одного объекта, на который может быть много ссылок
- static def load(datamap as datamap_file, delta as int) as $name:
- obj as object
- return obj if delta == 0 or datamap.map_objects.TryGetValue(delta, obj)
- obj = $name(datamap, delta)
- datamap.map_objects[delta] = obj
- return obj
- //метод для считывая объекта из базы. может понабиться при отмене изменений
- def force_load(replace as bool):
- new_val = $name(datamap, datamap.load[of exchange_struct](address), address)
- datamap.map_objects[address] = new_val if replace
- return new_val
- def save_null():
- //метит в массиве как null
- exchange = exchange_struct()
- save() if address
- //сохраняет все строки объекта
- def save_strings():
- $save_block
- //сохраняет объект
- def save():
- save_strings()
- datamap.write[of exchange_struct](exchange, address)
- //файл базы, откуда объект
- //одновременно можно работать с несколькими базами
- public datamap as datamap_file
- //смещение в базе
- public address as int
- //объявление типа структуры
- $struct_data
- //сама структура
- exchange as exchange_struct
- //остальное тело класса
- $(Persist.Body)
- def constructor():
- pass
- def constructor(dmap as datamap_file):
- datamap = dmap
- def constructor(dmap as datamap_file, add as int):
- datamap = dmap
- address = add
- exchange = datamap.load[of exchange_struct](address)
- def constructor(dmap as datamap_file, data_class as exchange_struct, add as int):
- datamap = dmap
- exchange = data_class
- address = add
- |]
- for i in var_data:
- x.Members.Add(i)
- yield x
- //для хранения value объектов
- macro VAR:
- case [| VAR $name as $type |]:
- if type.ToString() == "bool":
- type_ex = "bool"
- else:
- type_ex = 'int'
- //делаем доступными переменные из expander
- expander name.ToString(), type_ex
- vars.Add(Field(Name: name_ref.ToString(), Type : type))
- yield [|
- //простой обмен с полями структуры для чтения-записи
- $name as $type:
- get:
- return exchange.$name
- set:
- exchange.$name = value
- |]
- macro REFBLOCK:
- //для классов, которые хранятся как структуры, без возможности нескольких ссылок
- //это ускоряет загрузку за счет избежания регистации загруженных объектов
- case [| REFBLOCK $name as $type |]:
- expander name.ToString(), 'int'
- vars.Add(Field(Name: name_ref.ToString(), Type : type))
- gtype = type as GenericTypeReference
- assert gtype
- //получаем тип элемента, который можем использовать в макрокоде
- elem_type = ReferenceExpression(gtype.GenericArguments[0].ToString())
- //получаем тип контейнера, который можем использовать в макрокоде
- container_type = ReferenceExpression(gtype.Name)
- //получаем тип struct элементов контейнера
- struct_ref = ReferenceExpression(gtype.GenericArguments[0].ToString() + ".exchange_struct")
- yield [|
- $name as $type:
- get:
- if not $name_loaded:
- $name_loaded = true
- if exchange.$name:
- //загружаем, преобразовуем в нормальные классы
- arr = datamap.load_arr[of $struct_ref](exchange.$name)
- $name_ref = $container_type of $elem_type( $elem_type.convert_up(datamap,arr, exchange.$name))
- else:
- //ссылка нуль, значит контейнер пуст
- $name_ref = $container_type of $elem_type()
- return $name_ref
- set:
- $name_ref = value
- //записываем в базу, если присваивают новый контейнер
- datamap.write_arr[of $struct_ref]($elem_type.to_array(datamap, $name_ref), exchange.$name)
- |]
- macro REFVAL:
- case [| REFVAL $name as $type |]:
- //для сохранения массивов value типа, почти 1 в 1 как раньше
- expander name.ToString(), 'int'
- vars.Add(Field(Name: name_ref.ToString(), Type : type))
- gtype = type as GenericTypeReference
- assert gtype
- elem_type = ReferenceExpression(gtype.GenericArguments[0].ToString())
- container_type = ReferenceExpression(gtype.Name)
- yield [|
- $name as $type:
- get:
- if not $name_loaded:
- $name_loaded = true
- if exchange.$name:
- arr = datamap.load_arr[of $elem_type](exchange.$name)
- $name_ref = $container_type of $elem_type(arr)
- else:
- $name_ref = $container_type of $elem_type()
- return $name_ref
- set:
- $name_ref = value
- $name_loaded = true
- datamap.write_arr[of $elem_type]($name_ref.ToArray(), exchange.$name)
- |]
- //простая ссылка на объект
- macro REF:
- case [| REF $name as string |]:
- expander name.ToString(), 'int'
- //для строк используем спецкод поддержки, потому что они самостоятельные сохраняемые объекты,
- //запись которых контролируется их хозяином.
- name_has_saved = ReferenceExpression($name + '_has_saved')
- vars.Add(Field(Name: name_has_saved.ToString(), Type :SimpleTypeReference('bool')))
- vars.Add(Field(Name: name_ref.ToString(), Type :SimpleTypeReference('string')))
- sc = [|
- if $name_has_saved:
- datamap.write_string($name_ref, exchange.$name)
- $name_has_saved = false
- |]
- save_block.Add(sc)
- yield [|
- $name as string:
- get:
- if $name_ref is null:
- if exchange.$name:
- $name_ref = datamap.load_string(exchange.$name)
- else:
- $name_ref = ""
- return $name_ref
- set:
- $name_has_saved = true
- $name_ref = value
- |]
- //простая ссылка на другой Persist класс или контейнер с Persist классами
- case [| REF $name as $type |]:
- expander name.ToString(), 'int'
- vars.Add(Field(Name: name_ref.ToString(), Type : type))
- type_ref = ReferenceExpression(type.ToString())
- gtype = type as GenericTypeReference
- //контейнер или просто тип
- if gtype:
- //все как раньше
- elem_type = ReferenceExpression(gtype.GenericArguments[0].ToString())
- container_type = ReferenceExpression(gtype.Name)
- result = [|
- $name as $type:
- get:
- if not $name_loaded:
- $name_loaded = true
- if exchange.$name:
- arr = datamap.load_arr[of int](exchange.$name)
- $name_ref = $container_type of $elem_type()
- for el_address in arr:
- if el_address:
- el = $elem_type.load(datamap, el_address)
- else:
- el = null
- $name_ref.Add(el)
- else:
- $name_ref = $container_type of $elem_type()
- return $name_ref
- set:
- $name_ref = value
- $name_loaded = true
- arr_list = List of int()
- for el in value:
- if el:
- if el.address == 0:
- el.save()
- addr = el.address
- else:
- addr = 0
- arr_list.Add(addr)
- datamap.write_arr[of int](arr_list.ToArray(), exchange.$name)
- |]
- else: //для простого типа
- result = [|
- $name as $type:
- get:
- if not $name_loaded:
- $name_loaded = true
- $name_ref = $type_ref.load(datamap, exchange.$name) if exchange.$name
- return $name_ref
- set:
- $name_ref = value
- $name_loaded = true
- exchange.$name = $name_ref.address
- |]
- yield result
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement