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 Names and Attributes
182 --------------------
183
184 Bare names refer to locals or globals according to the localnames and
185 globalnames declarations, or to constants such as None, True, False and
186 NotImplemented. Storage of local or global names is done using explicit
187 functions as follows:
188
189 storelocal(name, value)
190 storeattr(module, name, value) # see below
191
192 No operator usage: all operators are converted to invocations, including
193 all attribute access except static references to modules or particular class
194 or function definitions using the following notation:
195
196 static(package)
197 static(package.module)
198 static(package.module.cls)
199 static(package.module.cls.function)
200
201 A shorthand dot notation could be employed:
202
203 package.module
204 package.module.cls
205 package.module.cls.function
206
207 Where multiple definitions of static objects occur, the dot notation cannot be
208 used, and the full name of such definitions must be quoted. For example:
209
210 static("package.module.cls#1.function")
211
212 In general, attribute access must use an explicit function indicating the
213 kind of access operation being performed. For example:
214
215 # Instance-related operations:
216
217 loadattr(obj, attrname) # preserve retrieved context
218
219 # Static attribute operations:
220
221 loadaddress(parent, attrname) # preserve retrieved context
222 loadaddresscontext(parent, attrname, obj) # replace context with obj
223 loadaddresscontextcond(parent, attrname, obj) # run-time context decision
224
225 # Unoptimised operations:
226
227 loadattrindex(obj, attrname) # preserve retrieved context
228 loadattrindexcontextcond(obj, attrname) # run-time context decision
229
230 # Instance-related operations:
231
232 storeattr(obj, attrname, value) # preserve context for value
233
234 # Static attribute operations:
235
236 storeaddress(parent, attrname, value) # preserve context for value
237 storeaddresscontext(parent, attrname, value, obj) # replace context with obj
238
239 # Unoptimised operations:
240
241 storeattrindex(obj, attrname, value) # preserve context for value
242
243 Recall that for loadattrindex family functions, the location of the attribute
244 is obtained from the object table and the nature of the attribute is
245 determined from the stored context value.
246
247 Temporary variables could employ similar functions:
248
249 loadtemp(0)
250 storetemp(0, value)
251
252 Operators and Invocations
253 -------------------------
254
255 Conventional operators use the operator functions.
256
257 Special operators could also use the operator functions (where available)
258 but might as well be supported directly:
259
260 __is__(a, b)
261 __is_not__(a, b)
262
263 Logical operators involving short-circuit evaluation could be represented
264 as function calls, but the evaluation semantics would be preserved:
265
266 __and__(...) # returns the first non-true value or the final value
267 __not__(obj) # returns the inverse of the boolean interpretation of obj
268 __or__(...) # returns the first true value or the final value
269
270 Comparisons could be rephrased in a verbose fashion:
271
272 a < b < c becomes lt(a, b) and lt(b, c)
273 or __and__(lt(a, b), lt(b, c))
274
275 Advanced Control-Flow
276 ---------------------
277
278 Any statements requiring control-flow definition in terms of blocks must
279 be handled in the language as the notions of labels and blocks are not
280 introduced earlier apart from the special case of jumping to another
281 callable (described below).
282
283 Special functions for low-level operations:
284
285 check(obj, type)
286 jump(callable)
287
288 Function/subroutine definition with entry points for checked and unchecked
289 parameters.
290
291 def fn_checked(self, ...):
292 check(self, Type) # raises a TypeError if not isinstance(self, Type)
293 jump(fn_unchecked) # preserves the frame and return address
294
295 def fn_unchecked(self, ...):
296 ...
297
298 The jump function might also be used for inlining appropriate functions.
299
300 Exceptions must also be handled in the language.
301
302 Object Type Detection
303 ---------------------
304
305 Occasionally, the type of an object (instance of a particular class, class,
306 and so on) needs to be determined at run-time:
307
308 isclass(obj)