kopia lustrzana https://github.com/peterhinch/micropython-samples
191 wiersze
5.9 KiB
Python
191 wiersze
5.9 KiB
Python
![]() |
# os module is loaded on demand
|
||
|
#import os
|
||
|
|
||
|
from . import compiled
|
||
|
|
||
|
|
||
|
class Compiler:
|
||
|
|
||
|
START_CHAR = "{"
|
||
|
STMNT = "%"
|
||
|
STMNT_END = "%}"
|
||
|
EXPR = "{"
|
||
|
EXPR_END = "}}"
|
||
|
|
||
|
def __init__(self, file_in, file_out, indent=0, seq=0, loader=None):
|
||
|
self.file_in = file_in
|
||
|
self.file_out = file_out
|
||
|
self.loader = loader
|
||
|
self.seq = seq
|
||
|
self._indent = indent
|
||
|
self.stack = []
|
||
|
self.in_literal = False
|
||
|
self.flushed_header = False
|
||
|
self.args = "*a, **d"
|
||
|
|
||
|
def indent(self, adjust=0):
|
||
|
if not self.flushed_header:
|
||
|
self.flushed_header = True
|
||
|
self.indent()
|
||
|
self.file_out.write("def render%s(%s):\n" % (str(self.seq) if self.seq else "", self.args))
|
||
|
self.stack.append("def")
|
||
|
self.file_out.write(" " * (len(self.stack) + self._indent + adjust))
|
||
|
|
||
|
def literal(self, s):
|
||
|
if not s:
|
||
|
return
|
||
|
if not self.in_literal:
|
||
|
self.indent()
|
||
|
self.file_out.write('yield """')
|
||
|
self.in_literal = True
|
||
|
self.file_out.write(s.replace('"', '\\"'))
|
||
|
|
||
|
def close_literal(self):
|
||
|
if self.in_literal:
|
||
|
self.file_out.write('"""\n')
|
||
|
self.in_literal = False
|
||
|
|
||
|
def render_expr(self, e):
|
||
|
self.indent()
|
||
|
self.file_out.write('yield str(' + e + ')\n')
|
||
|
|
||
|
def parse_statement(self, stmt):
|
||
|
tokens = stmt.split(None, 1)
|
||
|
if tokens[0] == "args":
|
||
|
if len(tokens) > 1:
|
||
|
self.args = tokens[1]
|
||
|
else:
|
||
|
self.args = ""
|
||
|
elif tokens[0] == "set":
|
||
|
self.indent()
|
||
|
self.file_out.write(stmt[3:].strip() + "\n")
|
||
|
elif tokens[0] == "include":
|
||
|
if not self.flushed_header:
|
||
|
# If there was no other output, we still need a header now
|
||
|
self.indent()
|
||
|
tokens = tokens[1].split(None, 1)
|
||
|
args = ""
|
||
|
if len(tokens) > 1:
|
||
|
args = tokens[1]
|
||
|
if tokens[0][0] == "{":
|
||
|
self.indent()
|
||
|
# "1" as fromlist param is uPy hack
|
||
|
self.file_out.write('_ = __import__(%s.replace(".", "_"), None, None, 1)\n' % tokens[0][2:-2])
|
||
|
self.indent()
|
||
|
self.file_out.write("yield from _.render(%s)\n" % args)
|
||
|
return
|
||
|
|
||
|
with self.loader.input_open(tokens[0][1:-1]) as inc:
|
||
|
self.seq += 1
|
||
|
c = Compiler(inc, self.file_out, len(self.stack) + self._indent, self.seq)
|
||
|
inc_id = self.seq
|
||
|
self.seq = c.compile()
|
||
|
self.indent()
|
||
|
self.file_out.write("yield from render%d(%s)\n" % (inc_id, args))
|
||
|
elif len(tokens) > 1:
|
||
|
if tokens[0] == "elif":
|
||
|
assert self.stack[-1] == "if"
|
||
|
self.indent(-1)
|
||
|
self.file_out.write(stmt + ":\n")
|
||
|
else:
|
||
|
self.indent()
|
||
|
self.file_out.write(stmt + ":\n")
|
||
|
self.stack.append(tokens[0])
|
||
|
else:
|
||
|
if stmt.startswith("end"):
|
||
|
assert self.stack[-1] == stmt[3:]
|
||
|
self.stack.pop(-1)
|
||
|
elif stmt == "else":
|
||
|
assert self.stack[-1] == "if"
|
||
|
self.indent(-1)
|
||
|
self.file_out.write("else:\n")
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def parse_line(self, l):
|
||
|
while l:
|
||
|
start = l.find(self.START_CHAR)
|
||
|
if start == -1:
|
||
|
self.literal(l)
|
||
|
return
|
||
|
self.literal(l[:start])
|
||
|
self.close_literal()
|
||
|
sel = l[start + 1]
|
||
|
#print("*%s=%s=" % (sel, EXPR))
|
||
|
if sel == self.STMNT:
|
||
|
end = l.find(self.STMNT_END)
|
||
|
assert end > 0
|
||
|
stmt = l[start + len(self.START_CHAR + self.STMNT):end].strip()
|
||
|
self.parse_statement(stmt)
|
||
|
end += len(self.STMNT_END)
|
||
|
l = l[end:]
|
||
|
if not self.in_literal and l == "\n":
|
||
|
break
|
||
|
elif sel == self.EXPR:
|
||
|
# print("EXPR")
|
||
|
end = l.find(self.EXPR_END)
|
||
|
assert end > 0
|
||
|
expr = l[start + len(self.START_CHAR + self.EXPR):end].strip()
|
||
|
self.render_expr(expr)
|
||
|
end += len(self.EXPR_END)
|
||
|
l = l[end:]
|
||
|
else:
|
||
|
self.literal(l[start])
|
||
|
l = l[start + 1:]
|
||
|
|
||
|
def header(self):
|
||
|
self.file_out.write("# Autogenerated file\n")
|
||
|
|
||
|
def compile(self):
|
||
|
self.header()
|
||
|
for l in self.file_in:
|
||
|
self.parse_line(l)
|
||
|
self.close_literal()
|
||
|
return self.seq
|
||
|
|
||
|
|
||
|
class Loader(compiled.Loader):
|
||
|
|
||
|
def __init__(self, pkg, dir):
|
||
|
super().__init__(pkg, dir)
|
||
|
self.dir = dir
|
||
|
if pkg == "__main__":
|
||
|
# if pkg isn't really a package, don't bother to use it
|
||
|
# it means we're running from "filesystem directory", not
|
||
|
# from a package.
|
||
|
pkg = None
|
||
|
|
||
|
self.pkg_path = ""
|
||
|
if pkg:
|
||
|
p = __import__(pkg)
|
||
|
if isinstance(p.__path__, str):
|
||
|
# uPy
|
||
|
self.pkg_path = p.__path__
|
||
|
else:
|
||
|
# CPy
|
||
|
self.pkg_path = p.__path__[0]
|
||
|
self.pkg_path += "/"
|
||
|
|
||
|
def input_open(self, template):
|
||
|
path = self.pkg_path + self.dir + "/" + template
|
||
|
return open(path)
|
||
|
|
||
|
def compiled_path(self, template):
|
||
|
return self.dir + "/" + template.replace(".", "_") + ".py"
|
||
|
|
||
|
def load(self, name):
|
||
|
try:
|
||
|
return super().load(name)
|
||
|
except (OSError, ImportError):
|
||
|
pass
|
||
|
|
||
|
compiled_path = self.pkg_path + self.compiled_path(name)
|
||
|
|
||
|
f_in = self.input_open(name)
|
||
|
f_out = open(compiled_path, "w")
|
||
|
c = Compiler(f_in, f_out, loader=self)
|
||
|
c.compile()
|
||
|
f_in.close()
|
||
|
f_out.close()
|
||
|
return super().load(name)
|