summaryrefslogtreecommitdiff
path: root/ftt-codegen
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2019-06-01 21:53:04 +0300
committerLars Wirzenius <liw@liw.fi>2019-06-01 21:53:04 +0300
commit8d603a5d369178a04e099acd93de450ff351022d (patch)
treef0b7d67c243e080453e6d90c892a09c06f75279e /ftt-codegen
parenta53e77f157915a0ca199e65b27d049bfa7b6ae61 (diff)
downloadfable-poc-8d603a5d369178a04e099acd93de450ff351022d.tar.gz
Add: prototype code generator, with echo example
Diffstat (limited to 'ftt-codegen')
-rwxr-xr-xftt-codegen154
1 files changed, 154 insertions, 0 deletions
diff --git a/ftt-codegen b/ftt-codegen
new file mode 100755
index 0000000..7e01553
--- /dev/null
+++ b/ftt-codegen
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+import copy
+import re
+import sys
+
+import CommonMark_bkrs as CommonMark
+import yaml
+
+
+class Scenario:
+
+ def __init__(self):
+ self._name = None
+ self._steps = []
+
+ def get_name(self):
+ return self._name
+
+ def set_name(self, name):
+ self._name = name
+
+ def append_steps(self, steps):
+ self._steps.extend(steps)
+
+ def get_steps(self):
+ steps = []
+ prev_keyword = None
+ for step in self._steps:
+ words = step.split()
+ if words:
+ if words[0].lower() == 'and':
+ assert prev_keyword is not None
+ words[0] = prev_keyword
+ else:
+ words[0] = words[0].lower()
+ prev_keyword = words[0]
+ steps.append(' '.join(words))
+ return steps
+
+ def is_empty(self):
+ return len(self._steps) == 0
+
+
+class Fable:
+
+ def __init__(self):
+ self._scenarios = []
+
+ def start_scenario(self, name):
+ s = Scenario()
+ s.set_name(name)
+ self._scenarios.append(s)
+
+ def append_steps(self, steps):
+ s = self._scenarios[-1]
+ s.append_steps(steps)
+
+ def get_scenarios(self):
+ return self._scenarios[:]
+
+
+def collect_header(fable, o):
+ heading = ' '.join(o.strings)
+ fable.start_scenario(heading)
+
+
+def collect_fencedcode(fable, o):
+ if o.info == 'fable':
+ fable.append_steps(o.strings[1:])
+
+
+collectors = {
+ 'ATXHeader': collect_header,
+ 'SetextHeader': collect_header,
+ 'FencedCode': collect_fencedcode,
+}
+
+
+def collect(fable, o):
+ if o.t not in collectors:
+# debug('{} not known'.format(repr(o.t)))
+ return
+ collector = collectors[o.t]
+ return collector(fable, o)
+
+
+def walk(o, func):
+ done = func(o)
+ if not done:
+ for c in o.children:
+ walk(c, func)
+
+
+def find_binding(bindings, keyword, rest):
+ keyword = keyword.lower()
+ for b in bindings:
+ if keyword not in b:
+ continue
+ m = re.match(b[keyword], rest, re.I)
+ if not m:
+ continue
+ return b['function'], m.groupdict()
+ assert 0, "Couldn't find binding for {} {}".format(keyword, rest)
+
+
+def codegen(f, step, bindings):
+ words = step.split()
+ keyword = words[0]
+ rest = ' '.join(words[1:])
+ function, args = find_binding(bindings, keyword, rest)
+ f.write('args = {\n')
+ for arg in args:
+ f.write('"{}": "{}"\n'.format(arg, args[arg]))
+ f.write('}\n')
+ f.write('{}(**args)\n'.format(function))
+
+def debug(msg):
+ if False:
+ sys.stderr.write('DEBUG: {}\n'.format(msg))
+ sys.stderr.flush()
+
+
+debug('reading bindings')
+bindings = yaml.safe_load(open(sys.argv[1]))
+
+debug('reading prelude')
+prelude = open(sys.argv[2]).read()
+sys.stdout.write(prelude)
+
+debug('reading inputs')
+text = ''.join(open(filename).read() for filename in sys.argv[3:])
+
+debug('parse')
+parser = CommonMark.DocParser()
+ast = parser.parse(text)
+
+debug('output')
+fable = Fable()
+walk(ast, lambda o: collect(fable, o))
+
+scenarios = []
+for s in fable.get_scenarios():
+ if not s.is_empty():
+ scenarios.append(s)
+
+for s in scenarios:
+ debug('scenario: {}'.format(s.get_name()))
+ for step in s.get_steps():
+ debug(' step: {}'.format(step))
+ codegen(sys.stdout, step, bindings)
+ sys.stdout.write('print("OK")\n')
+
+debug('ok')