1+ from __future__ import annotations
12import inspect
23import types
34import csv
5+ from typing import Dict
46
57__version__ = "0.1.1"
68
@@ -12,13 +14,6 @@ def string_handle(*args, **kwargs):
1214 return string_handle
1315
1416
15- def expand_newlines (lst_in ):
16- new_list = []
17- for line in lst_in :
18- new_list .extend (line .replace ('\t ' , ' ' ).split ('\n ' ))
19- return new_list
20-
21-
2217type_handler = {
2318 int : string_template ("\t mp_int_t {0} = mp_obj_get_int({0}_obj);" ),
2419 float : string_template ("\t mp_float_t {0} = mp_obj_get_float({0}_obj);" ),
@@ -33,6 +28,267 @@ def expand_newlines(lst_in):
3328 object : string_template ("\t mp_obj_t {0} args[ARG_{0}].u_obj;" )
3429}
3530
31+ return_type_handler = {
32+ int : "\t mp_int_t ret_val;" ,
33+ float : "\t mp_float_t ret_val;" ,
34+ bool : "\t bool ret_val;" ,
35+ str : "" ,
36+ # tuple: string_template(
37+ # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
38+ # list: string_template(
39+ # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
40+ # set: string_template(
41+ # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
42+ None : ""
43+ }
44+
45+ return_handler = {
46+ int : "\t return mp_obj_new_int(ret_val);" ,
47+ float : "\t return mp_obj_new_float(ret_val);" ,
48+ bool : "\t return mp_obj_new_bool(ret_val);" ,
49+ str : "\t return mp_obj_new_str(<ret_val_ptr>, <ret_val_len>);" ,
50+ None : "\t return mp_const_none;"
51+ }
52+
53+ shortened_types = {
54+ int : "int" ,
55+ object : "obj" ,
56+ None : "null" ,
57+ bool : "bool" ,
58+ }
59+
60+
61+ def expand_newlines (lst_in ):
62+ new_list = []
63+ for line in lst_in :
64+ new_list .extend (line .replace ('\t ' , ' ' ).split ('\n ' ))
65+ return new_list
66+
67+
68+ class BaseContainer :
69+ def load_c (self , input : str ):
70+ """
71+ :param input: String of c source
72+ :return: self
73+ """
74+ return self
75+
76+ def load_python (self , input ) -> BaseContainer :
77+ """
78+ :param input: Python Object
79+ :return: self
80+ """
81+ return self
82+
83+ def to_c (self ):
84+ """
85+ Parse the container into c source code
86+ :return:
87+ """
88+
89+ def to_python (self ):
90+ """
91+ Parse the container into python objects
92+ :return:
93+ """
94+
95+
96+ class ModuleContainer :
97+ def __init__ (self ):
98+ self .headers = ['#include "py/obj.h"' , '#include "py/runtime.h"' , '#include "py/builtin.h"' ]
99+ self .functions = []
100+
101+
102+ class FunctionContainer (BaseContainer ):
103+ return_type_handler = {
104+ int : "mp_int_t ret_val;" ,
105+ float : "mp_float_t ret_val;" ,
106+ bool : "bool ret_val;" ,
107+ str : None ,
108+ # tuple: string_template(
109+ # "mp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
110+ # list: string_template(
111+ # "mp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
112+ # set: string_template(
113+ # "mp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
114+ None : None
115+ }
116+ return_handler = {
117+ int : "return mp_obj_new_int(ret_val);" ,
118+ float : "return mp_obj_new_float(ret_val);" ,
119+ bool : "return mp_obj_new_bool(ret_val);" ,
120+ str : "return mp_obj_new_str(<ret_val_ptr>, <ret_val_len>);" ,
121+ None : "return mp_const_none;"
122+ }
123+
124+ def __init__ (self ):
125+ self .comments = None
126+ self .name = None
127+ self .module = None
128+ self .parameters : ParametersContainer = ParametersContainer ()
129+ self .code = None
130+ self .return_type = None
131+ self .return_value = None
132+ self .signature = None
133+
134+ def load_python (self , input ) -> FunctionContainer :
135+ """
136+ :param input: Function to parse
137+ :return:
138+ """
139+ self .comments = input .__doc__
140+ self .name = input .__name__
141+ self .module = input .__module__
142+ self .signature = inspect .signature (input )
143+ self .parameters .load_python (self .signature .parameters )
144+ self .return_type = inspect .signature (input ).return_annotation
145+ return self
146+
147+ def to_c_comments (self ):
148+ """
149+ Uses single line comments as we can't know if there are string escapes such as /* in the code
150+ :param f:
151+ :return:
152+ """
153+ if self .comments :
154+ return '\n ' .join (["//" + line .strip () for line in self .comments .splitlines () if line .strip ()])
155+
156+ def to_c_func_def (self ):
157+ return f"STATIC mp_obj_t { self .module } _{ self .name } "
158+
159+ def to_c_return_val_init (self ):
160+ return self .return_type_handler [self .return_type ]
161+
162+ def to_c_code_body (self ):
163+ if self .code :
164+ return self .code
165+ else :
166+ return "//Your code here"
167+
168+ def to_c_return_value (self ):
169+ if self .return_value is not None :
170+ return self .return_value
171+ else :
172+ return self .return_handler .get (self .return_type )
173+
174+ def to_c_define (self ):
175+ if self .parameters .type == "positional" :
176+ return f"MP_DEFINE_CONST_FUN_OBJ_{ self .parameters .count } (" \
177+ f"{ self .module } _{ self .name } _obj, { self .module } _{ self .name } );"
178+ elif self .parameters .type == "between" :
179+ return f"MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(" \
180+ f"{ self .module } _{ self .name } _obj, " \
181+ f"{ self .parameters .count } , { self .parameters .count } , { self .module } _{ self .name } );"
182+ elif self .parameters .type == "keyword" :
183+ return f"MP_DEFINE_CONST_FUN_OBJ_KW({ self .module } _{ self .name } _obj, 1, { self .module } _{ self .name } );"
184+
185+ def to_c_arg_array_def (self ):
186+ if self .parameters .type == "keyword" :
187+ return f"STATIC const mp_arg_t { self .module } _{ self .name } _allowed_args[]"
188+
189+ def to_c (self ):
190+ # TODO work on formatter
191+ resp = self .to_c_comments ()
192+ resp += "\n "
193+ resp += f"{ self .to_c_func_def ()} ({ self .parameters .to_c_input ()} ) {{\n "
194+ resp += " " + "\n " .join (self .parameters .to_c_init ().splitlines ()) + "\n "
195+ if self .to_c_return_val_init ():
196+ resp += " " + self .to_c_return_val_init ()
197+ resp += f"\n \n { self .to_c_code_body ()} \n \n "
198+ resp += f" { self .to_c_return_value ()} \n "
199+ resp += "}\n "
200+ resp += self .to_c_define ()
201+ return resp
202+
203+
204+ class ParametersContainer (BaseContainer ):
205+ type_handler = {
206+ int : string_template ("mp_int_t {0} = mp_obj_get_int({0}_obj);" ),
207+ float : string_template ("mp_float_t {0} = mp_obj_get_float({0}_obj);" ),
208+ bool : string_template ("bool {0} = mp_obj_is_true({0}_obj);" ),
209+ str : string_template ("const char* {0} = mp_obj_str_get_str({0}_obj);" ),
210+ tuple : string_template (
211+ "mp_obj_t *{0} = NULL;\n size_t {0}_len = 0;\n mp_obj_get_array({0}_arg, &{0}_len, &{0});" ),
212+ list : string_template (
213+ "mp_obj_t *{0} = NULL;\n size_t {0}_len = 0;\n mp_obj_get_array({0}_arg, &{0}_len, &{0});" ),
214+ set : string_template (
215+ "mp_obj_t *{0} = NULL;\n size_t {0}_len = 0;\n mp_obj_get_array({0}_arg, &{0}_len, &{0});" ),
216+ object : string_template ("\t mp_obj_t {0} args[ARG_{0}].u_obj;" )
217+ }
218+
219+ def __init__ (self ):
220+ self .type = ""
221+ self .count = 0
222+ self .parameters = None
223+
224+ def load_python (self , input : Dict [str , inspect .Parameter ]) -> ParametersContainer :
225+ self .parameters = input
226+ self .count = len (self .parameters )
227+ simple = all ([param .kind == param .POSITIONAL_OR_KEYWORD for param in self .parameters .values ()])
228+ if simple and self .count < 4 :
229+ self .type = "positional"
230+ elif simple :
231+ self .type = "between"
232+ else :
233+ self .type = "keyword"
234+ return self
235+
236+ def to_c_enums (self ):
237+ if self .type != "keyword" :
238+ return None
239+ return f"enum {{ { ', ' .join (['ARG_' + k for k in self .parameters ])} }};"
240+
241+ def to_c_kw_allowed_args (self ):
242+ if self .type == "keyword" :
243+ args = []
244+ for name , param in self .parameters .items ():
245+ if param .kind in [param .POSITIONAL_OR_KEYWORD , param .POSITIONAL_ONLY ]:
246+ arg_type = "MP_ARG_REQUIRED"
247+ else :
248+ arg_type = "MP_ARG_KW_ONLY"
249+ type_txt = shortened_types [param .annotation ]
250+ if param .default is inspect ._empty :
251+ default = ""
252+ elif param .default is None :
253+ default = f"{{ .u_{ type_txt } = MP_OBJ_NULL }}"
254+ else :
255+ default = f"{{ .u_{ type_txt } = { param .default } }}"
256+ args .append (f"{{ MP_QSTR_{ name } , { arg_type } | MP_ARG_{ type_txt .upper ()} , { default } }}," )
257+ return args
258+ # return f"\tSTATIC const mp_arg_t {f.__module__}_{f.__name__}_allowed_args[] = {{\n\t\t{args}\n\t}};"
259+
260+ def to_c_arg_array (self ):
261+ if self .type != "keyword" :
262+ return None
263+
264+ def to_c_kw_arg_unpack (self ):
265+ if self .type != "keyword" :
266+ return None
267+
268+ def to_c_input (self ):
269+ if self .type == "positional" :
270+ return ", " .join ([f"mp_obj_t { x } _obj" for x in self .parameters ])
271+ elif self .type == "between" :
272+ return "size_t n_args, const mp_obj_t *args"
273+ elif self .type == "keyword" :
274+ # Complex case
275+ return "size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {"
276+
277+ def to_c_init (self ):
278+ if self .type == "keyword" :
279+ return "\n " .join ([self .to_c_enums (),
280+ self .to_c_kw_allowed_args (),
281+ "" ,
282+ self .to_c_arg_array (),
283+ "" ,
284+ self .to_c_kw_arg_unpack ()])
285+ else :
286+ return "\n " .join ([self .type_handler [value .annotation ](param ) for param , value in self .parameters .items ()])
287+
288+
289+ class ReturnContainer (BaseContainer ):
290+ pass
291+
36292
37293def stub_function (f ):
38294 # Function implementation
@@ -87,29 +343,6 @@ def function_init(func_name):
87343 return f"STATIC mp_obj_t { func_name } ("
88344
89345
90- return_type_handler = {
91- int : "\t mp_int_t ret_val;" ,
92- float : "\t mp_float_t ret_val;" ,
93- bool : "\t bool ret_val;" ,
94- str : "" ,
95- # tuple: string_template(
96- # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
97- # list: string_template(
98- # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
99- # set: string_template(
100- # "\tmp_obj_t *{0} = NULL;\n\tsize_t {0}_len = 0;\n\tmp_obj_get_array({0}_arg, &{0}_len, &{0});"),
101- None : ""
102- }
103-
104- return_handler = {
105- int : "\t return mp_obj_new_int(ret_val);" ,
106- float : "\t return mp_obj_new_float(ret_val);" ,
107- bool : "\t return mp_obj_new_bool(ret_val);" ,
108- str : "\t return mp_obj_new_str(<ret_val_ptr>, <ret_val_len>);" ,
109- None : "\t return mp_const_none;"
110- }
111-
112-
113346def ret_val_init (ret_type ):
114347 return return_type_handler [ret_type ]
115348
@@ -136,14 +369,6 @@ def kw_enum(params):
136369 return f"\t enum {{ { ', ' .join (['ARG_' + k for k in params ])} }};"
137370
138371
139- shortened_types = {
140- int : "int" ,
141- object : "obj" ,
142- None : "null" ,
143- bool : "bool" ,
144- }
145-
146-
147372def kw_allowed_args (f , params ):
148373 args = []
149374 for name , param in params .items ():
0 commit comments