Skip to content

Commit

Permalink
:add: two utilities to grammar module
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdd committed Jul 2, 2016
1 parent 942cf2b commit b07b5e6
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 6 deletions.
4 changes: 2 additions & 2 deletions doc/source/conf.py
Expand Up @@ -58,9 +58,9 @@
# built documents.
#
# The short X.Y version.
version = u'1.3'
version = u'1.4'
# The full version, including alpha/beta/rc tags.
release = u'1.3'
release = u'1.4'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
31 changes: 31 additions & 0 deletions doc/source/tutorial.rst
Expand Up @@ -169,3 +169,34 @@ is provided with:
with open('mycode') as f:
# 'start' is the default rule name used to start parsing
model = parser.parse(f.read(), rule_name='start')
When using the ``NodeWalker`` class to traverse the model (built with the
``ModelBuilderSemantics`` class), those two functions are usefull:

.. code-block:: python
from link.utils.grammar import codegenerator, adopt_children, find_ancestor
from grako.model import ModelBuilderSemantics
with open('grammar.bnf') as f:
module = codegenerator('mydsl', 'MyDSL', f.read())
parser = module.MyDSLParser(semantics)
with open('mycode') as f:
# 'start' is the default rule name used to start parsing
model = parser.parse(f.read(), rule_name='start')
Use this before calling the NodeWalker in order to have nodes parent member set:

.. code-block:: python
adopt_children(model._ast, parent=model)
When traversing the model, you may need to get informations about a parent node:

pnode = find_ancestor(node, 'ParentNode')

if pnode is not None:
# parent node was found
2 changes: 1 addition & 1 deletion link/utils/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-

__version__ = '1.3'
__version__ = '1.4'

CONF_BASE_PATH = 'link/utils'
57 changes: 57 additions & 0 deletions link/utils/grammar.py
@@ -1,6 +1,8 @@

from grako.parser import GrakoGrammarGenerator
from grako.codegen import pythoncg
from grako.util import Mapping
from grako.model import Node

from six import exec_
import imp
Expand Down Expand Up @@ -43,3 +45,58 @@ def codegenerator(modname, prefix, grammar):
sys.modules[modname] = module

return module


def adopt_children(ast, parent=None):
"""
Make sure that grako Nodes have the parent property set correctly.
:param ast: Grako AST
:type ast: grako.model.Node, grako.util.Mapping or list
:param parent: Parent Node
:type parent: grako.model.Node or None
"""

childset = set()

if isinstance(ast, Node) and ast not in childset:
if isinstance(parent, Node):
ast._parent = parent
childset.add(ast)

elif isinstance(ast, Mapping):
for c in ast.values():
adopt_children(c, parent=parent)

elif isinstance(ast, list):
for c in ast:
adopt_children(c, parent=parent)

for child in childset:
adopt_children(child._ast, parent=child)


def find_ancestor(node, classname):
"""
Find first node's ancestor which match class' name.
:param node: Grako Node
:type node: grako.model.Node
:param classname: Class' name
:type classname: str
:returns: Node's ancestor or None if not found
:rtype: grako.model.Node
"""

pnode = node.parent

while pnode is not None:
if pnode.__class__.__name__ == classname:
break

pnode = pnode.parent

return pnode
34 changes: 31 additions & 3 deletions link/utils/test/grammar.py
Expand Up @@ -3,13 +3,14 @@
from b3j0f.utils.ut import UTCase
from unittest import main

from link.utils.grammar import codegenerator
from link.utils.grammar import codegenerator, adopt_children, find_ancestor
from grako.model import ModelBuilderSemantics, Node
from grako.exceptions import FailedToken
import sys


class TestCodeGenerator(UTCase):
def test_module(self):
class TestGrammar(UTCase):
def test_codegenerator(self):
mod = codegenerator('mydsl', 'MyDSL', 'dsl = "DSL" ;')

self.assertEqual(mod.__name__, 'mydsl')
Expand All @@ -23,6 +24,33 @@ def test_module(self):
with self.assertRaises(FailedToken):
parser.parse('dsl', rule_name='dsl')

def test_adopt_children(self):
mod = codegenerator('mydsl', 'MyDSL', '''
subnode::SubNode = v:"DSL" ;
dsl::RootNode = sub:{ subnode }+ ;
''')

parser = mod.MyDSLParser(semantics=ModelBuilderSemantics())
model = parser.parse('DSL', rule_name='dsl')
self.assertIsInstance(model, Node)

adopt_children(model._ast, parent=model)
self.assertIs(model.sub[0].parent, model)

def test_find_ancestor(self):
mod = codegenerator('mydsl', 'MyDSL', '''
subsubnode::SubSubNode = v:"DSL" ;
subnode::SubNode = sub:subsubnode ;
dsl::RootNode = sub:subnode ;
''')

parser = mod.MyDSLParser(semantics=ModelBuilderSemantics())
model = parser.parse('DSL', rule_name='dsl')
adopt_children(model._ast, parent=model)
pnode = find_ancestor(model.sub.sub, 'RootNode')

self.assertIs(pnode, model)


if __name__ == '__main__':
main()

0 comments on commit b07b5e6

Please sign in to comment.