Skip to content

Instantly share code, notes, and snippets.

@zed
Last active May 7, 2017 22:52
Show Gist options
  • Save zed/fa834138b97ff6cd01d90114536c6e0a to your computer and use it in GitHub Desktop.
Save zed/fa834138b97ff6cd01d90114536c6e0a to your computer and use it in GitHub Desktop.
.PHONY: clean test show
test: parse_scheme_description.py scheme_parser.py
python $<
scheme_parser.py: scheme.ebnf
python -m tatsu -o $@ $<
show: scheme.png
display $<
scheme.png: scheme.ebnf
python -m tatsu --draw --outfile $@ $<
clean:
git clean -x -d -f
# $@ - current target
# $* '%'-part (works if there *is* '%' in specification)
# $< first dependence
# $^ all dependencies (without duplicates)
#!/usr/bin/env python
r"""Parse scheme description.
Example:
>>> scheme_description = '''
... INPUTS: x1, x2, x3;
... OUTPUTS: y;
... ELEMENTS: e1, e5 = OR, e2 = AND, e3 = not, e4 = AND;
... SCHEMA: e1(x1, x2), e2(x2, x3),
... e3(e1, e2), y(e3);
... DEFECT: e3 = 1, x1 = 0;
... TEST: 101, 011, 100
... '''
...
>>> scheme = parse(scheme_description)
>>> import yaml # $ pip install pyyaml
>>> print(yaml.dump(scheme))
DEFECT: {e3: 1, x1: 0}
ELEMENTS: {e1: OR, e2: AND, e3: not, e4: AND, e5: OR}
INPUTS: [x1, x2, x3]
OUTPUTS: [y]
SCHEMA:
e1: [x1, x2]
e2: [x2, x3]
e3: [e1, e2]
y: [e3]
TEST: [101, 11, 100]
<BLANKLINE>
"""
from scheme_parser import SchemeParser, SchemeSemantics
class Semantics(SchemeSemantics):
def object(self, ast):
"""pair { ';' pair }"""
return dict(ast)
def pair(self, ast):
"""pair = name ':' value"""
return ast[0], ast[2]
def assign_dict(self, ast):
"""dict_item {',' dict_item}"""
return {k: value for keys, value in ast for k in keys}
def dict_item(self, ast):
"""keys '=' list_item"""
i = ast.index('=')
assert i == (len(ast) - 2)
return ast[:i], ast[i+1]
def paren_dict_item(self, ast):
"""name '(' ~ comma_list ')'"""
return ast[0], ast[2]
def paren_dict(self, ast):
"""paren_dict_item {',' paren_dict_item }"""
return dict(ast)
def number(self, ast):
return int(ast)
def parse(scheme_description):
return SchemeParser(semantics=Semantics()).parse(scheme_description)
if __name__ == '__main__':
import doctest, sys
sys.exit(doctest.testmod().failed)
@@grammar :: Scheme
# https://ru.stackoverflow.com/questions/662595/%D0%9F%D0%B0%D1%80%D1%81%D0%B8%D0%BD%D0%B3-%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8F-%D1%81%D1%85%D0%B5%D0%BC%D1%8B-%D0%BB%D0%B5%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9-%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7
# input file defines an object
start
=
object $
;
# an object is one or more name:value pairs
pair
=
name ':' ~ value
;
object
=
@+:pair {';' ~ @+:pair}
;
value
=
paren_dict | assign_dict | comma_list | name | number
;
name
=
/(?!\d)\w+/
;
number
=
/\d+/
;
# x1, x2, x3
# 101, 011, 100
list_item
=
name | number
;
comma_list
=
@+:list_item {',' @+:list_item}
;
# e1, e5 = OR, e2 = AND, e3 = not, e4 = AND
keys
=
@+:name {',' @+:name}
;
dict_item
=
keys '=' ~ list_item
;
assign_dict
=
@+:dict_item {',' @+:dict_item}
;
# e1(x1, x2), e2(x2, x3), e3(e1, e2), y(e3)
paren_dict_item
=
name '(' ~ comma_list ')'
;
paren_dict
=
@+:paren_dict_item {',' @+:paren_dict_item}
;
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# CAVEAT UTILITOR
#
# This file was automatically generated by Tatsu.
#
# https://pypi.python.org/pypi/tatsu/
#
# Any changes you make to it will be overwritten the next time
# the file is generated.
from __future__ import print_function, division, absolute_import, unicode_literals
from tatsu.buffering import Buffer
from tatsu.parsing import graken, Parser
from tatsu.util import re, RE_FLAGS, generic_main # noqa
KEYWORDS = {}
class SchemeBuffer(Buffer):
def __init__(
self,
text,
whitespace=None,
nameguard=None,
comments_re=None,
eol_comments_re=None,
ignorecase=None,
namechars='',
**kwargs
):
super(SchemeBuffer, self).__init__(
text,
whitespace=whitespace,
nameguard=nameguard,
comments_re=comments_re,
eol_comments_re=eol_comments_re,
ignorecase=ignorecase,
namechars=namechars,
**kwargs
)
class SchemeParser(Parser):
def __init__(
self,
whitespace=None,
nameguard=None,
comments_re=None,
eol_comments_re=None,
ignorecase=None,
left_recursion=False,
parseinfo=True,
keywords=None,
namechars='',
buffer_class=SchemeBuffer,
**kwargs
):
if keywords is None:
keywords = KEYWORDS
super(SchemeParser, self).__init__(
whitespace=whitespace,
nameguard=nameguard,
comments_re=comments_re,
eol_comments_re=eol_comments_re,
ignorecase=ignorecase,
left_recursion=left_recursion,
parseinfo=parseinfo,
keywords=keywords,
namechars=namechars,
buffer_class=buffer_class,
**kwargs
)
@graken()
def _start_(self):
self._object_()
self._check_eof()
@graken()
def _pair_(self):
self._name_()
self._token(':')
self._cut()
self._value_()
@graken()
def _object_(self):
self._pair_()
self.add_last_node_to_name('@')
def block1():
self._token(';')
self._cut()
self._pair_()
self.add_last_node_to_name('@')
self._closure(block1)
@graken()
def _value_(self):
with self._choice():
with self._option():
self._paren_dict_()
with self._option():
self._assign_dict_()
with self._option():
self._comma_list_()
with self._option():
self._name_()
with self._option():
self._number_()
self._error('no available options')
@graken()
def _name_(self):
self._pattern(r'(?!\d)\w+')
@graken()
def _number_(self):
self._pattern(r'\d+')
@graken()
def _list_item_(self):
with self._choice():
with self._option():
self._name_()
with self._option():
self._number_()
self._error('no available options')
@graken()
def _comma_list_(self):
self._list_item_()
self.add_last_node_to_name('@')
def block1():
self._token(',')
self._list_item_()
self.add_last_node_to_name('@')
self._closure(block1)
@graken()
def _keys_(self):
self._name_()
self.add_last_node_to_name('@')
def block1():
self._token(',')
self._name_()
self.add_last_node_to_name('@')
self._closure(block1)
@graken()
def _dict_item_(self):
self._keys_()
self._token('=')
self._cut()
self._list_item_()
@graken()
def _assign_dict_(self):
self._dict_item_()
self.add_last_node_to_name('@')
def block1():
self._token(',')
self._dict_item_()
self.add_last_node_to_name('@')
self._closure(block1)
@graken()
def _paren_dict_item_(self):
self._name_()
self._token('(')
self._cut()
self._comma_list_()
self._token(')')
@graken()
def _paren_dict_(self):
self._paren_dict_item_()
self.add_last_node_to_name('@')
def block1():
self._token(',')
self._paren_dict_item_()
self.add_last_node_to_name('@')
self._closure(block1)
class SchemeSemantics(object):
def start(self, ast):
return ast
def pair(self, ast):
return ast
def object(self, ast):
return ast
def value(self, ast):
return ast
def name(self, ast):
return ast
def number(self, ast):
return ast
def list_item(self, ast):
return ast
def comma_list(self, ast):
return ast
def keys(self, ast):
return ast
def dict_item(self, ast):
return ast
def assign_dict(self, ast):
return ast
def paren_dict_item(self, ast):
return ast
def paren_dict(self, ast):
return ast
def main(filename, startrule, **kwargs):
with open(filename) as f:
text = f.read()
parser = SchemeParser()
return parser.parse(text, startrule, filename=filename, **kwargs)
if __name__ == '__main__':
import json
from tatsu.util import asjson
ast = generic_main(main, SchemeParser, name='Scheme')
print('AST:')
print(ast)
print()
print('JSON:')
print(json.dumps(asjson(ast), indent=2))
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment