Skip to content
This repository was archived by the owner on May 6, 2024. It is now read-only.

Parser idea #30

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ pip-wheel-metadata/

# Docs
_build/

# ply
lextab.py
parsetab.py
parser.out
163 changes: 163 additions & 0 deletions jarpc/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from enum import Enum
from typing import Union

import ply.lex as lex
import ply.yacc as yacc


class SyntaxError(Exception):
def __init__(self, text: str):
self.text = text


class Action(Enum):
CONNECT = "REQUEST"
PING = "PING"
INFO = "INFO"
REQUEST = "REQUEST"
RESPOND = "RESPOND"


class Query:
def __init__(
self,
action: Action,
parameter: str = None,
destination: Union[int, str] = "ALL",
):
self.action = action
self.parameter = parameter
if parameter:
self.clean_parameter = parameter.replace("'", "\\'")
self.destination = destination

@property
def has_parameter(self) -> bool:
return self.parameter is not None

def __str__(self) -> str:
if not self.has_parameter:
return f"{self.action};"
else:
return f"{self.action} '{self.clean_parameter}' TO {self.destination};"


tokens = (
"SEMICOLON",
"ALL",
"TO",
"STRING",
"INTEGER",
"PING",
"INFO",
"REQUEST",
"RESPOND",
"CONNECT",
)

# very basic integers
t_INTEGER = r"\d+"

# our actions
t_PING = r"PING"
t_INFO = r"INFO"
t_REQUEST = r"REQUEST|REQ"
t_RESPOND = r"RESPOND|RESP"
t_CONNECT = r"CONNECT|CONN"

t_SEMICOLON = r";"
t_TO = r"TO|FROM"
t_ALL = r"ALL"
t_ignore_WS = r"\s+"


def t_STRING(t: lex.LexToken) -> lex.LexToken:
r'("(?:\\"|.)*?"|\'(?:\\\'|.)*?\')'

# make multiple quotes possible like this
t.value = t.value[1:-1]
t.value = bytes(t.value, "utf-8").decode("unicode_escape")

return t


def t_error(t: lex.LexToken) -> None:
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)


lex.lex(optimize=1)


def p_query(p: yacc.YaccProduction) -> None:
"""
query : action_noargs SEMICOLON
| action_args STRING SEMICOLON
| action_args STRING TO destination SEMICOLON
"""
nargs = len(p) - 2 # minus [0] and ;
if nargs == 1: # action_noargs
p[0] = Query(action=p[1])
elif nargs == 2: # action_args STRING
p[0] = Query(action=p[1], parameter=p[2])
elif nargs == 4: # action_args STRING TO destination
p[0] = Query(action=p[1], parameter=p[2], destination=p[4])


# def p_action(p):
# """
# action : PING
# | INFO
# | REQUEST
# | RESPOND
# | CONNECT
# """
# p[0] = p[1]


def p_action_no_args(p: yacc.YaccProduction) -> None:
"""
action_noargs : PING
| INFO
| CONNECT
"""
p[0] = p[1]


def p_action_args(p: yacc.YaccProduction) -> None:
"""
action_args : REQUEST
| RESPOND
"""
p[0] = p[1]


def p_destination(p: yacc.YaccProduction) -> None:
"""
destination : INTEGER
| ALL
"""
p[0] = p[1]


def p_error(p: yacc.YaccProduction) -> None:
if not p: # missing ;
raise SyntaxError("Missing ; after query")
raise SyntaxError("Syntax error at '%s'" % p.value)


parser = yacc.yacc()

if __name__ == "__main__":
while True:
try:
s = input("yarpc > ")
except (EOFError, KeyboardInterrupt):
break
if not s:
continue
try:
r = parser.parse(s)
except SyntaxError as e:
print(e.text)
print(r)
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ ignore_missing_imports = True
[mypy-uvloop]
ignore_missing_imports = True

[mypy-ply.*]
ignore_missing_imports = True

# disable mypy completely for now
[mypy-tests.*]
ignore_errors = True
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
with open("README.md", "r", encoding="utf-8") as f:
long_description = f.read()


init_py = os.path.join(os.path.dirname(__file__), "jarpc", "__init__.py")

with open(init_py) as f:
Expand All @@ -22,7 +23,7 @@
except IndexError:
raise RuntimeError(f"Unable to find author in {init_py}")

install_requires = ["aioredis"]
install_requires = ["aioredis", "ply"]

classifiers = [
"Development Status :: 3 - Alpha",
Expand Down