From f262e453b3a7bbaa599ffc13b37f2bef42370813 Mon Sep 17 00:00:00 2001 From: Aidan Holloway-Bidwell Date: Sat, 26 Oct 2019 11:58:19 -0400 Subject: [PATCH] create and update command parsing --- tinytalk/grammar.py | 111 +++++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/tinytalk/grammar.py b/tinytalk/grammar.py index 621560e..8dd5ef9 100644 --- a/tinytalk/grammar.py +++ b/tinytalk/grammar.py @@ -1,11 +1,16 @@ +import pprint + from parsimonious.grammar import Grammar, NodeVisitor from parsimonious.nodes import RegexNode -import pprint grammar = Grammar( r""" - query = "when" condition (";" condition)* ws? - condition = adjectives? tags data? (ws alias)? + program = query write + query = "when" entry (";" entry)* + write = (create / update) (";" (create / update))* + create = ws "create" entry + update = ws "update" ws name data + entry = adjectives? tags data? (ws alias)? adjectives = (ws adjective)+ tags = (ws tag)+ data = (ws datum)+ @@ -27,44 +32,82 @@ def not_whitespace(o): return not (isinstance(o, RegexNode) and o.expr_name == "ws") -class Condition: - - def __init__(self, *_, adjectives, tags, data, alias): +class Entry: + def __init__(self, *_, adjectives, tags, data): self.adjectives = adjectives self.tags = tags self.data = data + + def __str__(self): + return pprint.pformat(self) + + +class AliasEntry(Entry): + + def __init__(self, *_, adjectives, tags, data, alias): + super().__init__(adjectives=adjectives, tags=tags, data=data) self.alias = alias def __repr__(self): - return f"{self.__class__.__name__}(adjectives={self.adjectives}, tags={self.tags}, data={self.data}, alias={self.alias})" + return f"{self.__class__.__name__}(adjectives={self.adjectives}, " \ + f"tags={self.tags}, " \ + f"data={self.data}, " \ + f"alias={self.alias})" - def __str__(self): - return pprint.pformat(self) + +class Update(Entry): + + def __init__(self, *_, alias, data): + super().__init__(adjectives=None, tags=None, data=data) + self.alias = alias + + def __repr__(self): + return f"{self.__class__.__name__}(alias={self.alias}, " \ + f"data={self.data})" class TinyTalkVisitor(NodeVisitor): + def visit_program(self, _node, visited_children): + query, write = visited_children + return {"query": query, "write": write} + + def visit_write(self, _node, visited_children): + first, rest = visited_children + write = [first[0]] + if isinstance(rest, list): + write += [pair[1][0] for pair in rest] + return write + + def visit_create(self, _node, visited_children): + ws1, _create, entry = visited_children + return entry + + def visit_update(self, _node, visited_children): + _ws1, _update, _ws2, alias, data = visited_children + return Update(alias=alias, data=data) + def visit_query(self, _node, visited_children): """ Returns the overall output. """ - _when, first, rest, _ws = visited_children + _when, first, rest = visited_children query = [first] if isinstance(rest, list): query += [pair[1] for pair in rest] return query - def visit_condition(self, _node, visited_children): + def visit_entry(self, _node, visited_children): adjectives_opt, tags, data_opt, alias_opt = visited_children adjectives = set(adjectives_opt[0]) if isinstance(adjectives_opt, list) else None - data = dict(data_opt[0]) if isinstance(data_opt, list) else None + data = data_opt[0] if isinstance(data_opt, list) else None alias = alias_opt[0][1] if isinstance(alias_opt, list) else None - cond = Condition(adjectives=adjectives, - tags=set(tags), - data=data, - alias=alias) + cond = AliasEntry(adjectives=adjectives, + tags=set(tags), + data=data, + alias=alias) return cond def visit_alias(self, _node, visited_children): _as, _ws, name = visited_children - return name.text + return name def visit_adjectives(self, _node, visited_children): adjs = [child[1] for child in visited_children] @@ -79,11 +122,11 @@ def visit_tags(self, _node, visited_children): def visit_tag(self, _node, visited_children): _pound, name = visited_children - return name.text + return name def visit_data(self, _node, visited_children): data = [child[1] for child in visited_children] - return data + return dict(data) def visit_datum(self, _node, visited_children): _as, name, val_opt = visited_children @@ -91,19 +134,13 @@ def visit_datum(self, _node, visited_children): if isinstance(val_opt, list): _ws, _colon, val = val_opt[0] val = val[0] - return name.text, val - - def visit_number(self, _node, visited_children): - sign_opt, integer, decimal_opt = visited_children - number = integer[0] - if isinstance(decimal_opt, list): - point, decimal = decimal_opt[0] - number += point.text - number += decimal[0] - if isinstance(sign_opt, list): - sign = sign_opt[0][0].text - number = sign + number - return float(number) + return name, val + + def visit_number(self, node, _visited_children): + return float(node.text) + + def visit_name(self, node, _visited_children): + return node.text def visit_digit(self, node, _visited_children): return node.text @@ -134,14 +171,14 @@ def generic_visit(self, node, visited_children): # TODO fix rules to block protected namespaces? # * namespace Pong -# when #aruco x where 0 < x < 100, y [create friend #paddle x: 100, y] +# when [#aruco x where 0 < x < 100, y] create [friend #paddle x: 100, y] # ** app #paddle x y -# when friend #aruco y as marker [update my y: marker y] -# when global nearby #ball +# when [friend #aruco y] as marker update [my y: marker y] +# when [global nearby #ball # x where 90 < x < 110, # velocity_x, -# y where (my y - 50) < y < (my y + 50) +# y where (my y - 50) < y < (my y + 50)] # as ball -# [update ball velocity_x: (ball velocity_x * -1)] +# update [ball velocity_x: (ball velocity_x * -1)] # implies that the #paddle was declared in the Pong namespace