From c9fb4913a6f62deb95d8367ea100efee595a86b9 Mon Sep 17 00:00:00 2001
From: Igor Dejanovic <igor.dejanovic@gmail.com>
Date: Mon, 17 Feb 2014 01:14:42 +0100
Subject: [PATCH] refs #2 Support for referencing child node as attributes on
 NonTerminal

This works for rules that are referenced from the rule which created
NonTerminal.
---
 arpeggio/__init__.py          | 23 +++++++++++++++++++++++
 tests/test_rulename_lookup.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)
 create mode 100644 tests/test_rulename_lookup.py

diff --git a/arpeggio/__init__.py b/arpeggio/__init__.py
index 267bd58..1319e88 100644
--- a/arpeggio/__init__.py
+++ b/arpeggio/__init__.py
@@ -615,6 +615,9 @@ class NonTerminal(ParseTreeNode):
         super(NonTerminal, self).__init__(type, position, error)
         self.nodes = flatten([nodes])
 
+        # Child nodes cache. Used for lookup by rule name.
+        self._child_cache = {}
+
     @property
     def desc(self):
         return self.name
@@ -628,6 +631,26 @@ class NonTerminal(ParseTreeNode):
     def __repr__(self):
         return "[ %s ]" % ", ".join([repr(x) for x in self.nodes])
 
+    def __getattr__(self, item):
+        """
+        Find a child (non)terminal by the rule name.
+
+        Args:
+            item(str): The name of the child node.
+        """
+        # First check the cache
+        if item in self._child_cache:
+            return self._child_cache[item]
+
+        # If not found in the cache find it and store it in the
+        # cache for later.
+        for n in self.nodes:
+            if n.type == item:
+                self._child_cache[item] = n
+                return n
+
+        raise AttributeError
+
 
 # ----------------------------------------------------
 # Semantic Actions
diff --git a/tests/test_rulename_lookup.py b/tests/test_rulename_lookup.py
new file mode 100644
index 0000000..2fec9d0
--- /dev/null
+++ b/tests/test_rulename_lookup.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+#######################################################################
+# Name: test_peg_parser
+# Purpose: Test for parser constructed using PEG textual grammars.
+# Author: Igor R. Dejanović <igor DOT dejanovic AT gmail DOT com>
+# Copyright: (c) 2014 Igor R. Dejanović <igor DOT dejanovic AT gmail DOT com>
+# License: MIT License
+#######################################################################
+
+from unittest import TestCase
+
+# Grammar
+from arpeggio import ParserPython, ZeroOrMore
+
+
+def foo(): return "a", bar, "b", baz
+def bar(): return "c"
+def baz(): return "d"
+
+
+
+class TestPEGParser(TestCase):
+
+    def test_lookup_single(self):
+
+        parser = ParserPython(foo)
+
+        result = parser.parse("a c b d")
+
+        self.assertTrue(hasattr(result, "bar"))
+        self.assertTrue(hasattr(result, "baz"))
+        self.assertTrue(not hasattr(result, "unexisting"))
+
-- 
2.18.0