paul@627 | 1 | A Systems Programming Language Target for Micropython
|
paul@627 | 2 | =====================================================
|
paul@627 | 3 |
|
paul@627 | 4 | Python-compatible syntax for processing using the compiler module.
|
paul@627 | 5 |
|
paul@627 | 6 | The principal focus is on specific machine code generation and not
|
paul@627 | 7 | analysis. Thus, only block generation, address reference generation,
|
paul@627 | 8 | temporary storage administration and other code generation tasks are to be
|
paul@627 | 9 | left to the systems programming language compiler.
|
paul@627 | 10 |
|
paul@636 | 11 | Program Data and Data Structure Definition
|
paul@636 | 12 | ------------------------------------------
|
paul@636 | 13 |
|
paul@627 | 14 | Given that micropython has already deduced object and parameter details,
|
paul@627 | 15 | such information must be communicated in the systems programming language
|
paul@627 | 16 | so that the compiler does not have to deduce it again.
|
paul@627 | 17 |
|
paul@627 | 18 | Explicit constant declaration shall be done at the start of the main
|
paul@627 | 19 | module:
|
paul@627 | 20 |
|
paul@627 | 21 | __constants__(...)
|
paul@627 | 22 |
|
paul@627 | 23 | Explicit structure declaration is still performed using class statements,
|
paul@627 | 24 | but base classes are omitted and attributes are declared explicitly as
|
paul@627 | 25 | follows:
|
paul@627 | 26 |
|
paul@627 | 27 | class C:
|
paul@627 | 28 | __instattrs__(member...)
|
paul@627 | 29 | __classattrs__(member...)
|
paul@627 | 30 |
|
paul@627 | 31 | Other object table information, such as inherited class attributes and
|
paul@627 | 32 | class compatibility (to support isinstance) are also declared explicitly:
|
paul@627 | 33 |
|
paul@627 | 34 | __inherited__(superclass, member...)
|
paul@627 | 35 | __descendants__(class...)
|
paul@627 | 36 |
|
paul@627 | 37 | Other than function definitions, no other code statements shall appear in
|
paul@627 | 38 | class definitions; such statements will appear after classes have been
|
paul@638 | 39 | defined.
|
paul@638 | 40 |
|
paul@638 | 41 | For classes in the module namespace or within other classes, the __main__
|
paul@638 | 42 | function collects together all "loose" (module-level) statements; class
|
paul@638 | 43 | attribute assignments will occur in the __main__ function, and where a name
|
paul@638 | 44 | is associated with a function definition and another object, the function will
|
paul@638 | 45 | also be explicitly assigned in the __main__ function using its full name.
|
paul@638 | 46 |
|
paul@638 | 47 | For classes in function namespaces, the containing function could contain the
|
paul@638 | 48 | "loose" statements at the point at which the class appears. However, such
|
paul@638 | 49 | classes are not currently supported in micropython.
|
paul@637 | 50 |
|
paul@637 | 51 | Any class or function defined once in a namespace need not be assigned to that
|
paul@637 | 52 | namespace in the __main__ function, but where multiple definitions exist and
|
paul@637 | 53 | program logic determines which definition prevails, such definitions must be
|
paul@637 | 54 | assigned in the __main__ function.
|
paul@637 | 55 |
|
paul@637 | 56 | For example:
|
paul@637 | 57 |
|
paul@637 | 58 | class C:
|
paul@637 | 59 | def method(self, ...):
|
paul@637 | 60 | ...
|
paul@637 | 61 | if something:
|
paul@637 | 62 | method = something
|
paul@637 | 63 |
|
paul@637 | 64 | This is represented as follows:
|
paul@637 | 65 |
|
paul@637 | 66 | class C:
|
paul@637 | 67 | ...
|
paul@637 | 68 | def method(self, ...):
|
paul@637 | 69 | ...
|
paul@637 | 70 |
|
paul@637 | 71 | def __main__():
|
paul@638 | 72 | __globalnames__(...)
|
paul@637 | 73 | ...
|
paul@637 | 74 | method = module.C.method
|
paul@637 | 75 | if something:
|
paul@637 | 76 | __storeaddress__(module.C, something)
|
paul@627 | 77 |
|
paul@636 | 78 | Imports
|
paul@636 | 79 | -------
|
paul@636 | 80 |
|
paul@627 | 81 | Imports act as invocations of module code and name assignments within a
|
paul@627 | 82 | particular scope and are defined as follows:
|
paul@627 | 83 |
|
paul@627 | 84 | # import package
|
paul@627 | 85 | package.__main__()
|
paul@638 | 86 | package = __static__(package)
|
paul@627 | 87 |
|
paul@627 | 88 | # import package.module
|
paul@627 | 89 | package.__main__()
|
paul@627 | 90 | package.module.__main__()
|
paul@638 | 91 | package = __static__(package)
|
paul@627 | 92 |
|
paul@627 | 93 | # from package.module import cls
|
paul@627 | 94 | package.__main__()
|
paul@627 | 95 | package.module.__main__()
|
paul@627 | 96 | cls = __loadattribute__(package.module, cls) # see below
|
paul@627 | 97 |
|
paul@627 | 98 | Since import statements can appear in code that may be executed more than
|
paul@627 | 99 | once, __main__ functions should test and set a flag indicating whether the
|
paul@627 | 100 | function has already been called.
|
paul@627 | 101 |
|
paul@627 | 102 | Python would arguably be more sensible as a language if imports were
|
paul@627 | 103 | processed separately, but this would then rule out logic controlling the
|
paul@627 | 104 | use of modules.
|
paul@627 | 105 |
|
paul@636 | 106 | Name and Attribute Declarations
|
paul@636 | 107 | -------------------------------
|
paul@636 | 108 |
|
paul@629 | 109 | Assignments and name usage involve locals and globals but usage is declared
|
paul@629 | 110 | explicitly:
|
paul@627 | 111 |
|
paul@627 | 112 | __localnames__(...)
|
paul@627 | 113 |
|
paul@627 | 114 | At the function level, locals are genuine local name definitions whereas
|
paul@627 | 115 | globals refer to module globals:
|
paul@627 | 116 |
|
paul@627 | 117 | __globalnames__(...)
|
paul@627 | 118 |
|
paul@629 | 119 | At the module level, locals are effectively equivalent to module globals but
|
paul@629 | 120 | are declared as follows:
|
paul@629 | 121 |
|
paul@629 | 122 | __moduleattrs__(...)
|
paul@629 | 123 |
|
paul@629 | 124 | Each module's __main__ function will declare any referenced module globals as
|
paul@629 | 125 | globals. Note that the __main__ function is not a genuine attribute of any
|
paul@629 | 126 | module but an internal construct used to initialise modules appropriately.
|
paul@627 | 127 |
|
paul@627 | 128 | Such declarations must appear first in a program unit (module, function).
|
paul@627 | 129 | For example:
|
paul@627 | 130 |
|
paul@627 | 131 | def f(a, b):
|
paul@627 | 132 | __localnames__(a, b, x, y)
|
paul@627 | 133 | __globalnames__(f, g)
|
paul@627 | 134 |
|
paul@638 | 135 | __storelocal__(x, 1)
|
paul@638 | 136 | __storelocal__(y, x)
|
paul@638 | 137 | __storelocal__(a, b)
|
paul@638 | 138 | __storeaddress__(module, g, f)
|
paul@627 | 139 |
|
paul@636 | 140 | Names and Attributes
|
paul@636 | 141 | --------------------
|
paul@636 | 142 |
|
paul@638 | 143 | Bare names refer to locals or globals according to the __localnames__ and
|
paul@638 | 144 | __globalnames__ declarations, or to constants such as None, True, False and
|
paul@638 | 145 | NotImplemented. Storage of local or global names is done using explicit
|
paul@638 | 146 | functions as follows:
|
paul@638 | 147 |
|
paul@638 | 148 | __storelocal__(name, value)
|
paul@638 | 149 | __storeaddress__(module, name, value) # see below
|
paul@638 | 150 |
|
paul@627 | 151 | No operator usage: all operators are converted to invocations, including
|
paul@637 | 152 | all attribute access except static references to modules or particular class
|
paul@637 | 153 | or function definitions using the following notation:
|
paul@637 | 154 |
|
paul@637 | 155 | __static__(package)
|
paul@637 | 156 | __static__(package.module)
|
paul@637 | 157 | __static__(package.module.cls)
|
paul@637 | 158 | __static__(package.module.cls.function)
|
paul@627 | 159 |
|
paul@637 | 160 | A shorthand dot notation could be employed:
|
paul@637 | 161 |
|
paul@637 | 162 | package.module
|
paul@637 | 163 | package.module.cls
|
paul@637 | 164 | package.module.cls.function
|
paul@637 | 165 |
|
paul@637 | 166 | Where multiple definitions of static objects occur, the dot notation cannot be
|
paul@637 | 167 | used, and the full name of such definitions must be quoted. For example:
|
paul@637 | 168 |
|
paul@637 | 169 | __static__("package.module.cls#1.function")
|
paul@627 | 170 |
|
paul@627 | 171 | In general, attribute access must use an explicit function indicating the
|
paul@627 | 172 | kind of access operation being performed. For example:
|
paul@627 | 173 |
|
paul@627 | 174 | __loadaddress__(obj, attrname)
|
paul@627 | 175 | __loadaddresscontext__(obj, attrname)
|
paul@627 | 176 | __loadaddresscontextcond__(obj, attrname)
|
paul@627 | 177 | __loadattr__(obj, attrname)
|
paul@627 | 178 | __loadattrindex__(obj, attrname)
|
paul@627 | 179 | __loadattrindexcontext__(obj, attrname)
|
paul@627 | 180 | __loadattrindexcontextcond__(obj, attrname)
|
paul@627 | 181 |
|
paul@627 | 182 | __storeaddress__(obj, attrname, value)
|
paul@627 | 183 | __storeaddresscontext__(obj, attrname, value)
|
paul@627 | 184 | __storeattr__(obj, attrname, value)
|
paul@627 | 185 | __storeattrindex__(obj, attrname, value)
|
paul@627 | 186 |
|
paul@638 | 187 | Temporary variables could employ similar functions:
|
paul@638 | 188 |
|
paul@638 | 189 | __loadtemp__(0)
|
paul@638 | 190 | __storetemp__(0, value)
|
paul@638 | 191 |
|
paul@636 | 192 | Operators and Invocations
|
paul@636 | 193 | -------------------------
|
paul@636 | 194 |
|
paul@627 | 195 | Conventional operators use the operator functions.
|
paul@627 | 196 |
|
paul@627 | 197 | Special operators could also use the operator functions (where available)
|
paul@627 | 198 | but might as well be supported directly:
|
paul@627 | 199 |
|
paul@627 | 200 | __is__(a, b)
|
paul@627 | 201 |
|
paul@627 | 202 | Logical operators involving short-circuit evaluation could be represented
|
paul@627 | 203 | as function calls, but the evaluation semantics would be preserved:
|
paul@627 | 204 |
|
paul@627 | 205 | __and__(...) # returns the first non-true value or the final value
|
paul@627 | 206 | __not__(obj) # returns the inverse of the boolean interpretation of obj
|
paul@627 | 207 | __or__(...) # returns the first true value or the final value
|
paul@627 | 208 |
|
paul@627 | 209 | Comparisons could be rephrased in a verbose fashion:
|
paul@627 | 210 |
|
paul@627 | 211 | a < b < c becomes lt(a, b) and lt(b, c)
|
paul@627 | 212 | or __and__(lt(a, b), lt(b, c))
|
paul@627 | 213 |
|
paul@636 | 214 | Advanced Control-Flow
|
paul@636 | 215 | ---------------------
|
paul@636 | 216 |
|
paul@627 | 217 | Any statements requiring control-flow definition in terms of blocks must
|
paul@627 | 218 | be handled in the language as the notions of labels and blocks are not
|
paul@627 | 219 | introduced earlier apart from the special case of jumping to another
|
paul@627 | 220 | callable (described below).
|
paul@627 | 221 |
|
paul@627 | 222 | Special functions for low-level operations:
|
paul@627 | 223 |
|
paul@627 | 224 | __check__(obj, type)
|
paul@627 | 225 | __jump__(callable)
|
paul@627 | 226 |
|
paul@627 | 227 | Function/subroutine definition with entry points for checked and unchecked
|
paul@627 | 228 | parameters.
|
paul@627 | 229 |
|
paul@627 | 230 | def fn_checked(self, ...):
|
paul@627 | 231 | __check__(self, Type) # raises a TypeError if not isinstance(self, Type)
|
paul@627 | 232 | __jump__(fn_unchecked) # preserves the frame and return address
|
paul@627 | 233 |
|
paul@627 | 234 | def fn_unchecked(self, ...):
|
paul@627 | 235 | ...
|
paul@636 | 236 |
|
paul@636 | 237 | Exceptions must also be handled in the language.
|