UseCase:
я все еще пишу “транслятор кода\препроцессор” (надеюсь на дня закончить) для проприеритарной среды разработки.
Т.к. основная претензия к текущему положению вещей у меня в отсутствии возможностей для инкапсуляции и полном отсутствии инструментов модульных тестов, иду не по пути парсинга исходников, а поддерживаю полностью живые объекты, чтобы можно было гонять тесты на питоне, не заходя в среду назначения.
Поэтому генерация кода идет по принципу логирования.
Допустим, при запуске вне процесса компиляции (в тестах), этот код ведет себя вполне по-человечьи:
arrY = kArrInt(name='arrY', sequence=[1, 2, 3, 6]) y = kInt(name='y') break_indicies = [0, 2, 3, 3, 3] with For(arr=self.arrX) as seq: for idx, val in enumerate(seq): self.x <<= val with For(arr=arrY) as seq_y: for idx2, val2 in enumerate(seq_y): with If(self.x == val2): check() y <<= val2 with self.subTest(): self.assertEqual( idx2, break_indicies[idx]) with Else(): check() Break()
inc($_for_loop_curr_idx)
%_for_loop_idx[$_for_loop_curr_idx] := 0
while(%_for_loop_idx[$_for_loop_curr_idx] < 5)
$x := %arrX[%_for_loop_idx[$_for_loop_curr_idx]]
inc($_for_loop_curr_idx)
%_for_loop_idx[$_for_loop_curr_idx] := 0
while(%_for_loop_idx[$_for_loop_curr_idx] < 4)
if($x = %arrY[%_for_loop_idx[$_for_loop_curr_idx]])
$y := %arrY[%_for_loop_idx[$_for_loop_curr_idx]]
else
%_for_loop_idx[$_for_loop_curr_idx] := 4
end if
inc(%_for_loop_idx[$_for_loop_curr_idx])
end while
dec($_for_loop_curr_idx)
inc(%_for_loop_idx[$_for_loop_curr_idx])
end while
dec($_for_loop_curr_idx)
Аднака, все куда сложнее, когда дело касается вызываемых объектов, их там кот наплакал: каллбеки и функции, но они есть, и сделаны с помощью декораторов, которые эти функции добавляют в лог, а на этапе генерации кода разворачивают.
Типа так:
@func def method(self, arg=kOut(int)): self.assertEqual( arg.name(), '%_stack_functions_int_arr[0 + %_stack_functions_int_idx' + '[$_stack_functions_int_pointer]]') arg <<= self.int def test_funcs(self): @func def foo(loc: kArg(int, 5)=arr): with For(arr=loc) as seq: for item in seq: self.method(item) foo() # test for excluding from generation without # "calling" it @func def bar(var: int): self.method() bar(1, inline=True) generated_exec = KspObject.generate_all_executables() self.maxDiff = None self.assertEqual( unpack_lines(generated_exec), executables.format(method=Function.get_func_name(self.method), foo=Function.get_func_name(foo)))
Но вот каллбеки вызывать прямо как-то противоестесственно. Да и не понятно где.
вот такое их использование чисто синтаксически как раз само то:
init_lines = \ '''on init foo_line bar_line cb body is 4 end on''' release_lines = \ '''on release release end on''' class TestWrapers(DevTest): def setUp(self): Output().refresh() self.var = 4 def cb(self): Output().put(f'cb body is {self.var}') self.var += 1 def runTest(self): @init def foo(): Output().put('foo_line') @init def bar(): Output().put('bar_line') init(self.cb) self.assertEqual(self.var, 4) self.assertEqual( unpack_lines(InitCallback.generate_body()), init_lines) self.assertEqual(self.var, 5) @release def rls(): Output().put('release') self.assertEqual( unpack_lines(ReleaseCallback.generate_body()), release_lines) rls() self.assertEqual(Output().pop(), 'release')
Но я бы хотел сохранить возможность пакетной генерации скриптов, причем, желательно, не вылезая для этого в батники, а делать это средствами питона.
Допустим, есть два объекта типа Script, в функциях main которого и инициализируются все необходимые скрипты классы. Ну и при компиляции они, соответственно, генерируют код, а потом скрипт рефрешит классы к начальному состоянию, можно компилировать следующий.
Вот такой схеме оч мешает наличие декораторов… Можно ли как-то избежать их, не теряя выразительности, или каким-то макаром “переимпортировать” модули, чтобы заново создавались все объекты, присутствующие в них?
