Last active
May 7, 2017 22:52
-
-
Save zed/fa834138b97ff6cd01d90114536c6e0a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/scheme.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@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} | |
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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