From 8d603a5d369178a04e099acd93de450ff351022d Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 1 Jun 2019 21:53:04 +0300 Subject: Add: prototype code generator, with echo example --- ftt-codegen | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100755 ftt-codegen (limited to 'ftt-codegen') 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') -- cgit v1.2.1