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