Skip to content

Commit

Permalink
Move assembly functions to grammar class.
Browse files Browse the repository at this point in the history
  • Loading branch information
rickardlindberg committed May 1, 2020
1 parent 187e8be commit 7709ec4
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 27 deletions.
6 changes: 6 additions & 0 deletions writing/rlmeta-vm-poster/README.md
Expand Up @@ -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
10 changes: 1 addition & 9 deletions writing/rlmeta-vm-poster/codegenerator.rlmeta
Expand Up @@ -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
<
< }
Expand Down
29 changes: 12 additions & 17 deletions 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
Expand Down Expand Up @@ -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):

Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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')
Expand Down
9 changes: 8 additions & 1 deletion writing/rlmeta-vm-poster/support.py
Expand Up @@ -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):

Expand Down

0 comments on commit 7709ec4

Please sign in to comment.