diff --git a/tools/convert b/tools/convert new file mode 100755 index 0000000..572f411 --- /dev/null +++ b/tools/convert @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 + +import sys as _sys +import re as _re +import json as _json + + +def coin(template, arguments): + result = template + for (key, value, ) in arguments.items(): + result = result.replace("{{%s}}" % key, value) + return result + + +def convert_symbol(symbol): + if (symbol.startswith("<")): + return {"type": "variable", "parameters": {"id": symbol[1:-1]}} + elif (symbol.startswith("[")): + return {"type": "terminal", "parameters": {"id": symbol[1:-1]}} + elif (symbol.startswith("{")): + return {"type": "label", "parameters": {"id": symbol[1:-1]}} + else: + raise ValueError("unknown symbol: %s" % symbol) + + +def extract_name(symbol, type_): + conversion = convert_symbol(symbol) + if (not (conversion["type"] == type_)): + raise ValueError("type mismatch") + else: + name = conversion["parameters"]["id"] + return (None if (name == "") else name) + + + +def main(): + pattern_name = "[0-9a-zA-Z_-]*" + pattern_terminal = coin("\[{{pattern_name}}\]", {"pattern_name": pattern_name}) + pattern_variable = coin("<{{pattern_name}}>", {"pattern_name": pattern_name}) + pattern_label = coin("\{{{pattern_name}}\}", {"pattern_name": pattern_name}) + + rules = [ + # version + { + "pattern": coin( + "@{{space}}(.+)", + { + "space": "\s*", + } + ), + "action": lambda state, matching: { + "version": matching.group(1), + "specification": state["specification"], + }, + }, + # lexer_rule + { + "pattern": coin( + "{{space}}({{pattern_terminal}}){{space}}:{{space}}\"(.*)\"(?:{{space}}:{{space}}(\S+){{space}})?", + { + "space": "\s*", + "pattern_terminal": pattern_terminal, + } + ), + "action": lambda state, matching: { + "version": state["version"], + "specification": { + "version": state["specification"]["version"], + "lexer_rules": ( + state["specification"]["lexer_rules"] + + + [{ + "pattern": matching.group(2), + "name": extract_name(matching.group(1), "terminal"), + "pass": (len(matching.groups()) >= 3), + }] + ), + "parser_rules": state["specification"]["parser_rules"], + "parser_start": state["specification"]["parser_start"], + } + } + }, + # parser_rule + { + "pattern": coin( + "({{pattern_label}}){{space}}:{{space}}({{pattern_variable}}){{space}}:(.*)", + { + "space": "\s*", + "pattern_variable": pattern_variable, + "pattern_label": pattern_label, + } + ), + "action": lambda state, matching: { + "version": state["version"], + "specification": { + "version": state["specification"]["version"], + "lexer_rules": state["specification"]["lexer_rules"], + "parser_rules": ( + state["specification"]["parser_rules"] + + + [{ + "label": extract_name(matching.group(1), "label"), + "premise": extract_name(matching.group(2), "variable"), + "conclusion": list(map( + convert_symbol, + _re.findall( + coin( + "{{pattern_terminal}}|{{pattern_variable}}", + { + "pattern_terminal": pattern_terminal, + "pattern_variable": pattern_variable, + } + ), + matching.group(3) + ) + )), + }] + ), + "parser_start": state["specification"]["parser_start"], + } + } + }, + # parser_start + { + "pattern": coin( + "({{pattern_variable}})", + { + "space": "\s*", + "pattern_variable": pattern_variable, + } + ), + "action": lambda state, matching: { + "version": state["version"], + "specification": { + "version": state["specification"]["version"], + "lexer_rules": state["specification"]["lexer_rules"], + "parser_rules": state["specification"]["parser_rules"], + "parser_start": extract_name(matching.group(1), "variable"), + } + } + }, + # empty + { + "pattern": ".*", + "action": lambda state, matching: state, + }, + ] + + state = { + "version": "1", + "specification": { + "version": "2", + "lexer_rules": [], + "parser_rules": [], + "parser_start": None, + }, + } + content = _sys.stdin.read() + for line in content.split("\n"): + for rule in rules: + matching = _re.match("^%s$" % rule["pattern"], line) + if (not (matching is None)): + state = rule["action"](state, matching) + break + _sys.stdout.write(_json.dumps(state["specification"], indent = "\t")) + +main()