Franck Pommereau

done exam (untested)

......@@ -8,7 +8,14 @@ import string as S
from collections.abc import Iterable
from pathlib import Path
from .asm import asm
from rich.progress import track as rich_track
from pygments.lexer import RegexLexer
from pygments.token import Name, Keyword, Number, Text, Comment, String
from pygments.formatters import LatexFormatter
from pygments import highlight
from .asm import asm, Preprocessor, stdlib
from . import cpuinfo as cnfo
from .vat import TTC, AsmError
from . import words as W
......@@ -38,6 +45,19 @@ class Func:
env.update(zip(self.args, args))
return self.eval(self.expr, env)
def sub(self, repl):
rex = re.compile(fr"([\"\'])({'|'.join(repl)})\1", re.I)
def sub(match):
return (match.group(1)
+ repl[match.group(2).upper()]
+ match.group(1))
return self.__class__(self.name,
self.args,
rex.sub(sub, self.expr),
self._env)
@classmethod
def env(cls, extra={}, **more):
env = dict(cls._builtins)
......@@ -56,7 +76,7 @@ class Func:
class Source:
_pyrun = re.compile(r"\s*\#\s*\>\>\>\s*(.+)$")
_subst = re.compile(r"\{\{(.+)\}\}$")
_subst = re.compile(r"\{\{(.+)\}\}")
_meta = {"func": re.compile(r"^\s*\#\s*(\w+)\s+([\w\s,]*)=>(.+)$"),
"expr": re.compile(r"^\s*\#\s*(\w+)\s*=\s*(.+)$"),
"text": re.compile(r"^\s*\#\s*(\w+)\s*:\s*(.+)$"),
......@@ -94,7 +114,7 @@ class Source:
else:
new = str(new)
line = line.replace(match.group(0), new)
self.src.append(line)
self.src.append(line.rstrip())
continue
g = match.groups()
if kind == "expr":
......@@ -127,12 +147,39 @@ class Source:
for lno, line in enumerate(stream, start=1):
yield lno, line.rstrip()
def copy(self, src=[]):
cls = self.__class__
new = cls.__new__(cls)
new.meta = dict(self.meta)
new.src = src or self.src[:]
new.lno = dict(self.lno)
new.onl = dict(self.onl)
new._ttc = None
return new
@property
def ttc(self):
if self._ttc is None:
self._ttc = TTC()
return self._ttc
def source(self, hide={}):
lines = []
for onl, lin in enumerate(self.src, start=1):
lno = self.lno[onl]
sub = hide.get(lno, None)
tab = " " * (len(lin) - len(lin.lstrip()))
if sub is None:
lines.append(lin)
elif callable(sub):
lines.append(sub(lno, lin, tab))
else:
if not isinstance(sub, str):
sub = "{indent}# line {lno}"
env = dict(self.meta, indent=tab, lno=lno)
lines.append(Func.eval(f"f{sub!r}", env))
return "\n".join(lines)
def check(self, *funcs, maxsteps=1024, ttc=None):
if ttc is None:
ttc = self.ttc
......@@ -160,3 +207,110 @@ class Source:
raise CheckError(name, f"raised {exc}: {err}")
if not ok:
raise CheckError(name, f"failed with {ok!r}")
_irqx = re.compile(r"^irq[0-9A-F]$", re.I)
@property
@F.cache
def labels(self):
labels = set()
tokens = [ln.origin.toks for ln in
Preprocessor([{"source": self.source(),
"pathname": "<exam>"}])]
for tokline in tokens:
for tok in tokline:
if tok.endswith(":") or tok.startswith("@"):
lbl = tok.strip("@:").lower()
if not self._irqx.match(lbl):
labels.add(lbl.upper())
return labels
def randomize(self, names=W.animals, check=True):
# registers
regs = [r for r in cnfo.regname if r.startswith("R")]
R.shuffle(regs)
repl = dict(zip(cnfo.regname, regs))
# labels
repl.update(zip(self.labels, R.sample(names, len(self.labels))))
# rewrite code
rex = re.compile(fr"\b({'|'.join(repl)})\b", re.I)
new = self.copy([rex.sub(lambda m: repl[m.group(1).upper()], line)
for line in self.src])
for k, v in new.meta.items():
if isinstance(v, str):
new.meta[k] = rex.sub(lambda m: repl[m.group(1).upper()], v)
elif isinstance(v, Func):
new.meta[k] = v.sub(repl)
if check:
new.check()
return repl, new
class SourcePool:
def __init__(self, paths, chk={}, verbose=True):
self.src = []
if verbose:
self._track = rich_track
for p in self._track(paths,
transient=True,
description="Loading TTC sources"):
self.src.append(Source(p, **chk))
def _track(self, items, **_):
return items
def pick(self, rand={}):
return R.choice(self.src).randomize(**rand)
macros = set()
for line in (ln.strip() for ln in stdlib.splitlines()):
toks = line.split()
if toks and toks[0] == "%def":
macros.add(toks[1])
class TTCLexer(RegexLexer):
name = "TTC"
aliases = ["ttc"]
filenames = ["*.ttc"]
flags = re.I
tokens = {
"root": [
(r"\s+", Text),
(r"#.*\n", Comment.Single),
(r"%.*\n", Comment.Preproc),
(r"\$\S+", Name.Variable),
(r"@\S+", Name.Label),
(r"\S+:", Name.Label),
(fr"\b({'|'.join(cnfo.parameters)})\b", Keyword.Reserved),
(fr"\b({'|'.join(macros)})\b", Name.Function),
(fr"\b({'|'.join(cnfo.regname)})\b", Name.Attribute),
(r"[0-9A-F]+", Number),
(r'"[^"]*"', String),
(r"'[^']*'", String),
]
}
class Pygmentize:
def __init__(self, fmt=LatexFormatter):
self.lexer = TTCLexer()
self.formatter = fmt()
@F.cache
def __call__(self, source, single=False, **opt):
if isinstance(source, str):
src = source
elif isinstance(source, (tuple, list)):
src = " ".join(str(t) for t in source)
elif isinstance(source, Source):
src = source.source(**opt)
else:
raise ValueError("invalid HRM source")
pyg = highlight(src, self.lexer, self.formatter)
if single:
lines = pyg.splitlines()
return " ".join(lines[1:-1])
else:
return pyg
......
This diff could not be displayed because it is too large.
from libc.stdlib cimport srand, rand
from libc.stdlib cimport rand
from libc.time cimport time
from cython cimport view
......@@ -10,8 +10,6 @@ from rich.text import Text
from rich.color import Color
from rich.style import Style
srand(<unsigned int>time(NULL))
cdef word[16] INTR = [ # i => bit i
0b0000000000000001,
0b0000000000000010,
......@@ -80,6 +78,18 @@ class AsmError(Exception):
super().__init__("assembly error")
self.errors = errors
def print(self):
for line, errors in self.errors.items():
last = None
for ln in line.backtrace():
if last is None:
rprint(Text(f"{ln.path}:{ln.lno}", "red"))
elif (ln.path, ln.lno) != last:
rprint(Text(f" from {ln.path}:{ln.lno}", "dim"))
last = (ln.path, ln.lno)
for err in errors:
rprint(Text(" >>", "yellow"), Text(err))
cdef list FG = [Color.from_ansi(15),
Color.from_ansi(0),
......@@ -604,3 +614,28 @@ cdef class TTC:
if ip in labels:
rprint(f"[dim magenta]{' '.join(sorted(labels[ip]))}:")
ip = self.print(ip, print_regs=False, print_irq=False, ram=ram)
def __getitem__(self, key):
cdef int start, stop, step
if isinstance(key, str):
return self.ram[<int>self.labels[key]]
elif isinstance(key, slice):
if key.start is None:
start = 0
elif isinstance(key.start, str):
start = self.labels[key.start]
else:
start = key.start
if key.stop is None:
stop = len(self.ram)
elif isinstance(key.stop, str):
stop = self.labels[key.stop]
else:
stop = key.stop
if key.step is None:
step = 1
else:
step = key.step
return self.ram[start:stop:step]
else:
return self.ram[<int>key]
......
......@@ -6125,7 +6125,6 @@ words = [
"halogen",
"haloing",
"halon",
"halt",
"halted",
"halter",
"halting",
......@@ -10154,7 +10153,6 @@ words = [
"pontiff",
"pontoon",
"pony",
"pop",
"popcorn",
"pope",
"poplar",
......@@ -10524,7 +10522,6 @@ words = [
"pursuit",
"purvey",
"purview",
"push",
"pushed",
"pusher",
"pushier",
......@@ -11118,7 +11115,6 @@ words = [
"resend",
"resent",
"reserve",
"reset",
"reside",
"resided",
"residue",
......@@ -12053,7 +12049,6 @@ words = [
"servo",
"sesame",
"session",
"set",
"setback",
"settee",
"setter",
......