From 7709ec4b72c1c8c2dfbbef1f75b1e2946d5ee0fd Mon Sep 17 00:00:00 2001 From: Rickard Lindberg Date: Fri, 1 May 2020 13:31:10 +0200 Subject: [PATCH] Move assembly functions to grammar class. --- writing/rlmeta-vm-poster/README.md | 6 ++++ writing/rlmeta-vm-poster/codegenerator.rlmeta | 10 +------ writing/rlmeta-vm-poster/rlmeta.py | 29 ++++++++----------- writing/rlmeta-vm-poster/support.py | 9 +++++- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/writing/rlmeta-vm-poster/README.md b/writing/rlmeta-vm-poster/README.md index 2c18c283..00ce967a 100644 --- a/writing/rlmeta-vm-poster/README.md +++ b/writing/rlmeta-vm-poster/README.md @@ -36,3 +36,9 @@ Getting closer to t-shirt programming. Diff: meld . ../rlmeta-vm + +Annoys me: + +* Message formatting in exception (should be done at call point) +* Assembly hard to read (not sure how to improve it) +* Counter is incremented at match time, not at eval time diff --git a/writing/rlmeta-vm-poster/codegenerator.rlmeta b/writing/rlmeta-vm-poster/codegenerator.rlmeta index dc6386b9..08829c17 100644 --- a/writing/rlmeta-vm-poster/codegenerator.rlmeta +++ b/writing/rlmeta-vm-poster/codegenerator.rlmeta @@ -2,15 +2,7 @@ CodeGenerator { ast = [%:x] -> x py = .:x -> repr(x) Grammar = .:x ast*:ys -> { "class " x "(Grammar):\n\n" > - "def __init__(self):\n" > - "self._instructions = i = []\n" - "self._labels = l = {}\n" - "def I(name, x=None, y=None):\n" > - "i.append((name, x, y))\n" - < - "def LABEL(name):\n" > - "l[name] = len(i)\n" - < + "def assemble(self, I, LABEL):\n" > ys < < } diff --git a/writing/rlmeta-vm-poster/rlmeta.py b/writing/rlmeta-vm-poster/rlmeta.py index 08f2147d..c9562b6a 100644 --- a/writing/rlmeta-vm-poster/rlmeta.py +++ b/writing/rlmeta-vm-poster/rlmeta.py @@ -1,7 +1,7 @@ import sys import pprint -SUPPORT = 'def vm(instructions, labels, start_rule, stream):\n label_counter = 0\n last_action = ConstantSemanticAction(None)\n pc = labels[start_rule]\n call_backtrack_stack = []\n stream, pos, stream_pos_stack = (stream, 0, [])\n scope, scope_stack = (None, [])\n fail_message = None\n latest_fail_message, latest_fail_pos = (None, tuple())\n memo = {}\n while True:\n name, arg1, arg2 = instructions[pc]\n if name == "PUSH_SCOPE":\n scope_stack.append(scope)\n scope = {}\n pc += 1\n continue\n elif name == "BACKTRACK":\n call_backtrack_stack.append((labels[arg1], pos, len(stream_pos_stack), len(scope_stack)))\n pc += 1\n continue\n elif name == "CALL":\n key = (arg1, tuple([x[1] for x in stream_pos_stack]+[pos]))\n if key in memo:\n last_action, stream_pos_stack = memo[key]\n stream_pos_stack = stream_pos_stack[:]\n stream, pos = stream_pos_stack.pop()\n pc += 1\n else:\n call_backtrack_stack.append((pc+1, key))\n pc = labels[arg1]\n continue\n elif name == "MATCH_CHARSEQ":\n for char in arg1:\n if pos >= len(stream) or stream[pos] != char:\n fail_message = ("expected {!r}", char)\n break\n pos += 1\n else:\n last_action = ConstantSemanticAction(arg1)\n pc += 1\n continue\n elif name == "COMMIT":\n call_backtrack_stack.pop()\n pc = labels[arg1]\n continue\n elif name == "POP_SCOPE":\n scope = scope_stack.pop()\n pc += 1\n continue\n elif name == "RETURN":\n if len(call_backtrack_stack) == 0:\n return last_action.eval()\n pc, key = call_backtrack_stack.pop()\n memo[key] = (last_action, stream_pos_stack+[(stream, pos)])\n continue\n elif name == "LIST_APPEND":\n scope.append(last_action)\n pc += 1\n continue\n elif name == "BIND":\n scope[arg1] = last_action\n pc += 1\n continue\n elif name == "ACTION":\n last_action = FnSemanticAction(arg1, scope)\n pc += 1\n continue\n elif name == "MATCH_RANGE":\n if pos >= len(stream) or not (arg1 <= stream[pos] <= arg2):\n fail_message = ("expected range {!r}-{!r}", arg1, arg2)\n else:\n last_action = ConstantSemanticAction(stream[pos])\n pos += 1\n pc += 1\n continue\n elif name == "LIST_START":\n scope_stack.append(scope)\n scope = []\n pc += 1\n continue\n elif name == "LIST_END":\n last_action = FnSemanticAction(lambda xs: [x.eval() for x in xs], scope)\n scope = scope_stack.pop()\n pc += 1\n continue\n elif name == "MATCH_ANY":\n if pos >= len(stream):\n fail_message = ("expected any",)\n else:\n last_action = ConstantSemanticAction(stream[pos])\n pos += 1\n pc += 1\n continue\n elif name == "PUSH_STREAM":\n if pos >= len(stream) or not isinstance(stream[pos], list):\n fail_message = ("expected list",)\n else:\n stream_pos_stack.append((stream, pos))\n stream = stream[pos]\n pos = 0\n pc += 1\n continue\n elif name == "POP_STREAM":\n if pos < len(stream):\n fail_message = ("expected end of list",)\n else:\n stream, pos = stream_pos_stack.pop()\n pos += 1\n pc += 1\n continue\n elif name == "MATCH_CALL_RULE":\n if pos >= len(stream):\n fail_message = ("expected any",)\n else:\n fn_name = str(stream[pos])\n key = (fn_name, tuple([x[1] for x in stream_pos_stack]+[pos]))\n if key in memo:\n last_action, stream_pos_stack = memo[key]\n stream_pos_stack = stream_pos_stack[:]\n stream, pos = stream_pos_stack.pop()\n pc += 1\n else:\n call_backtrack_stack.append((pc+1, key))\n pc = labels[fn_name]\n pos += 1\n continue\n elif name == "FAIL":\n fail_message = (arg1,)\n elif name == "LABEL":\n last_action = ConstantSemanticAction(label_counter)\n label_counter += 1\n pc += 1\n continue\n elif name == "MATCH_STRING":\n if pos >= len(stream) or stream[pos] != arg1:\n fail_message = ("expected {!r}", arg1)\n else:\n last_action = ConstantSemanticAction(arg1)\n pos += 1\n pc += 1\n continue\n else:\n raise Exception("unknown instruction {}".format(name))\n fail_pos = tuple([x[1] for x in stream_pos_stack]+[pos])\n if fail_pos >= latest_fail_pos:\n latest_fail_message = fail_message\n latest_fail_pos = fail_pos\n call_backtrack_entry = tuple()\n while call_backtrack_stack:\n call_backtrack_entry = call_backtrack_stack.pop()\n if len(call_backtrack_entry) == 4:\n break\n if len(call_backtrack_entry) != 4:\n fail_pos = list(latest_fail_pos)\n fail_stream = stream_pos_stack[0][0] if stream_pos_stack else stream\n while len(fail_pos) > 1:\n fail_stream = fail_stream[fail_pos.pop(0)]\n raise MatchError(latest_fail_message, fail_pos[0], fail_stream)\n (pc, pos, stream_stack_len, scope_stack_len) = call_backtrack_entry\n if len(stream_pos_stack) > stream_stack_len:\n stream = stream_pos_stack[stream_stack_len][0]\n stream_pos_stack = stream_pos_stack[:stream_stack_len]\n if len(scope_stack) > scope_stack_len:\n scope = scope_stack[scope_stack_len]\n scope_stack = scope_stack[:scope_stack_len]\n\nclass Grammar(object):\n\n def run(self, rule_name, stream):\n return vm(self._instructions, self._labels, rule_name, stream)\n\nclass ConstantSemanticAction(object):\n\n def __init__(self, value):\n self.value = value\n\n def eval(self):\n return self.value\n\nclass FnSemanticAction(object):\n\n def __init__(self, fn, scope):\n self.fn = fn\n self.scope = scope\n\n def eval(self):\n return self.fn(self.scope)\n\nclass MatchError(Exception):\n\n def __init__(self, message, pos, stream):\n Exception.__init__(self)\n self.message = message[0].format(*message[1:])\n self.pos = pos\n self.stream = stream\n\ndef join(items):\n return "".join(\n join(item) if isinstance(item, list) else str(item)\n for item in items\n )\n\ndef indent(text):\n return join(join([" ", line]) for line in text.splitlines(True))\n' +SUPPORT = 'def vm(instructions, labels, start_rule, stream):\n label_counter = 0\n last_action = ConstantSemanticAction(None)\n pc = labels[start_rule]\n call_backtrack_stack = []\n stream, pos, stream_pos_stack = (stream, 0, [])\n scope, scope_stack = (None, [])\n fail_message = None\n latest_fail_message, latest_fail_pos = (None, tuple())\n memo = {}\n while True:\n name, arg1, arg2 = instructions[pc]\n if name == "PUSH_SCOPE":\n scope_stack.append(scope)\n scope = {}\n pc += 1\n continue\n elif name == "BACKTRACK":\n call_backtrack_stack.append((labels[arg1], pos, len(stream_pos_stack), len(scope_stack)))\n pc += 1\n continue\n elif name == "CALL":\n key = (arg1, tuple([x[1] for x in stream_pos_stack]+[pos]))\n if key in memo:\n last_action, stream_pos_stack = memo[key]\n stream_pos_stack = stream_pos_stack[:]\n stream, pos = stream_pos_stack.pop()\n pc += 1\n else:\n call_backtrack_stack.append((pc+1, key))\n pc = labels[arg1]\n continue\n elif name == "MATCH_CHARSEQ":\n for char in arg1:\n if pos >= len(stream) or stream[pos] != char:\n fail_message = ("expected {!r}", char)\n break\n pos += 1\n else:\n last_action = ConstantSemanticAction(arg1)\n pc += 1\n continue\n elif name == "COMMIT":\n call_backtrack_stack.pop()\n pc = labels[arg1]\n continue\n elif name == "POP_SCOPE":\n scope = scope_stack.pop()\n pc += 1\n continue\n elif name == "RETURN":\n if len(call_backtrack_stack) == 0:\n return last_action.eval()\n pc, key = call_backtrack_stack.pop()\n memo[key] = (last_action, stream_pos_stack+[(stream, pos)])\n continue\n elif name == "LIST_APPEND":\n scope.append(last_action)\n pc += 1\n continue\n elif name == "BIND":\n scope[arg1] = last_action\n pc += 1\n continue\n elif name == "ACTION":\n last_action = FnSemanticAction(arg1, scope)\n pc += 1\n continue\n elif name == "MATCH_RANGE":\n if pos >= len(stream) or not (arg1 <= stream[pos] <= arg2):\n fail_message = ("expected range {!r}-{!r}", arg1, arg2)\n else:\n last_action = ConstantSemanticAction(stream[pos])\n pos += 1\n pc += 1\n continue\n elif name == "LIST_START":\n scope_stack.append(scope)\n scope = []\n pc += 1\n continue\n elif name == "LIST_END":\n last_action = FnSemanticAction(lambda xs: [x.eval() for x in xs], scope)\n scope = scope_stack.pop()\n pc += 1\n continue\n elif name == "MATCH_ANY":\n if pos >= len(stream):\n fail_message = ("expected any",)\n else:\n last_action = ConstantSemanticAction(stream[pos])\n pos += 1\n pc += 1\n continue\n elif name == "PUSH_STREAM":\n if pos >= len(stream) or not isinstance(stream[pos], list):\n fail_message = ("expected list",)\n else:\n stream_pos_stack.append((stream, pos))\n stream = stream[pos]\n pos = 0\n pc += 1\n continue\n elif name == "POP_STREAM":\n if pos < len(stream):\n fail_message = ("expected end of list",)\n else:\n stream, pos = stream_pos_stack.pop()\n pos += 1\n pc += 1\n continue\n elif name == "MATCH_CALL_RULE":\n if pos >= len(stream):\n fail_message = ("expected any",)\n else:\n fn_name = str(stream[pos])\n key = (fn_name, tuple([x[1] for x in stream_pos_stack]+[pos]))\n if key in memo:\n last_action, stream_pos_stack = memo[key]\n stream_pos_stack = stream_pos_stack[:]\n stream, pos = stream_pos_stack.pop()\n pc += 1\n else:\n call_backtrack_stack.append((pc+1, key))\n pc = labels[fn_name]\n pos += 1\n continue\n elif name == "FAIL":\n fail_message = (arg1,)\n elif name == "LABEL":\n last_action = ConstantSemanticAction(label_counter)\n label_counter += 1\n pc += 1\n continue\n elif name == "MATCH_STRING":\n if pos >= len(stream) or stream[pos] != arg1:\n fail_message = ("expected {!r}", arg1)\n else:\n last_action = ConstantSemanticAction(arg1)\n pos += 1\n pc += 1\n continue\n else:\n raise Exception("unknown instruction {}".format(name))\n fail_pos = tuple([x[1] for x in stream_pos_stack]+[pos])\n if fail_pos >= latest_fail_pos:\n latest_fail_message = fail_message\n latest_fail_pos = fail_pos\n call_backtrack_entry = tuple()\n while call_backtrack_stack:\n call_backtrack_entry = call_backtrack_stack.pop()\n if len(call_backtrack_entry) == 4:\n break\n if len(call_backtrack_entry) != 4:\n fail_pos = list(latest_fail_pos)\n fail_stream = stream_pos_stack[0][0] if stream_pos_stack else stream\n while len(fail_pos) > 1:\n fail_stream = fail_stream[fail_pos.pop(0)]\n raise MatchError(latest_fail_message, fail_pos[0], fail_stream)\n (pc, pos, stream_stack_len, scope_stack_len) = call_backtrack_entry\n if len(stream_pos_stack) > stream_stack_len:\n stream = stream_pos_stack[stream_stack_len][0]\n stream_pos_stack = stream_pos_stack[:stream_stack_len]\n if len(scope_stack) > scope_stack_len:\n scope = scope_stack[scope_stack_len]\n scope_stack = scope_stack[:scope_stack_len]\n\nclass Grammar(object):\n\n def run(self, rule_name, stream):\n instructions = []\n labels = {}\n def I(name, arg1=None, arg2=None):\n instructions.append((name, arg1, arg2))\n def LABEL(name):\n labels[name] = len(instructions)\n self.assemble(I, LABEL)\n return vm(instructions, labels, rule_name, stream)\n\nclass ConstantSemanticAction(object):\n\n def __init__(self, value):\n self.value = value\n\n def eval(self):\n return self.value\n\nclass FnSemanticAction(object):\n\n def __init__(self, fn, scope):\n self.fn = fn\n self.scope = scope\n\n def eval(self):\n return self.fn(self.scope)\n\nclass MatchError(Exception):\n\n def __init__(self, message, pos, stream):\n Exception.__init__(self)\n self.message = message[0].format(*message[1:])\n self.pos = pos\n self.stream = stream\n\ndef join(items):\n return "".join(\n join(item) if isinstance(item, list) else str(item)\n for item in items\n )\n\ndef indent(text):\n return join(join([" ", line]) for line in text.splitlines(True))\n' def vm(instructions, labels, start_rule, stream): label_counter = 0 @@ -173,7 +173,14 @@ def vm(instructions, labels, start_rule, stream): class Grammar(object): def run(self, rule_name, stream): - return vm(self._instructions, self._labels, rule_name, stream) + instructions = [] + labels = {} + def I(name, arg1=None, arg2=None): + instructions.append((name, arg1, arg2)) + def LABEL(name): + labels[name] = len(instructions) + self.assemble(I, LABEL) + return vm(instructions, labels, rule_name, stream) class ConstantSemanticAction(object): @@ -211,13 +218,7 @@ def indent(text): class Parser(Grammar): - def __init__(self): - self._instructions = i = [] - self._labels = l = {} - def I(name, x=None, y=None): - i.append((name, x, y)) - def LABEL(name): - l[name] = len(i) + def assemble(self, I, LABEL): LABEL('grammar') I('PUSH_SCOPE') I('CALL', 'name') @@ -767,13 +768,7 @@ def LABEL(name): class CodeGenerator(Grammar): - def __init__(self): - self._instructions = i = [] - self._labels = l = {} - def I(name, x=None, y=None): - i.append((name, x, y)) - def LABEL(name): - l[name] = len(i) + def assemble(self, I, LABEL): LABEL('ast') I('PUSH_SCOPE') I('PUSH_STREAM') @@ -803,7 +798,7 @@ def LABEL(name): LABEL(1) I('LIST_END') I('BIND', 'ys') - I('ACTION', lambda scope: join(['class ', scope['x'].eval(), '(Grammar):\n\n', indent(join(['def __init__(self):\n', indent(join(['self._instructions = i = []\n', 'self._labels = l = {}\n', 'def I(name, x=None, y=None):\n', indent(join(['i.append((name, x, y))\n'])), 'def LABEL(name):\n', indent(join(['l[name] = len(i)\n'])), scope['ys'].eval()]))]))])) + I('ACTION', lambda scope: join(['class ', scope['x'].eval(), '(Grammar):\n\n', indent(join(['def assemble(self, I, LABEL):\n', indent(join([scope['ys'].eval()]))]))])) I('POP_SCOPE') I('RETURN') LABEL('Rule') diff --git a/writing/rlmeta-vm-poster/support.py b/writing/rlmeta-vm-poster/support.py index b8b11ed1..c9c23cea 100644 --- a/writing/rlmeta-vm-poster/support.py +++ b/writing/rlmeta-vm-poster/support.py @@ -168,7 +168,14 @@ def vm(instructions, labels, start_rule, stream): class Grammar(object): def run(self, rule_name, stream): - return vm(self._instructions, self._labels, rule_name, stream) + instructions = [] + labels = {} + def I(name, arg1=None, arg2=None): + instructions.append((name, arg1, arg2)) + def LABEL(name): + labels[name] = len(instructions) + self.assemble(I, LABEL) + return vm(instructions, labels, rule_name, stream) class ConstantSemanticAction(object):