1 A Systems Programming Language Target for Micropython
2 =====================================================
3
4 Python-compatible syntax for processing using the compiler module.
5
6 The principal focus is on specific machine code generation and not
7 analysis. Thus, only block generation, address reference generation,
8 temporary storage administration and other code generation tasks are to be
9 left to the systems programming language compiler.
10
11 Special Functions
12 -----------------
13
14 In syspython, the function invocation notation is reserved to specify
15 primitive operations such as attribute access and actual function invocations,
16 with the latter being expressed as follows:
17
18 fn(y) # original Python
19 apply(fn, y) # syspython
20
21 Thus, in syspython, whenever the invocation notation is used, the target of
22 the invocation is always a special function and not a general Python function
23 or method. Note that the apply function resembles the Python function of the
24 same name but is not actually that particular function.
25
26 A family of special functions for invocations exists, addressing optimisation
27 situations identified by program analysis:
28
29 apply # general invocation
30 applyclass # direct invocation of an instantiator
31 applyfunction # function-specific invocation
32 applystaticmethod # specific invocation of a method via a class
33 applymethod # specific invocation of a method via self
34
35 Low-Level Code
36 --------------
37
38 Most Python-level program code should be wrapped in special function
39 invocations, and as a result other syntax features might be used to express
40 low-level concepts. Low-level operations may also be expressed using other
41 special functions. For example:
42
43 storelocal(element, loadobjtable(loadattr(obj, classcode), attrcode))
44
45 Here, element holds the raw data provided by the table access involving a base
46 defined by the classcode of an object and an offset defined by the supplied
47 attrcode.
48
49 Note that all low-level functions deal only with addresses and offsets, not
50 symbols. In the above example, loadattr combines the address of obj with the
51 symbol classcode whose actual value must be substituted by the compiler.
52 However, the loadobjtable function requires a genuine offset value for the
53 classcode (which is why loadattr is being used to obtain it), and a genuine
54 offset for the attrcode (which is provided directly).
55
56 Program Data and Data Structure Definition
57 ------------------------------------------
58
59 Given that micropython has already deduced object and parameter details,
60 such information must be communicated in the systems programming language
61 so that the compiler does not have to deduce it again.
62
63 Explicit constant declaration shall be done at the start of the main
64 module:
65
66 constants(...)
67
68 Each module may feature keyword arguments, and a list of such names is
69 provided as follows:
70
71 keywords(...)
72
73 Explicit structure declaration is still performed using class statements,
74 but base classes are omitted and attributes are declared explicitly as
75 follows:
76
77 class C:
78 instattrs(member...)
79 classattrs(member...)
80
81 Other object table information, such as inherited class attributes and
82 class compatibility (to support isinstance) are also declared explicitly:
83
84 inherited(superclass, member...)
85 descendants(class...)
86
87 Other than function definitions, no other code statements shall appear in
88 class definitions; such statements will appear after classes have been
89 defined.
90
91 For classes in the module namespace or within other classes, the __main__
92 function collects together all "loose" (module-level) statements; class
93 attribute assignments will occur in the __main__ function, and where a name
94 is associated with a function definition and another object, the function will
95 also be explicitly assigned in the __main__ function using its full name.
96
97 For classes in function namespaces, the containing function could contain the
98 "loose" statements at the point at which the class appears. However, such
99 classes are not currently supported in micropython.
100
101 Any class or function defined once in a namespace need not be assigned to that
102 namespace in the __main__ function, but where multiple definitions exist and
103 program logic determines which definition prevails, such definitions must be
104 assigned in the __main__ function.
105
106 For example:
107
108 class C:
109 def method(self, ...):
110 ...
111 if something:
112 method = something
113
114 This is represented as follows:
115
116 class C:
117 ...
118 def method(self, ...):
119 ...
120
121 def __main__():
122 globalnames(...)
123 ...
124 if something:
125 storeattr(module.C, method, something)
126
127 Imports
128 -------
129
130 Imports act as invocations of module code and name assignments within a
131 particular scope and are defined as follows:
132
133 # import package
134 package.__main__()
135 storelocal(package, static(package))
136
137 # import package.module
138 package.__main__()
139 package.module.__main__()
140 storelocal(package, static(package))
141
142 # from package.module import cls
143 package.__main__()
144 package.module.__main__()
145 storelocal(cls, loadattribute(package.module, cls)) # see below
146
147 Since import statements can appear in code that may be executed more than
148 once, __main__ functions should test and set a flag indicating whether the
149 function has already been called.
150
151 Python would arguably be more sensible as a language if imports were
152 processed separately, but this would then rule out logic controlling the
153 use of modules.
154
155 Name and Attribute Declarations
156 -------------------------------
157
158 Assignments and name usage involve locals and globals but usage is declared
159 explicitly:
160
161 localnames(...)
162
163 At the function level, locals are genuine local name definitions whereas
164 globals refer to module globals:
165
166 globalnames(...)
167
168 At the module level, locals are effectively equivalent to module globals and
169 are declared as such.
170
171 Each module's __main__ function will declare any referenced module globals as
172 globals. Note that the __main__ function is not a genuine attribute of any
173 module but an internal construct used to initialise modules appropriately.
174
175 Such declarations must appear first in a program unit (module, function).
176 For example:
177
178 def f(a, b):
179 localnames(a, b, x, y)
180 globalnames(f, g)
181
182 storelocal(x, 1)
183 storelocal(y, x)
184 storelocal(a, b)
185 storeattr(module, g, f)
186
187 Assignments
188 -----------
189
190 Since assignments can rebind names used in the value expression, the evaluated
191 expression must be captured and referenced when setting the targets. This is
192 done using the special $expr variable, and so the swap assignment...
193
194 a, b = b, a
195
196 ...would be written (more or less) as...
197
198 $expr = (b, a)
199 storelocal(a, apply(operator.getitem, $expr, 0))
200 storelocal(b, apply(operator.getitem, $expr, 1))
201
202 Names and Attributes
203 --------------------
204
205 Bare names refer to locals or globals according to the localnames and
206 globalnames declarations, or to constants such as None, True, False and
207 NotImplemented. Storage of local or global names is done using explicit
208 functions as follows:
209
210 storelocal(name, value)
211 storeattr(module, name, value) # see below
212
213 No operator usage: all operators are converted to invocations, including
214 all attribute access except static references to modules or particular class
215 or function definitions using the following notation:
216
217 static(package)
218 static(package.module)
219 static(package.module.cls)
220 static(package.module.cls.function)
221
222 A shorthand dot notation could be employed:
223
224 package.module
225 package.module.cls
226 package.module.cls.function
227
228 Where multiple definitions of static objects occur, the dot notation cannot be
229 used, and the full name of such definitions must be quoted. For example:
230
231 static("package.module.cls#1.function")
232
233 In general, attribute access must use an explicit function indicating the
234 kind of access operation being performed. For example:
235
236 # Instance-related operations:
237
238 loadattr(obj, attrname) # preserve retrieved context
239
240 # Constant attribute operations:
241
242 static(value) # see above
243 loadconstant(value, obj) # replace context with obj
244
245 # Static attribute operations:
246
247 loadaddress(parent, attrname) # preserve retrieved context
248 loadaddresscontext(parent, attrname, obj) # replace context with obj
249 loadaddresscontextcond(parent, attrname, obj) # run-time context decision
250
251 # Unoptimised operations:
252
253 loadattrindex(obj, attrname) # preserve retrieved context
254 loadattrindexcontextcond(obj, attrname) # run-time context decision
255
256 # Instance-related operations:
257
258 storeattr(obj, attrname, value) # preserve context for value
259
260 # Static attribute operations:
261
262 storeaddress(parent, attrname, value) # preserve context for value
263 storeaddresscontext(parent, attrname, value, obj) # replace context with obj
264
265 # Unoptimised operations:
266
267 storeattrindex(obj, attrname, value) # preserve context for value
268
269 Recall that for loadattrindex family functions, the location of the attribute
270 is obtained from the object table and the nature of the attribute is
271 determined from the stored context value.
272
273 Temporary variables could employ similar functions:
274
275 loadtemp(0)
276 storetemp(0, value)
277
278 Operators and Invocations
279 -------------------------
280
281 Conventional operators use the operator functions.
282
283 Special operators could also use the operator functions (where available)
284 but might as well be supported directly:
285
286 __is__(a, b)
287 __is_not__(a, b)
288
289 Logical operators involving short-circuit evaluation could be represented
290 as function calls, but the evaluation semantics would be preserved:
291
292 __and__(...) # returns the first non-true value or the final value
293 __not__(obj) # returns the inverse of the boolean interpretation of obj
294 __or__(...) # returns the first true value or the final value
295
296 Comparisons could be rephrased in a verbose fashion:
297
298 a < b < c becomes lt(a, b) and lt(b, c)
299 or __and__(lt(a, b), lt(b, c))
300
301 Advanced Control-Flow
302 ---------------------
303
304 Any statements requiring control-flow definition in terms of blocks must
305 be handled in the language as the notions of labels and blocks are not
306 introduced earlier apart from the special case of jumping to another
307 callable (described below).
308
309 Special functions for low-level operations:
310
311 check(obj, type)
312 jump(callable)
313
314 Function/subroutine definition with entry points for checked and unchecked
315 parameters.
316
317 def fn_checked(self, ...):
318 check(self, Type) # raises a TypeError if not isinstance(self, Type)
319 jump(fn_unchecked) # preserves the frame and return address
320
321 def fn_unchecked(self, ...):
322 ...
323
324 The jump function might also be used for inlining appropriate functions.
325
326 Exceptions must also be handled in the language.
327
328 Object Type Detection
329 ---------------------
330
331 Occasionally, the type of an object (instance of a particular class, class,
332 and so on) needs to be determined at run-time:
333
334 isclass(obj)