diff --git a/py2v_transpiler/tests/input/transpile/test_classes_inheritance.v b/py2v_transpiler/tests/input/transpile/test_classes_inheritance.v new file mode 100644 index 00000000..3f0529d9 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_classes_inheritance.v @@ -0,0 +1,146 @@ +module main + +// @line: test_classes_inheritance.py:1:0 +pub struct Animal { + name string +} +// @line: test_classes_inheritance.py:11:0 +pub struct Dog { + Animal + breed string +} +// @line: test_classes_inheritance.py:22:0 +pub struct Cat { + Animal + lives int +} +// @line: test_classes_inheritance.py:46:0 +pub struct Vehicle { + brand string +} +// @line: test_classes_inheritance.py:52:0 +pub struct Car { + Vehicle + model string +} + +pub const Animal_new_animal__annotations__ = { 'name': 'string' } +pub const Animal_speak__annotations__ = { 'return': 'string' } +pub const Animal_info__annotations__ = { 'return': 'string' } +pub const Dog_new_dog__annotations__ = { 'name': 'string', 'breed': 'string' } +pub const Dog_speak__annotations__ = { 'return': 'string' } +pub const Dog_fetch__annotations__ = { 'return': 'string' } +pub const Cat_new_cat__annotations__ = { 'name': 'string' } +pub const Cat_speak__annotations__ = { 'return': 'string' } +pub const Cat_climb__annotations__ = { 'return': 'string' } +pub const Vehicle_new_vehicle__annotations__ = { 'brand': 'string' } +pub const Car_new_car__annotations__ = { 'brand': 'string', 'model': 'string' } +pub const Car_description__annotations__ = { 'return': 'string' } + +// @line: test_classes_inheritance.py:2:4 +pub fn new_animal(name string) Animal { + mut self := Animal{} + self.name = name + return self +} +// @line: test_classes_inheritance.py:5:4 +pub fn (self Animal) speak() string { + return 'Some sound' +} +// @line: test_classes_inheritance.py:8:4 +pub fn (self Animal) info() string { + return 'I am ${self.name}' +} +// @line: test_classes_inheritance.py:12:4 +pub fn new_dog(name string, breed string) Dog { + mut self := Dog{} + self.Animal = new_animal(name) + self.breed = breed + return self +} +// @line: test_classes_inheritance.py:16:4 +pub fn (self Dog) speak() string { + return 'Woof!' +} +// @line: test_classes_inheritance.py:19:4 +pub fn (self Dog) fetch() string { + return '${self.name} is fetching' +} +// @line: test_classes_inheritance.py:23:4 +pub fn new_cat(name string) Cat { + mut self := Cat{} + self.Animal = new_animal(name) + self.lives = 9 + return self +} +// @line: test_classes_inheritance.py:27:4 +pub fn (self Cat) speak() string { + return 'Meow!' +} +// @line: test_classes_inheritance.py:30:4 +pub fn (self Cat) climb() string { + return '${self.name} is climbing' +} +// @line: test_classes_inheritance.py:33:0 +pub fn test_basic_inheritance() { + mut dog := new_dog('Buddy', 'Golden') + println('${dog.name}') + println('${dog.breed}') + println('${dog.speak()}') + println('${dog.info()}') + println('${dog.fetch()}') +} +// @line: test_classes_inheritance.py:41:0 +pub fn test_polymorphism() { + animals := [new_dog('Rex', 'Shepherd'), new_cat('Whiskers')] + for animal in animals { + println('${animal.name} says: ${animal.speak()}') + } +} +// @line: test_classes_inheritance.py:49:4 +pub fn new_vehicle(brand string) Vehicle { + mut self := Vehicle{} + self.brand = brand + return self +} +// @line: test_classes_inheritance.py:53:4 +pub fn new_car(brand string, model string) Car { + mut self := Car{} + self.Vehicle = new_vehicle(brand) + self.model = model + return self +} +// @line: test_classes_inheritance.py:57:4 +pub fn (self Car) description() string { + return '${self.brand} ${self.model}' +} +// @line: test_classes_inheritance.py:60:0 +pub fn test_class_variables() { + car := new_car('Toyota', 'Camry') + println('Car: ${car.description()}') + println('Wheels: ${car.wheels}') + println('Class wheels: ${Vehicle.wheels}') +} +// @line: test_classes_inheritance.py:66:0 +pub fn test_isinstance_issubclass() { + mut dog := new_dog('Spot', 'Labrador') + println('dog is Animal: ${dog is Animal}') + println('dog is Dog: ${dog is dog}') + println('dog is Cat: ${dog is Cat}') + println('Dog is subclass of Animal: ${issubclass(dog, Animal)}') + println('Cat is subclass of Animal: ${issubclass(Cat, Animal)}') + println('Dog is subclass of Cat: ${issubclass(dog, Cat)}') +} +// @line: test_classes_inheritance.py:77:0 +pub fn test() { + test_basic_inheritance() + test_polymorphism() + test_class_variables() + test_isinstance_issubclass() +} + +fn main() { + // @line: test_classes_inheritance.py:83:0 + // if __name__ == '__main__': + test() +} \ No newline at end of file diff --git a/py2v_transpiler/tests/input/transpile/test_classes_inheritance_helpers.v b/py2v_transpiler/tests/input/transpile/test_classes_inheritance_helpers.v new file mode 100644 index 00000000..d004f148 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_classes_inheritance_helpers.v @@ -0,0 +1,124 @@ +module main + +pub struct NoneType {} + +pub fn (n NoneType) str() string { + return 'None' +} + +pub struct Interpolation { +pub: + value Any + expression string + conversion string + format_spec string +} + +pub struct Template { +pub: + strings []string + interpolations []Interpolation +} + +pub fn (t Template) values() []Any { + mut res := []Any{cap: t.interpolations.len} + for i in t.interpolations { + res << i.value + } + return res +} + +pub fn (t1 Template) + (t2 Template) Template { + if t1.strings.len == 0 { return t2 } + if t2.strings.len == 0 { return t1 } + mut new_strings := t1.strings[..t1.strings.len - 1].clone() + new_strings << t1.strings.last() + t2.strings[0] + if t2.strings.len > 1 { + new_strings << t2.strings[1..] + } + mut new_interpolations := t1.interpolations.clone() + new_interpolations << t2.interpolations + return Template{ + strings: new_strings + interpolations: new_interpolations + } +} + +pub type Any = Interpolation | NoneType | Template | []Any | []u8 | bool | f64 | i64 | int | map[string]Any | string + +pub enum PyAnnotationFormat { value forwardref string } + +pub fn py_get_type_hints[T]() map[string]string { + mut hints := map[string]string{} + $for field in T.fields { + hints[field.name] = field.typ + } + return hints +} + +pub fn py_get_type_hints_generic(obj Any) map[string]string { + return map[string]string{} +} + +struct PyGeneratorInput { + val Any + is_exc bool + exc_msg string +} +struct PyGenerator[T] { +mut: + out chan T + in_ chan PyGeneratorInput + open bool = true +} + +fn (mut g PyGenerator[T]) next() ?T { + if !g.open { return none } + g.in_ <- PyGeneratorInput{val: 0} // Send dummy value + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) send(val Any) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{val: val} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) throw(msg string) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{is_exc: true, exc_msg: msg} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) close() { + g.open = false + g.in_.close() + // g.out will be closed by the generator function loop when it detects in_ closed or panic +} +fn py_yield[T](ch_out chan T, ch_in chan PyGeneratorInput, val T) Any { + ch_out <- val + inp := <-ch_in + if inp.is_exc { + panic(inp.exc_msg) + } + return inp.val +} +//##LLM@@ String formatting for bytes is stubbed and might be incorrect. Please implement proper bytes formatting or use V string interpolation. +fn py_bytes_format(fmt []u8, args Any) []u8 { + // Simplistic implementation for b'%s' % b'val' + // Converts bytes to string, formats, and converts back. + // This is not efficient or correct for non-ASCII bytes but works for simple cases. + fmt_str := fmt.bytestr() + // TODO: handle args properly. V's string interpolation/formatting expects distinct args. + // If args is []u8, treat as string. + arg_str := if args is []u8 { args.bytestr() } else { '${args}' } + + // Manual substitution of %s + // V does not have sprintf for runtime strings easily available in core without C interop. + // Simple replace for %s + res := fmt_str.replace('%s', arg_str) + return res.bytes() +} diff --git a/py2v_transpiler/tests/input/transpile/test_comprehensions.v b/py2v_transpiler/tests/input/transpile/test_comprehensions.v new file mode 100644 index 00000000..cf9616dd --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_comprehensions.v @@ -0,0 +1,103 @@ +module main + +// @line: test_comprehensions.py:1:0 +pub fn test_list_comprehension() { + mut squares := []int{cap: 10} + for x in 0..10 { + squares << x * x + } + println('${squares}') + mut evens := []int{} + for x in 0..20 { + if x % 2 == 0 { + evens << x + } + } + println('${evens}') + mut matrix := []int{cap: 3} + for i in 0..3 { + mut py_comp_1 := []int{cap: 3} + for j in 0..3 { + py_comp_1 << i * j + } + matrix << py_comp_1 + } + println('${matrix}') +} +// @line: test_comprehensions.py:14:0 +pub fn test_dict_comprehension() { + mut square_map := map[int]int{} + for x in 0..5 { + square_map[x] = x * x + } + println('${square_map}') + mut even_map := map[int]int{} + for x in 0..10 { + if x % 2 == 0 { + even_map[x] = x * 2 + } + } + println('${even_map}') +} +// @line: test_comprehensions.py:23:0 +pub fn test_set_comprehension() { + mut unique_squares := map[int]bool{} + for x in -3..4 { + unique_squares[x * x] = true + } + println('${unique_squares}') +} +// @line: test_comprehensions.py:28:0 +pub fn test_generator_expression() { + mut gen := []int{cap: 5} + for x in 0..5 { + gen << x * x + } + for val in gen { + println('${val}') + } + mut filtered_gen := []int{} + for x in 0..10 { + if x > 5 { + filtered_gen << x + } + } + for val in filtered_gen { + println('${val}') + } +} +// @line: test_comprehensions.py:39:0 +pub fn test_nested_loops_in_comprehension() { + //##LLM@@ Complex nested comprehension detected. To ensure readability and idiomatic V, please unfold this into explicit 'for' loops or a clean chain of .map() and .filter() calls. + mut pairs := [][]int{cap: 3} + for x in 0..3 { + for y in 0..3 { + pairs << [x, y] + } + } + println('${pairs}') + //##LLM@@ Complex nested comprehension detected. To ensure readability and idiomatic V, please unfold this into explicit 'for' loops or a clean chain of .map() and .filter() calls. + mut filtered_pairs := [][]int{cap: 5} + for x in 0..5 { + for y in 0..5 { + if x + y < 5 { + filtered_pairs << [x, y] + } + } + } + println('${filtered_pairs}') +} +// @line: test_comprehensions.py:48:0 +pub fn test() { + test_list_comprehension() + test_dict_comprehension() + test_set_comprehension() + test_generator_expression() + test_nested_loops_in_comprehension() +} + +fn main() { + // @line: test_comprehensions.py:55:0 + // if __name__ == '__main__': + test() +} \ No newline at end of file diff --git a/py2v_transpiler/tests/input/transpile/test_comprehensions_helpers.v b/py2v_transpiler/tests/input/transpile/test_comprehensions_helpers.v new file mode 100644 index 00000000..d004f148 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_comprehensions_helpers.v @@ -0,0 +1,124 @@ +module main + +pub struct NoneType {} + +pub fn (n NoneType) str() string { + return 'None' +} + +pub struct Interpolation { +pub: + value Any + expression string + conversion string + format_spec string +} + +pub struct Template { +pub: + strings []string + interpolations []Interpolation +} + +pub fn (t Template) values() []Any { + mut res := []Any{cap: t.interpolations.len} + for i in t.interpolations { + res << i.value + } + return res +} + +pub fn (t1 Template) + (t2 Template) Template { + if t1.strings.len == 0 { return t2 } + if t2.strings.len == 0 { return t1 } + mut new_strings := t1.strings[..t1.strings.len - 1].clone() + new_strings << t1.strings.last() + t2.strings[0] + if t2.strings.len > 1 { + new_strings << t2.strings[1..] + } + mut new_interpolations := t1.interpolations.clone() + new_interpolations << t2.interpolations + return Template{ + strings: new_strings + interpolations: new_interpolations + } +} + +pub type Any = Interpolation | NoneType | Template | []Any | []u8 | bool | f64 | i64 | int | map[string]Any | string + +pub enum PyAnnotationFormat { value forwardref string } + +pub fn py_get_type_hints[T]() map[string]string { + mut hints := map[string]string{} + $for field in T.fields { + hints[field.name] = field.typ + } + return hints +} + +pub fn py_get_type_hints_generic(obj Any) map[string]string { + return map[string]string{} +} + +struct PyGeneratorInput { + val Any + is_exc bool + exc_msg string +} +struct PyGenerator[T] { +mut: + out chan T + in_ chan PyGeneratorInput + open bool = true +} + +fn (mut g PyGenerator[T]) next() ?T { + if !g.open { return none } + g.in_ <- PyGeneratorInput{val: 0} // Send dummy value + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) send(val Any) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{val: val} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) throw(msg string) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{is_exc: true, exc_msg: msg} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) close() { + g.open = false + g.in_.close() + // g.out will be closed by the generator function loop when it detects in_ closed or panic +} +fn py_yield[T](ch_out chan T, ch_in chan PyGeneratorInput, val T) Any { + ch_out <- val + inp := <-ch_in + if inp.is_exc { + panic(inp.exc_msg) + } + return inp.val +} +//##LLM@@ String formatting for bytes is stubbed and might be incorrect. Please implement proper bytes formatting or use V string interpolation. +fn py_bytes_format(fmt []u8, args Any) []u8 { + // Simplistic implementation for b'%s' % b'val' + // Converts bytes to string, formats, and converts back. + // This is not efficient or correct for non-ASCII bytes but works for simple cases. + fmt_str := fmt.bytestr() + // TODO: handle args properly. V's string interpolation/formatting expects distinct args. + // If args is []u8, treat as string. + arg_str := if args is []u8 { args.bytestr() } else { '${args}' } + + // Manual substitution of %s + // V does not have sprintf for runtime strings easily available in core without C interop. + // Simple replace for %s + res := fmt_str.replace('%s', arg_str) + return res.bytes() +} diff --git a/py2v_transpiler/tests/input/transpile/test_decorators.v b/py2v_transpiler/tests/input/transpile/test_decorators.v new file mode 100644 index 00000000..edf5be51 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_decorators.v @@ -0,0 +1,225 @@ +module main + +import div72.vexc + +// @line: test_decorators.py:81:4 +pub struct CountCalls { + func fn (...Any) Any = unsafe { nil } + count int +} +// @line: test_decorators.py:118:4 +pub struct Temperature { + _celsius fn (...Any) Any = unsafe { nil } +} + +// @line: test_decorators.py:1:0 +pub fn test_simple_decorator() { +// @line: test_decorators.py:2:4 + mut decorator := fn (func fn (...Any) Any) fn (...Any) Any { +// @line: test_decorators.py:3:8 + mut wrapper := fn [func] () Any { + println('Before') + func() + println('After') + } + return wrapper + } +// @decorator +// @line: test_decorators.py:10:4 + mut say_hello := fn [decorator] () { + println('Hello!') + } + say_hello() +} +// @line: test_decorators.py:15:0 +pub fn test_decorator_with_args() { +// @line: test_decorators.py:16:4 + mut decorator := fn (func fn (...Any) Any) fn (...Any) Any { +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:17:8 + mut wrapper := fn [func] (args ...Any, kwargs map[string]string) Any { + println('Calling with args: ${args}, kwargs: ${kwargs}') + return func(...args, kwargs) + } + return wrapper + } +// @decorator +// @line: test_decorators.py:23:4 + mut greet := fn [decorator] (name string, age int) { + println('Name: ${name}, Age: ${age}') + } + greet('Alice', 30) +} +// @line: test_decorators.py:28:0 +pub fn test_decorator_return_value() { +// @line: test_decorators.py:29:4 + mut decorator := fn (func fn (...Any) Any) fn (...Any) Any { +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:30:8 + mut wrapper := fn [func] (args ...Any, kwargs map[string]string) Any { + mut result := func(...args, kwargs) + return result * 2 + } + return wrapper + } +// @decorator +// @line: test_decorators.py:36:4 + mut add := fn [decorator] (a int, b int) int { + return a + b + } + println('${add(3, 4)}') +} +// @line: test_decorators.py:41:0 +pub fn test_multiple_decorators() { +// @line: test_decorators.py:42:4 + mut decorator1 := fn (func fn (...Any) Any) fn (...Any) Any { +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:43:8 + mut wrapper := fn [func] (args ...Any, kwargs map[string]string) Any { + println('Decorator 1 before') + mut result := func(...args, kwargs) + println('Decorator 1 after') + return result + } + return wrapper + } +// @line: test_decorators.py:50:4 + mut decorator2 := fn (func fn (...Any) Any) fn (...Any) Any { +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:51:8 + mut wrapper := fn [func] (args ...Any, kwargs map[string]string) Any { + println('Decorator 2 before') + mut result := func(...args, kwargs) + println('Decorator 2 after') + return result + } + return wrapper + } +// @decorator1 +// @decorator2 +// @line: test_decorators.py:60:4 + mut test_func := fn [decorator1, decorator2] () { + println('Inside function') + } + test_func() +} +// @line: test_decorators.py:65:0 +pub fn test_decorator_with_params() { +// @line: test_decorators.py:66:4 + mut repeat := fn (times int) fn (...Any) Any { +// @line: test_decorators.py:67:8 + mut decorator := fn [times] (func fn (...Any) Any) fn (...Any) Any { +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:68:12 + mut wrapper := fn [func, times] (args ...Any, kwargs map[string]string) Any { + for _ in 0..times { + func(...args, kwargs) + } + } + return wrapper + } + return decorator + } +// @repeat(3) +// @line: test_decorators.py:75:4 + mut say_hi := fn [repeat] () { + println('Hi!') + } + say_hi() +} +// @line: test_decorators.py:80:0 +pub fn test_class_decorator() { +// @line: test_decorators.py:82:8 + mut new_ := fn (func fn (...Any) Any) { + mut self := {} + self.func = func + self.count = 0 + return self + } +//##LLM@@ Function `__call__` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:86:8 + //##LLM@@ Unmapped Python dunder method (e.g., __call__, __getitem__) detected. V handles object behavior and operator overloading differently. Please implement the equivalent V logic or refactor the calling code. + mut __call__ := fn (args ...Any, kwargs map[string]string) Any { + self.count += 1 + println('Call ${self.count}') + return self.func(...args, kwargs) + } +// @CountCalls +// @line: test_decorators.py:92:4 + mut greet := fn (name string) { + println('Hello, ${name}!') + } + greet('Alice') + greet('Bob') + greet('Charlie') +} +// @line: test_decorators.py:99:0 +pub fn test_functools_wraps() { +// @line: test_decorators.py:102:4 + mut decorator := fn (func fn (...Any) Any) fn (...Any) Any { +// @functools.wraps(func) +//##LLM@@ Function `wrapper` has both *args and **kwargs. V requires the variadic parameter (...args) to be the final parameter. Please reorder the parameters so that the variadic parameter is last, and update all calls to this function accordingly. +// @line: test_decorators.py:104:8 + mut wrapper := fn [func] (args ...Any, kwargs map[string]string) Any { + // Wrapper docstring + return func(...args, kwargs) + } + return wrapper + } +// @decorator +// @line: test_decorators.py:110:4 + mut original_func := fn [decorator] () { + // Original docstring + } + println('Name: ${original_func____name__}') + println('Doc: ${original_func____doc__}') +} +// @line: test_decorators.py:117:0 +pub fn test_property_decorator() { +// @line: test_decorators.py:119:8 + mut new_ := fn (celsius f64) { + mut self := {} + self._celsius = celsius + return self + } +// @property +// @line: test_decorators.py:123:8 + mut celsius := fn () f64 { + return self._celsius + } +// @celsius__setter +// @line: test_decorators.py:127:8 + mut set_celsius := fn [celsius] (value f64) f64 { + if value < -273.15 { + vexc.raise('ValueError', 'Below absolute zero') + } + self._celsius = value + } +// @property +// @line: test_decorators.py:133:8 + mut fahrenheit := fn () f64 { + return f64((self._celsius as f64) * f64(9)) / f64(5) + f64(32) + } + temp := new_temperature(25) + println('Celsius: ${temp.celsius}') + println('Fahrenheit: ${temp.fahrenheit}') + temp.celsius = 30 + println('New Celsius: ${temp.celsius}') +} +// @line: test_decorators.py:142:0 +pub fn test() { + test_simple_decorator() + test_decorator_with_args() + test_decorator_return_value() + test_multiple_decorators() + test_decorator_with_params() + test_class_decorator() + test_functools_wraps() + test_property_decorator() +} + +fn main() { + // @line: test_decorators.py:152:0 + // if __name__ == '__main__': + test() +} \ No newline at end of file diff --git a/py2v_transpiler/tests/input/transpile/test_decorators_helpers.v b/py2v_transpiler/tests/input/transpile/test_decorators_helpers.v new file mode 100644 index 00000000..8761a188 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_decorators_helpers.v @@ -0,0 +1,132 @@ +module main + +pub struct NoneType {} + +pub fn (n NoneType) str() string { + return 'None' +} + +pub struct Interpolation { +pub: + value Any + expression string + conversion string + format_spec string +} + +pub struct Template { +pub: + strings []string + interpolations []Interpolation +} + +pub fn (t Template) values() []Any { + mut res := []Any{cap: t.interpolations.len} + for i in t.interpolations { + res << i.value + } + return res +} + +pub fn (t1 Template) + (t2 Template) Template { + if t1.strings.len == 0 { return t2 } + if t2.strings.len == 0 { return t1 } + mut new_strings := t1.strings[..t1.strings.len - 1].clone() + new_strings << t1.strings.last() + t2.strings[0] + if t2.strings.len > 1 { + new_strings << t2.strings[1..] + } + mut new_interpolations := t1.interpolations.clone() + new_interpolations << t2.interpolations + return Template{ + strings: new_strings + interpolations: new_interpolations + } +} + +pub type Any = Interpolation | NoneType | Template | []Any | []u8 | bool | f64 | i64 | int | map[string]Any | string + +pub enum PyAnnotationFormat { value forwardref string } + +pub fn py_get_type_hints[T]() map[string]string { + mut hints := map[string]string{} + $for field in T.fields { + hints[field.name] = field.typ + } + return hints +} + +pub fn py_get_type_hints_generic(obj Any) map[string]string { + return map[string]string{} +} + +struct PyGeneratorInput { + val Any + is_exc bool + exc_msg string +} +struct PyGenerator[T] { +mut: + out chan T + in_ chan PyGeneratorInput + open bool = true +} + +fn py_reduce[T](op fn (acc T, x T) T, iter []T) T { + if iter.len == 0 { panic('reduce() of empty sequence with no initial value') } + mut acc := iter[0] + for i in 1..iter.len { + acc = op(acc, iter[i]) + } + return acc +} +fn (mut g PyGenerator[T]) next() ?T { + if !g.open { return none } + g.in_ <- PyGeneratorInput{val: 0} // Send dummy value + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) send(val Any) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{val: val} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) throw(msg string) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{is_exc: true, exc_msg: msg} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) close() { + g.open = false + g.in_.close() + // g.out will be closed by the generator function loop when it detects in_ closed or panic +} +fn py_yield[T](ch_out chan T, ch_in chan PyGeneratorInput, val T) Any { + ch_out <- val + inp := <-ch_in + if inp.is_exc { + panic(inp.exc_msg) + } + return inp.val +} +//##LLM@@ String formatting for bytes is stubbed and might be incorrect. Please implement proper bytes formatting or use V string interpolation. +fn py_bytes_format(fmt []u8, args Any) []u8 { + // Simplistic implementation for b'%s' % b'val' + // Converts bytes to string, formats, and converts back. + // This is not efficient or correct for non-ASCII bytes but works for simple cases. + fmt_str := fmt.bytestr() + // TODO: handle args properly. V's string interpolation/formatting expects distinct args. + // If args is []u8, treat as string. + arg_str := if args is []u8 { args.bytestr() } else { '${args}' } + + // Manual substitution of %s + // V does not have sprintf for runtime strings easily available in core without C interop. + // Simple replace for %s + res := fmt_str.replace('%s', arg_str) + return res.bytes() +} diff --git a/py2v_transpiler/tests/input/transpile/test_dict_operations.v b/py2v_transpiler/tests/input/transpile/test_dict_operations.v new file mode 100644 index 00000000..e3bb311c --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_dict_operations.v @@ -0,0 +1,113 @@ +module main + +// @line: test_dict_operations.py:1:0 +pub fn test_dict_creation() { + mut d1 := {'a': 1, 'b': 2} + println('${d1}') + mut d2 := map[string]int{} + println('${d2}') + d3 := map[string]Any([['x', 10], ['y', 20]]) + println('${d3}') +} +// @line: test_dict_operations.py:14:0 +pub fn test_dict_access() { + mut d := {'name': Any('Alice'), 'age': Any(30), 'city': Any('NYC')} + println('${d['name']}') + println('${d['age'] or { 0 }}') + println('${d['country'] or { 'USA' }}') +} +// @line: test_dict_operations.py:20:0 +pub fn test_dict_modification() { + mut d := {'a': 1} + d['b'] = 2 + d['a'] = 10 + println('${d}') +} +// @line: test_dict_operations.py:26:0 +pub fn test_dict_deletion() { + mut d := {'a': 1, 'b': 2, 'c': 3} + mut val := d.pop('b') + println('Popped: ${val}, Dict: ${d}') + d.delete('a') + println('After del: ${d}') + /* d.clear() */ d = {} + println('Cleared: ${d}') +} +// @line: test_dict_operations.py:37:0 +pub fn test_dict_keys_values_items() { + mut d := {'x': 1, 'y': 2, 'z': 3} + println('Keys: ${[]Any(d.keys())}') + println('Values: ${[]Any(d.values())}') + println('Items: ${[]Any(d.items())}') + for key in d { + println('Key: ${key}') + } + for key, value in d { + println('${key}: ${value}') + } +} +// @line: test_dict_operations.py:51:0 +pub fn test_dict_update() { + mut d1 := {'a': 1, 'b': 2} + mut d2 := {'b': 20, 'c': 3} + d1.update(d2) + println('Updated: ${d1}') + d1.update() + println('Updated with kwargs: ${d1}') +} +// @line: test_dict_operations.py:60:0 +pub fn test_dict_comprehension() { + mut squares := map[int]int{} + for x in 0..5 { + squares[x] = x * x + } + println('${squares}') + mut even_squares := map[int]int{} + for x in 0..10 { + if x % 2 == 0 { + even_squares[x] = x * x + } + } + println('${even_squares}') +} +// @line: test_dict_operations.py:68:0 +pub fn test_dict_fromkeys() { + keys := ['a', 'b', 'c'] + mut d := dict.fromkeys(keys, 0).clone() + println('From keys: ${d}') + mut d2 := dict.fromkeys(keys, []Any{}).clone() + println('From keys with list: ${d2}') +} +// @line: test_dict_operations.py:76:0 +pub fn test_dict_setdefault() { + mut d := {'a': 1} + mut val := d.setdefault('b', 2) + println('Setdefault result: ${val}, Dict: ${d}') + val2 := d.setdefault('a', 100) + println('Setdefault existing: ${val2}, Dict: ${d}') +} +// @line: test_dict_operations.py:84:0 +pub fn test_dict_membership() { + mut d := {'x': 1, 'y': 2} + println('${'x' in d}') + println('${'z' !in d}') +} +// @line: test_dict_operations.py:89:0 +pub fn test() { + test_dict_creation() + test_dict_access() + test_dict_modification() + test_dict_deletion() + test_dict_keys_values_items() + test_dict_update() + test_dict_comprehension() + test_dict_fromkeys() + test_dict_setdefault() + test_dict_membership() +} + +fn main() { + // @line: test_dict_operations.py:101:0 + // if __name__ == '__main__': + test() +} \ No newline at end of file diff --git a/py2v_transpiler/tests/input/transpile/test_dict_operations_helpers.v b/py2v_transpiler/tests/input/transpile/test_dict_operations_helpers.v new file mode 100644 index 00000000..d004f148 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_dict_operations_helpers.v @@ -0,0 +1,124 @@ +module main + +pub struct NoneType {} + +pub fn (n NoneType) str() string { + return 'None' +} + +pub struct Interpolation { +pub: + value Any + expression string + conversion string + format_spec string +} + +pub struct Template { +pub: + strings []string + interpolations []Interpolation +} + +pub fn (t Template) values() []Any { + mut res := []Any{cap: t.interpolations.len} + for i in t.interpolations { + res << i.value + } + return res +} + +pub fn (t1 Template) + (t2 Template) Template { + if t1.strings.len == 0 { return t2 } + if t2.strings.len == 0 { return t1 } + mut new_strings := t1.strings[..t1.strings.len - 1].clone() + new_strings << t1.strings.last() + t2.strings[0] + if t2.strings.len > 1 { + new_strings << t2.strings[1..] + } + mut new_interpolations := t1.interpolations.clone() + new_interpolations << t2.interpolations + return Template{ + strings: new_strings + interpolations: new_interpolations + } +} + +pub type Any = Interpolation | NoneType | Template | []Any | []u8 | bool | f64 | i64 | int | map[string]Any | string + +pub enum PyAnnotationFormat { value forwardref string } + +pub fn py_get_type_hints[T]() map[string]string { + mut hints := map[string]string{} + $for field in T.fields { + hints[field.name] = field.typ + } + return hints +} + +pub fn py_get_type_hints_generic(obj Any) map[string]string { + return map[string]string{} +} + +struct PyGeneratorInput { + val Any + is_exc bool + exc_msg string +} +struct PyGenerator[T] { +mut: + out chan T + in_ chan PyGeneratorInput + open bool = true +} + +fn (mut g PyGenerator[T]) next() ?T { + if !g.open { return none } + g.in_ <- PyGeneratorInput{val: 0} // Send dummy value + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) send(val Any) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{val: val} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) throw(msg string) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{is_exc: true, exc_msg: msg} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) close() { + g.open = false + g.in_.close() + // g.out will be closed by the generator function loop when it detects in_ closed or panic +} +fn py_yield[T](ch_out chan T, ch_in chan PyGeneratorInput, val T) Any { + ch_out <- val + inp := <-ch_in + if inp.is_exc { + panic(inp.exc_msg) + } + return inp.val +} +//##LLM@@ String formatting for bytes is stubbed and might be incorrect. Please implement proper bytes formatting or use V string interpolation. +fn py_bytes_format(fmt []u8, args Any) []u8 { + // Simplistic implementation for b'%s' % b'val' + // Converts bytes to string, formats, and converts back. + // This is not efficient or correct for non-ASCII bytes but works for simple cases. + fmt_str := fmt.bytestr() + // TODO: handle args properly. V's string interpolation/formatting expects distinct args. + // If args is []u8, treat as string. + arg_str := if args is []u8 { args.bytestr() } else { '${args}' } + + // Manual substitution of %s + // V does not have sprintf for runtime strings easily available in core without C interop. + // Simple replace for %s + res := fmt_str.replace('%s', arg_str) + return res.bytes() +} diff --git a/py2v_transpiler/tests/input/transpile/test_exceptions.v b/py2v_transpiler/tests/input/transpile/test_exceptions.v new file mode 100644 index 00000000..a4b939f2 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_exceptions.v @@ -0,0 +1,239 @@ +module main + +import div72.vexc + +// @line: test_exceptions.py:1:0 +pub fn test_basic_try_except() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + mut result := 10 / 2 + println('Result: ${result}') + vexc.end_try() + } else { + py_exc_1 := vexc.get_curr_exc() + if py_exc_1.name == 'ZeroDivisionError' { + println('Division by zero!') + } + else { + vexc.raise(py_exc_1.name, py_exc_1.msg) + } + } +} +// @line: test_exceptions.py:8:0 +pub fn test_multiple_except() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + value := 'not a number'.int() + vexc.end_try() + } else { + py_exc_3 := vexc.get_curr_exc() + if py_exc_3.name == 'ValueError' { + println('ValueError caught') + } + else if py_exc_3.name == 'TypeError' { + println('TypeError caught') + } + else { + vexc.raise(py_exc_3.name, py_exc_3.msg) + } + } +} +// @line: test_exceptions.py:16:0 +pub fn test_except_with_as() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + mut result := 10 / 0 + vexc.end_try() + } else { + py_exc_5 := vexc.get_curr_exc() + if py_exc_5.name == 'ZeroDivisionError' { + e := py_exc_5 + println('Caught exception: ${e}') + } + else { + vexc.raise(py_exc_5.name, py_exc_5.msg) + } + } +} +// @line: test_exceptions.py:22:0 +pub fn test_else_clause() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + mut py_success_6 := false + if C.try() { + mut result := 10 / 2 + py_success_6 = true + vexc.end_try() + } else { + py_exc_7 := vexc.get_curr_exc() + if py_exc_7.name == 'ZeroDivisionError' { + println('Division by zero') + } + else { + vexc.raise(py_exc_7.name, py_exc_7.msg) + } + } + if py_success_6 { + println('Division successful: ${result}') + } +} +// @line: test_exceptions.py:30:0 +pub fn test_finally_clause() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + { + defer { + println('Finally block always executes') + } + if C.try() { + println('Trying...') + mut result := 10 / 2 + vexc.end_try() + } else { + py_exc_9 := vexc.get_curr_exc() + if py_exc_9.name == 'ZeroDivisionError' { + println('Error') + } + else { + vexc.raise(py_exc_9.name, py_exc_9.msg) + } + } + } +} +// @line: test_exceptions.py:39:0 +pub fn test_raise_exception() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + vexc.raise('ValueError', 'Custom error message') + vexc.end_try() + } else { + py_exc_11 := vexc.get_curr_exc() + if py_exc_11.name == 'ValueError' { + e := py_exc_11 + println('Caught: ${e}') + } + else { + vexc.raise(py_exc_11.name, py_exc_11.msg) + } + } +} +// @line: test_exceptions.py:45:0 +pub fn test_raise_with_cause() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + 'invalid'.int() + vexc.end_try() + } else { + py_exc_14 := vexc.get_curr_exc() + if py_exc_14.name == 'ValueError' { + e := py_exc_14 + vexc.raise('TypeError', 'Conversion failed') + } + else { + vexc.raise(py_exc_14.name, py_exc_14.msg) + } + } + vexc.end_try() + } else { + py_exc_15 := vexc.get_curr_exc() + if py_exc_15.name == 'TypeError' { + e := py_exc_15 + println('Caught with cause: ${e}') + println('__cause__: ${e.__cause__}') + } + else { + vexc.raise(py_exc_15.name, py_exc_15.msg) + } + } +} +// @line: test_exceptions.py:55:0 +pub fn test_assert_statement() { + x := 5 + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + assert x == 5 + println('Assertion passed') + vexc.end_try() + } else { + py_exc_17 := vexc.get_curr_exc() + if py_exc_17.name == 'AssertionError' { + e := py_exc_17 + println('Assertion failed: ${e}') + } + else { + vexc.raise(py_exc_17.name, py_exc_17.msg) + } + } +} +// @line: test_exceptions.py:63:0 +pub fn test_nested_exceptions() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + vexc.raise('ValueError', 'Inner error') + vexc.end_try() + } else { + py_exc_20 := vexc.get_curr_exc() + if py_exc_20.name == 'ValueError' { + vexc.raise('RuntimeError', 'Outer error') + } + else { + vexc.raise(py_exc_20.name, py_exc_20.msg) + } + } + vexc.end_try() + } else { + py_exc_21 := vexc.get_curr_exc() + if py_exc_21.name == 'RuntimeError' { + e := py_exc_21 + println('Caught outer: ${e}') + } + else { + vexc.raise(py_exc_21.name, py_exc_21.msg) + } + } +} +// @line: test_exceptions.py:72:0 +pub fn test_exception_in_function() { +// @line: test_exceptions.py:73:4 + mut divide := fn (a Any, b Any) Any { + if b == 0 { + vexc.raise('ZeroDivisionError', 'Cannot divide by zero') + } + return a / b + } + //##LLM@@ Python try/except/finally block detected. V uses Result/Option types for error handling. Please refactor this function to return a Result (!Type) or Option (?Type), and handle errors using V's 'or { ... }' or '?' syntax. + if C.try() { + mut result := divide(10, 0) + vexc.end_try() + } else { + py_exc_23 := vexc.get_curr_exc() + if py_exc_23.name == 'ZeroDivisionError' { + e := py_exc_23 + println('Function exception: ${e}') + } + else { + vexc.raise(py_exc_23.name, py_exc_23.msg) + } + } +} +// @line: test_exceptions.py:83:0 +pub fn test() { + test_basic_try_except() + test_multiple_except() + test_except_with_as() + test_else_clause() + test_finally_clause() + test_raise_exception() + test_raise_with_cause() + test_assert_statement() + test_nested_exceptions() + test_exception_in_function() +} + +fn main() { + // @line: test_exceptions.py:95:0 + // if __name__ == '__main__': + test() +} \ No newline at end of file diff --git a/py2v_transpiler/tests/input/transpile/test_exceptions_helpers.v b/py2v_transpiler/tests/input/transpile/test_exceptions_helpers.v new file mode 100644 index 00000000..d004f148 --- /dev/null +++ b/py2v_transpiler/tests/input/transpile/test_exceptions_helpers.v @@ -0,0 +1,124 @@ +module main + +pub struct NoneType {} + +pub fn (n NoneType) str() string { + return 'None' +} + +pub struct Interpolation { +pub: + value Any + expression string + conversion string + format_spec string +} + +pub struct Template { +pub: + strings []string + interpolations []Interpolation +} + +pub fn (t Template) values() []Any { + mut res := []Any{cap: t.interpolations.len} + for i in t.interpolations { + res << i.value + } + return res +} + +pub fn (t1 Template) + (t2 Template) Template { + if t1.strings.len == 0 { return t2 } + if t2.strings.len == 0 { return t1 } + mut new_strings := t1.strings[..t1.strings.len - 1].clone() + new_strings << t1.strings.last() + t2.strings[0] + if t2.strings.len > 1 { + new_strings << t2.strings[1..] + } + mut new_interpolations := t1.interpolations.clone() + new_interpolations << t2.interpolations + return Template{ + strings: new_strings + interpolations: new_interpolations + } +} + +pub type Any = Interpolation | NoneType | Template | []Any | []u8 | bool | f64 | i64 | int | map[string]Any | string + +pub enum PyAnnotationFormat { value forwardref string } + +pub fn py_get_type_hints[T]() map[string]string { + mut hints := map[string]string{} + $for field in T.fields { + hints[field.name] = field.typ + } + return hints +} + +pub fn py_get_type_hints_generic(obj Any) map[string]string { + return map[string]string{} +} + +struct PyGeneratorInput { + val Any + is_exc bool + exc_msg string +} +struct PyGenerator[T] { +mut: + out chan T + in_ chan PyGeneratorInput + open bool = true +} + +fn (mut g PyGenerator[T]) next() ?T { + if !g.open { return none } + g.in_ <- PyGeneratorInput{val: 0} // Send dummy value + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) send(val Any) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{val: val} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) throw(msg string) ?T { + if !g.open { panic('StopIteration') } + g.in_ <- PyGeneratorInput{is_exc: true, exc_msg: msg} + res := <-g.out + if res == none { g.open = false } + return res +} +fn (mut g PyGenerator[T]) close() { + g.open = false + g.in_.close() + // g.out will be closed by the generator function loop when it detects in_ closed or panic +} +fn py_yield[T](ch_out chan T, ch_in chan PyGeneratorInput, val T) Any { + ch_out <- val + inp := <-ch_in + if inp.is_exc { + panic(inp.exc_msg) + } + return inp.val +} +//##LLM@@ String formatting for bytes is stubbed and might be incorrect. Please implement proper bytes formatting or use V string interpolation. +fn py_bytes_format(fmt []u8, args Any) []u8 { + // Simplistic implementation for b'%s' % b'val' + // Converts bytes to string, formats, and converts back. + // This is not efficient or correct for non-ASCII bytes but works for simple cases. + fmt_str := fmt.bytestr() + // TODO: handle args properly. V's string interpolation/formatting expects distinct args. + // If args is []u8, treat as string. + arg_str := if args is []u8 { args.bytestr() } else { '${args}' } + + // Manual substitution of %s + // V does not have sprintf for runtime strings easily available in core without C interop. + // Simple replace for %s + res := fmt_str.replace('%s', arg_str) + return res.bytes() +} diff --git a/transpilation_report.md b/transpilation_report.md new file mode 100644 index 00000000..51309476 --- /dev/null +++ b/transpilation_report.md @@ -0,0 +1,38 @@ +# Отчет о трансляции Python в V (py2v) + +Этот отчет содержит результаты трансляции пяти файлов тестов и выявленные ошибки/ограничения. + +## 1. Список транслированных файлов +- `test_classes_inheritance.py` +- `test_comprehensions.py` +- `test_decorators.py` +- `test_dict_operations.py` +- `test_exceptions.py` + +## 2. Выявленные ошибки и предупреждения + +### Общие проблемы +- **Mypy Dependency**: Транспиляция требует наличия `mypy` для корректного вывода типов. Без него многие типы определяются как `Any`. +- **C.try() в исключениях**: Транспилятор использует `if C.try() { ... } else { ... }` для обработки исключений. Это не является идиоматичным для V и может потребовать внешней библиотеки `div72.vexc` или C-интеропа. + +### test_decorators.py +- **Порядок вариативных аргументов**: V требует, чтобы вариативный параметр (`...args`) был последним. Транспилятор генерирует `fn (args ...Any, kwargs map[string]string)`, что вызовет ошибку компиляции в V. +- **Ограниченная поддержка декораторов**: Выдаются предупреждения: `Warning: Custom decorator '...' is not fully supported and might generate invalid code.`. Кастомные декораторы транслируются в анонимные функции, но их использование как декораторов (`@decorator`) в V не поддерживается напрямую. + +### test_dict_operations.py +- **Отсутствующие методы map**: В V встроенный тип `map` не имеет методов `fromkeys`, `setdefault`, `update`, `pop`. Транспилятор генерирует вызовы вроде `d.update(d2)`, `dict.fromkeys(keys, 0)`, которые не скомпилируются без дополнительных хелперов. +- **Инициализация из списка пар**: Конструкция `map[string]Any([['x', 10], ['y', 20]])` не является валидным синтаксисом инициализации карты в V. + +### test_comprehensions.py +- **Вложенные списковые включения**: Для сложных вложенных конструкций выдается рекомендация: `##LLM@@ Complex nested comprehension detected. To ensure readability and idiomatic V, please unfold this into explicit 'for' loops...`. +- **Типизация матриц**: В `test_list_comprehension` переменная `matrix` была инициализирована как `[]int`, хотя она должна быть `[][]int` (список списков). + +### test_classes_inheritance.py +- **isinstance и case-sensitivity**: В `test_isinstance_issubclass` трансляция `isinstance(dog, Dog)` превратилась в `dog is dog`, что неверно (должно быть `dog is Dog`). Также `issubclass` не является встроенной функцией V. + +## 3. Рекомендации (Issues) +1. **Исправить порядок аргументов**: В генераторе функций для V всегда помещать `...args` в конец списка параметров. +2. **Реализовать хелперы для dict**: Добавить реализации `py_dict_update`, `py_dict_setdefault` и т.д. в файл хелперов. +3. **Улучшить трансляцию isinstance/issubclass**: Обеспечить правильный регистр имен классов и добавить поддержку проверки иерархии типов. +4. **Улучшить вывод типов для вложенных структур**: Исправить ошибку, при которой вложенные списки получают плоский тип (например, `[]int` вместо `[][]int`). +5. **Миграция с C.try**: Рассмотреть возможность использования нативных для V `Result/Option` (`!`/`?`) вместо эмуляции исключений Python через C.