diff --git a/asd/doc.py b/asd/doc.py
index bbfdebb11a324d0aaf1b2ddfd7674d75d4bef155..08042fb1a968bc3b08b5c3ec70f4e8bbecbd1fd8 100644
--- a/asd/doc.py
+++ b/asd/doc.py
@@ -2,21 +2,16 @@
 # -*-coding:Utf-8 -*
 
 #==============================================================================
-# TENET: inference
+# unlAnt: ASD of UNL document
 #------------------------------------------------------------------------------
-# Module to execute the extraction process, 
-# by applying the transduction schemes with the SHACL inference engine
+# --
 #==============================================================================
 
 #==============================================================================
 # Importing required modules
 #==============================================================================
 
-import sys
-from subprocess import Popen, PIPE
-from antlr4 import FileStream, CommonTokenStream
-# from antlr.unlLexer import unlLexer
-# from antlr.unlParser import unlParser
+# None
 
 
 #==============================================================================
diff --git a/asd/unl.py b/asd/unl.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff21b261c91a93e06dd992d7291c0d9d7ee425a3
--- /dev/null
+++ b/asd/unl.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python3.10
+# -*-coding:Utf-8 -*
+
+#==============================================================================
+# unlAnt: ASD of UNL Graph
+#------------------------------------------------------------------------------
+# --
+#==============================================================================
+
+#==============================================================================
+# Importing required modules
+#==============================================================================
+
+# None
+
+
+#==============================================================================
+# Parameters
+#==============================================================================
+
+# None
+
+
+#==============================================================================
+# Document Class
+#==============================================================================
+
+  
+class Graph:
+    """ unl := Graph(link*) """
+    
+    def __init__(self, link_list):
+        self.link_list = link_list
+    
+    def to_string(self):
+        res = "Graph("
+        res_init_length = len(res) 
+        for link in self.link_list:
+            res += link.to_string() + ", "
+        if len(res) > res_init_length:
+            res = res[:-2]
+        res += ")"
+        return res
+    
+    
+class Link:
+    """ link := Link(relation, uw, uw) """
+    
+    def __init__(self, relation, uw1, uw2):
+        self.relation = relation
+        self.uw1 = uw1  
+        self.uw2 = uw2
+    
+    def to_string(self):
+        res = self.relation.to_string() + "("
+        res += self.uw1.to_string() + ", "
+        res += self.uw2.to_string() + ")"
+        return res
+    
+    
+class Relation:
+    """ relation := Relation(id) """
+    
+    def __init__(self, id):
+        self.id = id    
+    
+    def to_string(self):
+        return self.id
+    
+    
+class UniversalWord:
+    """ uw := UniversalWord(headword, restriction*, attr*) """
+    
+    def __init__(self, headword, restriction_list, attr_list):
+        self.headword = headword    
+        self.restriction_list = restriction_list   
+        self.attr_list = attr_list   
+    
+    def to_string(self):
+        res = self.headword.to_string() + "("
+        res_init_length = len(res) 
+        for restrictio in self.restriction_list:
+            res += restrictio.to_string() + ", "
+        res = res[:-1]
+        if len(res) > res_init_length:
+            res = res[:-1]
+            res += ")"
+        for attr in self.attr_list:
+            res += ".@" + attr.to_string()
+        return res
+    
+    
+class Headword:
+    """ headword := Headword(id) """
+    
+    def __init__(self, id):
+        self.id = id    
+    
+    def to_string(self):
+        return self.id.to_string()
+    
+    
+class Restriction:
+    """ restriction := Restriction(id) """
+    
+    def __init__(self, relation, id):
+        self.relation = relation
+        self.id = id
+    
+    def to_string(self):
+        return self.relation.to_string() + ">" + self.id.to_string()
+    
+    
+class Attribute:
+    """ attr := Attribute(id) """
+    
+    def __init__(self, id):
+        self.id = id    
+    
+    def to_string(self):
+        return self.id.to_string()
+    
+    
+class Ident:
+    """ id := Ident(string) """
+    
+    def __init__(self, id):
+        self.id = id    
+    
+    def to_string(self):
+        return self.id
+    
diff --git a/grammar/unl/unl.g4 b/grammar/unl/unl.g4
index 925fb6749d737c59c4266951878a0ee1c3b42154..202796f1a1d9603217b8b24d25e6e1f892ce6640 100644
--- a/grammar/unl/unl.g4
+++ b/grammar/unl/unl.g4
@@ -4,6 +4,10 @@
 
 grammar unl;
 
+@header {
+from asd import unl
+}
+
 
 //=============================================================================
 // Parser Grammar
@@ -13,40 +17,50 @@ grammar unl;
 // UNL representation
 //---------------------------------------------------------
 
-unlPart
-  : '{unl}' (relationOccurrence)+ '{/unl}'
+unlGraph returns [out]
+  : '{unl}'                               {link_list = []}
+    (l=link                               {link_list.append($l.out)} )+
+    '{/unl}'                              {$out = unl.Graph(link_list)}
   ;
 
-relationOccurrence
-  : universalRelation LP universalWord COMMA universalWord RP
+link returns [out]
+  : r=universalRelation
+    LP uw1=universalWord COMMA uw2=universalWord RP
+                                          {$out = unl.Link($r.out, $uw1.out, $uw2.out)}
   ;
 
-universalWord
-  : headword
-    (LP restriction (COMMA restriction)* RP)?
-    (attribute)*
-  | value
+universalWord returns [out]
+  : h=headword                            {headword =  $h.out}
+                                          {restriction_list = []}
+    (LP
+    r1=restriction                        {restriction_list.append($r1.out)}
+    (COMMA r2=restriction                 {restriction_list.append($r2.out)} )*
+    RP)?
+                                          {attr_list = []}
+    (a=attribute                          {attr_list.append($a.out)} )*
+                                          {$out = unl.UniversalWord(headword, restriction_list, attr_list)}
+  | v=value                               {$out = unl.UniversalWord($v.out, [], [])}
   ;
 
-headword
-  : ident
+headword returns [out]
+  : i=ident                              {$out = unl.Headword($i.out)}
   ;
 
-restriction
-  : universalRelation GREATER ident
+restriction returns [out]
+  : r=universalRelation GREATER i=ident  {$out = unl.Restriction($r.out, $i.out)}
   ;
 
-attribute
-  : DOT AT ident
+attribute returns [out]
+  : DOT AT i=ident                       {$out = unl.Attribute($i.out)}
   ;
 
-value
-  : VALUE
+value returns [out]
+  : v=VALUE                               {$out = unl.Ident($v.text)}
   ;
 
-universalRelation
-  : ( AND | AOJ | BEN | CNT |
-      EQU | ICL | OBJ | QUA )
+universalRelation returns [out]
+  : r=( AND | AOJ | BEN | CNT
+      | EQU | ICL | OBJ | QUA )           {$out = unl.Relation($r.text)}
   ;
 
 
@@ -56,9 +70,16 @@ universalRelation
 
 sentence : (word | punctuation | bracket)* ;
 
-ident : word (UNDERSCORE word)* ;
+ident returns [out]
+  : w1=word                               {ident = $w1.out}
+    (UNDERSCORE w2=word                   {ident += "_" + $w2.out} )*
+                                          {$out = unl.Ident(ident)}
+  ;
 
-word : LETTER | WORD ;
+word returns [$out]
+  : l=LETTER                              {$out = $l.text}
+  | w=WORD                                {$out = $w.text}
+  ;
 
 punctuation : DOT | COMMA | SEMCOL | COLON | DASH ;
 
diff --git a/grammar/unl/unl.interp b/grammar/unl/unl.interp
index 525ed69941a5b9c77d996dee48d06fbdda14e0af..355e5af8eba255643a50c0c1b2c90fe6f6aac675 100644
--- a/grammar/unl/unl.interp
+++ b/grammar/unl/unl.interp
@@ -59,8 +59,8 @@ WORD
 VALUE
 
 rule names:
-unlPart
-relationOccurrence
+unlGraph
+link
 universalWord
 headword
 restriction
@@ -75,4 +75,4 @@ bracket
 
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 29, 103, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 3, 2, 3, 2, 6, 2, 31, 10, 2, 13, 2, 14, 2, 32, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 49, 10, 4, 12, 4, 14, 4, 52, 11, 4, 3, 4, 3, 4, 5, 4, 56, 10, 4, 3, 4, 7, 4, 59, 10, 4, 12, 4, 14, 4, 62, 11, 4, 3, 4, 5, 4, 65, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 7, 10, 84, 10, 10, 12, 10, 14, 10, 87, 11, 10, 3, 11, 3, 11, 3, 11, 7, 11, 92, 10, 11, 12, 11, 14, 11, 95, 11, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 2, 2, 15, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 2, 6, 3, 2, 19, 26, 3, 2, 27, 28, 3, 2, 6, 10, 3, 2, 11, 14, 2, 98, 2, 28, 3, 2, 2, 2, 4, 36, 3, 2, 2, 2, 6, 64, 3, 2, 2, 2, 8, 66, 3, 2, 2, 2, 10, 68, 3, 2, 2, 2, 12, 72, 3, 2, 2, 2, 14, 76, 3, 2, 2, 2, 16, 78, 3, 2, 2, 2, 18, 85, 3, 2, 2, 2, 20, 88, 3, 2, 2, 2, 22, 96, 3, 2, 2, 2, 24, 98, 3, 2, 2, 2, 26, 100, 3, 2, 2, 2, 28, 30, 7, 3, 2, 2, 29, 31, 5, 4, 3, 2, 30, 29, 3, 2, 2, 2, 31, 32, 3, 2, 2, 2, 32, 30, 3, 2, 2, 2, 32, 33, 3, 2, 2, 2, 33, 34, 3, 2, 2, 2, 34, 35, 7, 4, 2, 2, 35, 3, 3, 2, 2, 2, 36, 37, 5, 16, 9, 2, 37, 38, 7, 11, 2, 2, 38, 39, 5, 6, 4, 2, 39, 40, 7, 7, 2, 2, 40, 41, 5, 6, 4, 2, 41, 42, 7, 12, 2, 2, 42, 5, 3, 2, 2, 2, 43, 55, 5, 8, 5, 2, 44, 45, 7, 11, 2, 2, 45, 50, 5, 10, 6, 2, 46, 47, 7, 7, 2, 2, 47, 49, 5, 10, 6, 2, 48, 46, 3, 2, 2, 2, 49, 52, 3, 2, 2, 2, 50, 48, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 53, 3, 2, 2, 2, 52, 50, 3, 2, 2, 2, 53, 54, 7, 12, 2, 2, 54, 56, 3, 2, 2, 2, 55, 44, 3, 2, 2, 2, 55, 56, 3, 2, 2, 2, 56, 60, 3, 2, 2, 2, 57, 59, 5, 12, 7, 2, 58, 57, 3, 2, 2, 2, 59, 62, 3, 2, 2, 2, 60, 58, 3, 2, 2, 2, 60, 61, 3, 2, 2, 2, 61, 65, 3, 2, 2, 2, 62, 60, 3, 2, 2, 2, 63, 65, 5, 14, 8, 2, 64, 43, 3, 2, 2, 2, 64, 63, 3, 2, 2, 2, 65, 7, 3, 2, 2, 2, 66, 67, 5, 20, 11, 2, 67, 9, 3, 2, 2, 2, 68, 69, 5, 16, 9, 2, 69, 70, 7, 16, 2, 2, 70, 71, 5, 20, 11, 2, 71, 11, 3, 2, 2, 2, 72, 73, 7, 6, 2, 2, 73, 74, 7, 17, 2, 2, 74, 75, 5, 20, 11, 2, 75, 13, 3, 2, 2, 2, 76, 77, 7, 29, 2, 2, 77, 15, 3, 2, 2, 2, 78, 79, 9, 2, 2, 2, 79, 17, 3, 2, 2, 2, 80, 84, 5, 22, 12, 2, 81, 84, 5, 24, 13, 2, 82, 84, 5, 26, 14, 2, 83, 80, 3, 2, 2, 2, 83, 81, 3, 2, 2, 2, 83, 82, 3, 2, 2, 2, 84, 87, 3, 2, 2, 2, 85, 83, 3, 2, 2, 2, 85, 86, 3, 2, 2, 2, 86, 19, 3, 2, 2, 2, 87, 85, 3, 2, 2, 2, 88, 93, 5, 22, 12, 2, 89, 90, 7, 18, 2, 2, 90, 92, 5, 22, 12, 2, 91, 89, 3, 2, 2, 2, 92, 95, 3, 2, 2, 2, 93, 91, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 21, 3, 2, 2, 2, 95, 93, 3, 2, 2, 2, 96, 97, 9, 3, 2, 2, 97, 23, 3, 2, 2, 2, 98, 99, 9, 4, 2, 2, 99, 25, 3, 2, 2, 2, 100, 101, 9, 5, 2, 2, 101, 27, 3, 2, 2, 2, 10, 32, 50, 55, 60, 64, 83, 85, 93]
\ No newline at end of file
+[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 29, 134, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 6, 2, 34, 10, 2, 13, 2, 14, 2, 35, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 59, 10, 4, 12, 4, 14, 4, 62, 11, 4, 3, 4, 3, 4, 5, 4, 66, 10, 4, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 72, 10, 4, 12, 4, 14, 4, 75, 11, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 82, 10, 4, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 7, 10, 106, 10, 10, 12, 10, 14, 10, 109, 11, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 117, 10, 11, 12, 11, 14, 11, 120, 11, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 128, 10, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 2, 2, 15, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 2, 5, 3, 2, 19, 26, 3, 2, 6, 10, 3, 2, 11, 14, 2, 130, 2, 28, 3, 2, 2, 2, 4, 40, 3, 2, 2, 2, 6, 81, 3, 2, 2, 2, 8, 83, 3, 2, 2, 2, 10, 86, 3, 2, 2, 2, 12, 91, 3, 2, 2, 2, 14, 96, 3, 2, 2, 2, 16, 99, 3, 2, 2, 2, 18, 107, 3, 2, 2, 2, 20, 110, 3, 2, 2, 2, 22, 127, 3, 2, 2, 2, 24, 129, 3, 2, 2, 2, 26, 131, 3, 2, 2, 2, 28, 29, 7, 3, 2, 2, 29, 33, 8, 2, 1, 2, 30, 31, 5, 4, 3, 2, 31, 32, 8, 2, 1, 2, 32, 34, 3, 2, 2, 2, 33, 30, 3, 2, 2, 2, 34, 35, 3, 2, 2, 2, 35, 33, 3, 2, 2, 2, 35, 36, 3, 2, 2, 2, 36, 37, 3, 2, 2, 2, 37, 38, 7, 4, 2, 2, 38, 39, 8, 2, 1, 2, 39, 3, 3, 2, 2, 2, 40, 41, 5, 16, 9, 2, 41, 42, 7, 11, 2, 2, 42, 43, 5, 6, 4, 2, 43, 44, 7, 7, 2, 2, 44, 45, 5, 6, 4, 2, 45, 46, 7, 12, 2, 2, 46, 47, 8, 3, 1, 2, 47, 5, 3, 2, 2, 2, 48, 49, 5, 8, 5, 2, 49, 50, 8, 4, 1, 2, 50, 65, 8, 4, 1, 2, 51, 52, 7, 11, 2, 2, 52, 53, 5, 10, 6, 2, 53, 60, 8, 4, 1, 2, 54, 55, 7, 7, 2, 2, 55, 56, 5, 10, 6, 2, 56, 57, 8, 4, 1, 2, 57, 59, 3, 2, 2, 2, 58, 54, 3, 2, 2, 2, 59, 62, 3, 2, 2, 2, 60, 58, 3, 2, 2, 2, 60, 61, 3, 2, 2, 2, 61, 63, 3, 2, 2, 2, 62, 60, 3, 2, 2, 2, 63, 64, 7, 12, 2, 2, 64, 66, 3, 2, 2, 2, 65, 51, 3, 2, 2, 2, 65, 66, 3, 2, 2, 2, 66, 67, 3, 2, 2, 2, 67, 73, 8, 4, 1, 2, 68, 69, 5, 12, 7, 2, 69, 70, 8, 4, 1, 2, 70, 72, 3, 2, 2, 2, 71, 68, 3, 2, 2, 2, 72, 75, 3, 2, 2, 2, 73, 71, 3, 2, 2, 2, 73, 74, 3, 2, 2, 2, 74, 76, 3, 2, 2, 2, 75, 73, 3, 2, 2, 2, 76, 77, 8, 4, 1, 2, 77, 82, 3, 2, 2, 2, 78, 79, 5, 14, 8, 2, 79, 80, 8, 4, 1, 2, 80, 82, 3, 2, 2, 2, 81, 48, 3, 2, 2, 2, 81, 78, 3, 2, 2, 2, 82, 7, 3, 2, 2, 2, 83, 84, 5, 20, 11, 2, 84, 85, 8, 5, 1, 2, 85, 9, 3, 2, 2, 2, 86, 87, 5, 16, 9, 2, 87, 88, 7, 16, 2, 2, 88, 89, 5, 20, 11, 2, 89, 90, 8, 6, 1, 2, 90, 11, 3, 2, 2, 2, 91, 92, 7, 6, 2, 2, 92, 93, 7, 17, 2, 2, 93, 94, 5, 20, 11, 2, 94, 95, 8, 7, 1, 2, 95, 13, 3, 2, 2, 2, 96, 97, 7, 29, 2, 2, 97, 98, 8, 8, 1, 2, 98, 15, 3, 2, 2, 2, 99, 100, 9, 2, 2, 2, 100, 101, 8, 9, 1, 2, 101, 17, 3, 2, 2, 2, 102, 106, 5, 22, 12, 2, 103, 106, 5, 24, 13, 2, 104, 106, 5, 26, 14, 2, 105, 102, 3, 2, 2, 2, 105, 103, 3, 2, 2, 2, 105, 104, 3, 2, 2, 2, 106, 109, 3, 2, 2, 2, 107, 105, 3, 2, 2, 2, 107, 108, 3, 2, 2, 2, 108, 19, 3, 2, 2, 2, 109, 107, 3, 2, 2, 2, 110, 111, 5, 22, 12, 2, 111, 118, 8, 11, 1, 2, 112, 113, 7, 18, 2, 2, 113, 114, 5, 22, 12, 2, 114, 115, 8, 11, 1, 2, 115, 117, 3, 2, 2, 2, 116, 112, 3, 2, 2, 2, 117, 120, 3, 2, 2, 2, 118, 116, 3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 119, 121, 3, 2, 2, 2, 120, 118, 3, 2, 2, 2, 121, 122, 8, 11, 1, 2, 122, 21, 3, 2, 2, 2, 123, 124, 7, 27, 2, 2, 124, 128, 8, 12, 1, 2, 125, 126, 7, 28, 2, 2, 126, 128, 8, 12, 1, 2, 127, 123, 3, 2, 2, 2, 127, 125, 3, 2, 2, 2, 128, 23, 3, 2, 2, 2, 129, 130, 9, 3, 2, 2, 130, 25, 3, 2, 2, 2, 131, 132, 9, 4, 2, 2, 132, 27, 3, 2, 2, 2, 11, 35, 60, 65, 73, 81, 105, 107, 118, 127]
\ No newline at end of file
diff --git a/grammar/unl/unlLexer.py b/grammar/unl/unlLexer.py
index 58e84e73c355631f444b54ba9dcd1ef8066116d2..954a344ec72b9d696f586ddc3bc3ee05d38692d0 100644
--- a/grammar/unl/unlLexer.py
+++ b/grammar/unl/unlLexer.py
@@ -8,6 +8,9 @@ else:
     from typing.io import TextIO
 
 
+from asd import unl
+
+
 
 def serializedATN():
     with StringIO() as buf:
diff --git a/grammar/unl/unlListener.py b/grammar/unl/unlListener.py
index d107a9cb01d483adc942656ba49b4da61f867ae1..f3908d8f97fe9a3c5526086e0d9a4ba5a40708fe 100644
--- a/grammar/unl/unlListener.py
+++ b/grammar/unl/unlListener.py
@@ -5,24 +5,27 @@ if __name__ is not None and "." in __name__:
 else:
     from unlParser import unlParser
 
+from asd import unl
+
+
 # This class defines a complete listener for a parse tree produced by unlParser.
 class unlListener(ParseTreeListener):
 
-    # Enter a parse tree produced by unlParser#unlPart.
-    def enterUnlPart(self, ctx:unlParser.UnlPartContext):
+    # Enter a parse tree produced by unlParser#unlGraph.
+    def enterUnlGraph(self, ctx:unlParser.UnlGraphContext):
         pass
 
-    # Exit a parse tree produced by unlParser#unlPart.
-    def exitUnlPart(self, ctx:unlParser.UnlPartContext):
+    # Exit a parse tree produced by unlParser#unlGraph.
+    def exitUnlGraph(self, ctx:unlParser.UnlGraphContext):
         pass
 
 
-    # Enter a parse tree produced by unlParser#relationOccurrence.
-    def enterRelationOccurrence(self, ctx:unlParser.RelationOccurrenceContext):
+    # Enter a parse tree produced by unlParser#link.
+    def enterLink(self, ctx:unlParser.LinkContext):
         pass
 
-    # Exit a parse tree produced by unlParser#relationOccurrence.
-    def exitRelationOccurrence(self, ctx:unlParser.RelationOccurrenceContext):
+    # Exit a parse tree produced by unlParser#link.
+    def exitLink(self, ctx:unlParser.LinkContext):
         pass
 
 
diff --git a/grammar/unl/unlParser.py b/grammar/unl/unlParser.py
index 6609289b4790a1245628350b0461329789a45be1..7cf6604bcb8baecc7c1b926a970ec81286c8152d 100644
--- a/grammar/unl/unlParser.py
+++ b/grammar/unl/unlParser.py
@@ -9,40 +9,52 @@ else:
 	from typing.io import TextIO
 
 
+from asd import unl
+
+
 def serializedATN():
     with StringIO() as buf:
         buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\35")
-        buf.write("g\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b")
-        buf.write("\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t")
-        buf.write("\16\3\2\3\2\6\2\37\n\2\r\2\16\2 \3\2\3\2\3\3\3\3\3\3\3")
-        buf.write("\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\7\4\61\n\4\f\4\16\4")
-        buf.write("\64\13\4\3\4\3\4\5\48\n\4\3\4\7\4;\n\4\f\4\16\4>\13\4")
-        buf.write("\3\4\5\4A\n\4\3\5\3\5\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7")
-        buf.write("\3\b\3\b\3\t\3\t\3\n\3\n\3\n\7\nT\n\n\f\n\16\nW\13\n\3")
-        buf.write("\13\3\13\3\13\7\13\\\n\13\f\13\16\13_\13\13\3\f\3\f\3")
-        buf.write("\r\3\r\3\16\3\16\3\16\2\2\17\2\4\6\b\n\f\16\20\22\24\26")
-        buf.write("\30\32\2\6\3\2\23\32\3\2\33\34\3\2\6\n\3\2\13\16\2b\2")
-        buf.write("\34\3\2\2\2\4$\3\2\2\2\6@\3\2\2\2\bB\3\2\2\2\nD\3\2\2")
-        buf.write("\2\fH\3\2\2\2\16L\3\2\2\2\20N\3\2\2\2\22U\3\2\2\2\24X")
-        buf.write("\3\2\2\2\26`\3\2\2\2\30b\3\2\2\2\32d\3\2\2\2\34\36\7\3")
-        buf.write("\2\2\35\37\5\4\3\2\36\35\3\2\2\2\37 \3\2\2\2 \36\3\2\2")
-        buf.write("\2 !\3\2\2\2!\"\3\2\2\2\"#\7\4\2\2#\3\3\2\2\2$%\5\20\t")
-        buf.write("\2%&\7\13\2\2&\'\5\6\4\2\'(\7\7\2\2()\5\6\4\2)*\7\f\2")
-        buf.write("\2*\5\3\2\2\2+\67\5\b\5\2,-\7\13\2\2-\62\5\n\6\2./\7\7")
-        buf.write("\2\2/\61\5\n\6\2\60.\3\2\2\2\61\64\3\2\2\2\62\60\3\2\2")
-        buf.write("\2\62\63\3\2\2\2\63\65\3\2\2\2\64\62\3\2\2\2\65\66\7\f")
-        buf.write("\2\2\668\3\2\2\2\67,\3\2\2\2\678\3\2\2\28<\3\2\2\29;\5")
-        buf.write("\f\7\2:9\3\2\2\2;>\3\2\2\2<:\3\2\2\2<=\3\2\2\2=A\3\2\2")
-        buf.write("\2><\3\2\2\2?A\5\16\b\2@+\3\2\2\2@?\3\2\2\2A\7\3\2\2\2")
-        buf.write("BC\5\24\13\2C\t\3\2\2\2DE\5\20\t\2EF\7\20\2\2FG\5\24\13")
-        buf.write("\2G\13\3\2\2\2HI\7\6\2\2IJ\7\21\2\2JK\5\24\13\2K\r\3\2")
-        buf.write("\2\2LM\7\35\2\2M\17\3\2\2\2NO\t\2\2\2O\21\3\2\2\2PT\5")
-        buf.write("\26\f\2QT\5\30\r\2RT\5\32\16\2SP\3\2\2\2SQ\3\2\2\2SR\3")
-        buf.write("\2\2\2TW\3\2\2\2US\3\2\2\2UV\3\2\2\2V\23\3\2\2\2WU\3\2")
-        buf.write("\2\2X]\5\26\f\2YZ\7\22\2\2Z\\\5\26\f\2[Y\3\2\2\2\\_\3")
-        buf.write("\2\2\2][\3\2\2\2]^\3\2\2\2^\25\3\2\2\2_]\3\2\2\2`a\t\3")
-        buf.write("\2\2a\27\3\2\2\2bc\t\4\2\2c\31\3\2\2\2de\t\5\2\2e\33\3")
-        buf.write("\2\2\2\n \62\67<@SU]")
+        buf.write("\u0086\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7")
+        buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16")
+        buf.write("\t\16\3\2\3\2\3\2\3\2\3\2\6\2\"\n\2\r\2\16\2#\3\2\3\2")
+        buf.write("\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3")
+        buf.write("\4\3\4\3\4\3\4\3\4\3\4\7\4;\n\4\f\4\16\4>\13\4\3\4\3\4")
+        buf.write("\5\4B\n\4\3\4\3\4\3\4\3\4\7\4H\n\4\f\4\16\4K\13\4\3\4")
+        buf.write("\3\4\3\4\3\4\3\4\5\4R\n\4\3\5\3\5\3\5\3\6\3\6\3\6\3\6")
+        buf.write("\3\6\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\t\3\t\3\t\3\n\3")
+        buf.write("\n\3\n\7\nj\n\n\f\n\16\nm\13\n\3\13\3\13\3\13\3\13\3\13")
+        buf.write("\3\13\7\13u\n\13\f\13\16\13x\13\13\3\13\3\13\3\f\3\f\3")
+        buf.write("\f\3\f\5\f\u0080\n\f\3\r\3\r\3\16\3\16\3\16\2\2\17\2\4")
+        buf.write("\6\b\n\f\16\20\22\24\26\30\32\2\5\3\2\23\32\3\2\6\n\3")
+        buf.write("\2\13\16\2\u0082\2\34\3\2\2\2\4(\3\2\2\2\6Q\3\2\2\2\b")
+        buf.write("S\3\2\2\2\nV\3\2\2\2\f[\3\2\2\2\16`\3\2\2\2\20c\3\2\2")
+        buf.write("\2\22k\3\2\2\2\24n\3\2\2\2\26\177\3\2\2\2\30\u0081\3\2")
+        buf.write("\2\2\32\u0083\3\2\2\2\34\35\7\3\2\2\35!\b\2\1\2\36\37")
+        buf.write("\5\4\3\2\37 \b\2\1\2 \"\3\2\2\2!\36\3\2\2\2\"#\3\2\2\2")
+        buf.write("#!\3\2\2\2#$\3\2\2\2$%\3\2\2\2%&\7\4\2\2&\'\b\2\1\2\'")
+        buf.write("\3\3\2\2\2()\5\20\t\2)*\7\13\2\2*+\5\6\4\2+,\7\7\2\2,")
+        buf.write("-\5\6\4\2-.\7\f\2\2./\b\3\1\2/\5\3\2\2\2\60\61\5\b\5\2")
+        buf.write("\61\62\b\4\1\2\62A\b\4\1\2\63\64\7\13\2\2\64\65\5\n\6")
+        buf.write("\2\65<\b\4\1\2\66\67\7\7\2\2\678\5\n\6\289\b\4\1\29;\3")
+        buf.write("\2\2\2:\66\3\2\2\2;>\3\2\2\2<:\3\2\2\2<=\3\2\2\2=?\3\2")
+        buf.write("\2\2><\3\2\2\2?@\7\f\2\2@B\3\2\2\2A\63\3\2\2\2AB\3\2\2")
+        buf.write("\2BC\3\2\2\2CI\b\4\1\2DE\5\f\7\2EF\b\4\1\2FH\3\2\2\2G")
+        buf.write("D\3\2\2\2HK\3\2\2\2IG\3\2\2\2IJ\3\2\2\2JL\3\2\2\2KI\3")
+        buf.write("\2\2\2LM\b\4\1\2MR\3\2\2\2NO\5\16\b\2OP\b\4\1\2PR\3\2")
+        buf.write("\2\2Q\60\3\2\2\2QN\3\2\2\2R\7\3\2\2\2ST\5\24\13\2TU\b")
+        buf.write("\5\1\2U\t\3\2\2\2VW\5\20\t\2WX\7\20\2\2XY\5\24\13\2YZ")
+        buf.write("\b\6\1\2Z\13\3\2\2\2[\\\7\6\2\2\\]\7\21\2\2]^\5\24\13")
+        buf.write("\2^_\b\7\1\2_\r\3\2\2\2`a\7\35\2\2ab\b\b\1\2b\17\3\2\2")
+        buf.write("\2cd\t\2\2\2de\b\t\1\2e\21\3\2\2\2fj\5\26\f\2gj\5\30\r")
+        buf.write("\2hj\5\32\16\2if\3\2\2\2ig\3\2\2\2ih\3\2\2\2jm\3\2\2\2")
+        buf.write("ki\3\2\2\2kl\3\2\2\2l\23\3\2\2\2mk\3\2\2\2no\5\26\f\2")
+        buf.write("ov\b\13\1\2pq\7\22\2\2qr\5\26\f\2rs\b\13\1\2su\3\2\2\2")
+        buf.write("tp\3\2\2\2ux\3\2\2\2vt\3\2\2\2vw\3\2\2\2wy\3\2\2\2xv\3")
+        buf.write("\2\2\2yz\b\13\1\2z\25\3\2\2\2{|\7\33\2\2|\u0080\b\f\1")
+        buf.write("\2}~\7\34\2\2~\u0080\b\f\1\2\177{\3\2\2\2\177}\3\2\2\2")
+        buf.write("\u0080\27\3\2\2\2\u0081\u0082\t\3\2\2\u0082\31\3\2\2\2")
+        buf.write("\u0083\u0084\t\4\2\2\u0084\33\3\2\2\2\13#<AIQikv\177")
         return buf.getvalue()
 
 
@@ -67,8 +79,8 @@ class unlParser ( Parser ):
                       "AOJ", "BEN", "CNT", "EQU", "ICL", "OBJ", "QUA", "LETTER", 
                       "WORD", "VALUE" ]
 
-    RULE_unlPart = 0
-    RULE_relationOccurrence = 1
+    RULE_unlGraph = 0
+    RULE_link = 1
     RULE_universalWord = 2
     RULE_headword = 3
     RULE_restriction = 4
@@ -81,9 +93,9 @@ class unlParser ( Parser ):
     RULE_punctuation = 11
     RULE_bracket = 12
 
-    ruleNames =  [ "unlPart", "relationOccurrence", "universalWord", "headword", 
-                   "restriction", "attribute", "value", "universalRelation", 
-                   "sentence", "ident", "word", "punctuation", "bracket" ]
+    ruleNames =  [ "unlGraph", "link", "universalWord", "headword", "restriction", 
+                   "attribute", "value", "universalRelation", "sentence", 
+                   "ident", "word", "punctuation", "bracket" ]
 
     EOF = Token.EOF
     T__0=1
@@ -123,57 +135,62 @@ class unlParser ( Parser ):
 
 
 
-    class UnlPartContext(ParserRuleContext):
+    class UnlGraphContext(ParserRuleContext):
         __slots__ = 'parser'
 
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.l = None # LinkContext
 
-        def relationOccurrence(self, i:int=None):
+        def link(self, i:int=None):
             if i is None:
-                return self.getTypedRuleContexts(unlParser.RelationOccurrenceContext)
+                return self.getTypedRuleContexts(unlParser.LinkContext)
             else:
-                return self.getTypedRuleContext(unlParser.RelationOccurrenceContext,i)
+                return self.getTypedRuleContext(unlParser.LinkContext,i)
 
 
         def getRuleIndex(self):
-            return unlParser.RULE_unlPart
+            return unlParser.RULE_unlGraph
 
         def enterRule(self, listener:ParseTreeListener):
-            if hasattr( listener, "enterUnlPart" ):
-                listener.enterUnlPart(self)
+            if hasattr( listener, "enterUnlGraph" ):
+                listener.enterUnlGraph(self)
 
         def exitRule(self, listener:ParseTreeListener):
-            if hasattr( listener, "exitUnlPart" ):
-                listener.exitUnlPart(self)
+            if hasattr( listener, "exitUnlGraph" ):
+                listener.exitUnlGraph(self)
 
 
 
 
-    def unlPart(self):
+    def unlGraph(self):
 
-        localctx = unlParser.UnlPartContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 0, self.RULE_unlPart)
+        localctx = unlParser.UnlGraphContext(self, self._ctx, self.state)
+        self.enterRule(localctx, 0, self.RULE_unlGraph)
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
             self.state = 26
             self.match(unlParser.T__0)
-            self.state = 28 
+            link_list = []
+            self.state = 31 
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while True:
-                self.state = 27
-                self.relationOccurrence()
-                self.state = 30 
+                self.state = 28
+                localctx.l = self.link()
+                link_list.append(localctx.l.out)
+                self.state = 33 
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
                 if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << unlParser.AND) | (1 << unlParser.AOJ) | (1 << unlParser.BEN) | (1 << unlParser.CNT) | (1 << unlParser.EQU) | (1 << unlParser.ICL) | (1 << unlParser.OBJ) | (1 << unlParser.QUA))) != 0)):
                     break
 
-            self.state = 32
+            self.state = 35
             self.match(unlParser.T__1)
+            localctx.out = unl.Graph(link_list)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -183,20 +200,30 @@ class unlParser ( Parser ):
         return localctx
 
 
-    class RelationOccurrenceContext(ParserRuleContext):
+    class LinkContext(ParserRuleContext):
         __slots__ = 'parser'
 
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.r = None # UniversalRelationContext
+            self.uw1 = None # UniversalWordContext
+            self.uw2 = None # UniversalWordContext
+
+        def LP(self):
+            return self.getToken(unlParser.LP, 0)
+
+        def COMMA(self):
+            return self.getToken(unlParser.COMMA, 0)
+
+        def RP(self):
+            return self.getToken(unlParser.RP, 0)
 
         def universalRelation(self):
             return self.getTypedRuleContext(unlParser.UniversalRelationContext,0)
 
 
-        def LP(self):
-            return self.getToken(unlParser.LP, 0)
-
         def universalWord(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(unlParser.UniversalWordContext)
@@ -204,44 +231,39 @@ class unlParser ( Parser ):
                 return self.getTypedRuleContext(unlParser.UniversalWordContext,i)
 
 
-        def COMMA(self):
-            return self.getToken(unlParser.COMMA, 0)
-
-        def RP(self):
-            return self.getToken(unlParser.RP, 0)
-
         def getRuleIndex(self):
-            return unlParser.RULE_relationOccurrence
+            return unlParser.RULE_link
 
         def enterRule(self, listener:ParseTreeListener):
-            if hasattr( listener, "enterRelationOccurrence" ):
-                listener.enterRelationOccurrence(self)
+            if hasattr( listener, "enterLink" ):
+                listener.enterLink(self)
 
         def exitRule(self, listener:ParseTreeListener):
-            if hasattr( listener, "exitRelationOccurrence" ):
-                listener.exitRelationOccurrence(self)
+            if hasattr( listener, "exitLink" ):
+                listener.exitLink(self)
 
 
 
 
-    def relationOccurrence(self):
+    def link(self):
 
-        localctx = unlParser.RelationOccurrenceContext(self, self._ctx, self.state)
-        self.enterRule(localctx, 2, self.RULE_relationOccurrence)
+        localctx = unlParser.LinkContext(self, self._ctx, self.state)
+        self.enterRule(localctx, 2, self.RULE_link)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 34
-            self.universalRelation()
-            self.state = 35
-            self.match(unlParser.LP)
-            self.state = 36
-            self.universalWord()
-            self.state = 37
-            self.match(unlParser.COMMA)
             self.state = 38
-            self.universalWord()
+            localctx.r = self.universalRelation()
             self.state = 39
+            self.match(unlParser.LP)
+            self.state = 40
+            localctx.uw1 = self.universalWord()
+            self.state = 41
+            self.match(unlParser.COMMA)
+            self.state = 42
+            localctx.uw2 = self.universalWord()
+            self.state = 43
             self.match(unlParser.RP)
+            localctx.out = unl.Link(localctx.r.out, localctx.uw1.out, localctx.uw2.out)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -257,6 +279,12 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.h = None # HeadwordContext
+            self.r1 = None # RestrictionContext
+            self.r2 = None # RestrictionContext
+            self.a = None # AttributeContext
+            self.v = None # ValueContext
 
         def headword(self):
             return self.getTypedRuleContext(unlParser.HeadwordContext,0)
@@ -265,6 +293,9 @@ class unlParser ( Parser ):
         def LP(self):
             return self.getToken(unlParser.LP, 0)
 
+        def RP(self):
+            return self.getToken(unlParser.RP, 0)
+
         def restriction(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(unlParser.RestrictionContext)
@@ -272,9 +303,6 @@ class unlParser ( Parser ):
                 return self.getTypedRuleContext(unlParser.RestrictionContext,i)
 
 
-        def RP(self):
-            return self.getToken(unlParser.RP, 0)
-
         def attribute(self, i:int=None):
             if i is None:
                 return self.getTypedRuleContexts(unlParser.AttributeContext)
@@ -312,52 +340,60 @@ class unlParser ( Parser ):
         self.enterRule(localctx, 4, self.RULE_universalWord)
         self._la = 0 # Token type
         try:
-            self.state = 62
+            self.state = 79
             self._errHandler.sync(self)
             token = self._input.LA(1)
             if token in [unlParser.LETTER, unlParser.WORD]:
                 self.enterOuterAlt(localctx, 1)
-                self.state = 41
-                self.headword()
-                self.state = 53
+                self.state = 46
+                localctx.h = self.headword()
+                headword =  localctx.h.out
+                restriction_list = []
+                self.state = 63
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
                 if _la==unlParser.LP:
-                    self.state = 42
+                    self.state = 49
                     self.match(unlParser.LP)
-                    self.state = 43
-                    self.restriction()
-                    self.state = 48
+                    self.state = 50
+                    localctx.r1 = self.restriction()
+                    restriction_list.append(localctx.r1.out)
+                    self.state = 58
                     self._errHandler.sync(self)
                     _la = self._input.LA(1)
                     while _la==unlParser.COMMA:
-                        self.state = 44
+                        self.state = 52
                         self.match(unlParser.COMMA)
-                        self.state = 45
-                        self.restriction()
-                        self.state = 50
+                        self.state = 53
+                        localctx.r2 = self.restriction()
+                        restriction_list.append(localctx.r2.out)
+                        self.state = 60
                         self._errHandler.sync(self)
                         _la = self._input.LA(1)
 
-                    self.state = 51
+                    self.state = 61
                     self.match(unlParser.RP)
 
 
-                self.state = 58
+                attr_list = []
+                self.state = 71
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
                 while _la==unlParser.DOT:
-                    self.state = 55
-                    self.attribute()
-                    self.state = 60
+                    self.state = 66
+                    localctx.a = self.attribute()
+                    attr_list.append(localctx.a.out)
+                    self.state = 73
                     self._errHandler.sync(self)
                     _la = self._input.LA(1)
 
+                localctx.out = unl.UniversalWord(headword, restriction_list, attr_list)
                 pass
             elif token in [unlParser.VALUE]:
                 self.enterOuterAlt(localctx, 2)
-                self.state = 61
-                self.value()
+                self.state = 76
+                localctx.v = self.value()
+                localctx.out = unl.UniversalWord(localctx.v.out, [], [])
                 pass
             else:
                 raise NoViableAltException(self)
@@ -377,6 +413,8 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.i = None # IdentContext
 
         def ident(self):
             return self.getTypedRuleContext(unlParser.IdentContext,0)
@@ -402,8 +440,9 @@ class unlParser ( Parser ):
         self.enterRule(localctx, 6, self.RULE_headword)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 64
-            self.ident()
+            self.state = 81
+            localctx.i = self.ident()
+            localctx.out = unl.Headword(localctx.i.out)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -419,14 +458,17 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.r = None # UniversalRelationContext
+            self.i = None # IdentContext
+
+        def GREATER(self):
+            return self.getToken(unlParser.GREATER, 0)
 
         def universalRelation(self):
             return self.getTypedRuleContext(unlParser.UniversalRelationContext,0)
 
 
-        def GREATER(self):
-            return self.getToken(unlParser.GREATER, 0)
-
         def ident(self):
             return self.getTypedRuleContext(unlParser.IdentContext,0)
 
@@ -451,12 +493,13 @@ class unlParser ( Parser ):
         self.enterRule(localctx, 8, self.RULE_restriction)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 66
-            self.universalRelation()
-            self.state = 67
+            self.state = 84
+            localctx.r = self.universalRelation()
+            self.state = 85
             self.match(unlParser.GREATER)
-            self.state = 68
-            self.ident()
+            self.state = 86
+            localctx.i = self.ident()
+            localctx.out = unl.Restriction(localctx.r.out, localctx.i.out)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -472,6 +515,8 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.i = None # IdentContext
 
         def DOT(self):
             return self.getToken(unlParser.DOT, 0)
@@ -503,12 +548,13 @@ class unlParser ( Parser ):
         self.enterRule(localctx, 10, self.RULE_attribute)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 70
+            self.state = 89
             self.match(unlParser.DOT)
-            self.state = 71
+            self.state = 90
             self.match(unlParser.AT)
-            self.state = 72
-            self.ident()
+            self.state = 91
+            localctx.i = self.ident()
+            localctx.out = unl.Attribute(localctx.i.out)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -524,6 +570,8 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.v = None # Token
 
         def VALUE(self):
             return self.getToken(unlParser.VALUE, 0)
@@ -548,8 +596,9 @@ class unlParser ( Parser ):
         self.enterRule(localctx, 12, self.RULE_value)
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 74
-            self.match(unlParser.VALUE)
+            self.state = 94
+            localctx.v = self.match(unlParser.VALUE)
+            localctx.out = unl.Ident((None if localctx.v is None else localctx.v.text))
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -565,6 +614,8 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.r = None # Token
 
         def AND(self):
             return self.getToken(unlParser.AND, 0)
@@ -611,13 +662,15 @@ class unlParser ( Parser ):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 76
+            self.state = 97
+            localctx.r = self._input.LT(1)
             _la = self._input.LA(1)
             if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << unlParser.AND) | (1 << unlParser.AOJ) | (1 << unlParser.BEN) | (1 << unlParser.CNT) | (1 << unlParser.EQU) | (1 << unlParser.ICL) | (1 << unlParser.OBJ) | (1 << unlParser.QUA))) != 0)):
-                self._errHandler.recoverInline(self)
+                localctx.r = self._errHandler.recoverInline(self)
             else:
                 self._errHandler.reportMatch(self)
                 self.consume()
+            localctx.out = unl.Relation((None if localctx.r is None else localctx.r.text))
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -676,29 +729,29 @@ class unlParser ( Parser ):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 83
+            self.state = 105
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << unlParser.DOT) | (1 << unlParser.COMMA) | (1 << unlParser.SEMCOL) | (1 << unlParser.COLON) | (1 << unlParser.DASH) | (1 << unlParser.LP) | (1 << unlParser.RP) | (1 << unlParser.LC) | (1 << unlParser.RC) | (1 << unlParser.LETTER) | (1 << unlParser.WORD))) != 0):
-                self.state = 81
+                self.state = 103
                 self._errHandler.sync(self)
                 token = self._input.LA(1)
                 if token in [unlParser.LETTER, unlParser.WORD]:
-                    self.state = 78
+                    self.state = 100
                     self.word()
                     pass
                 elif token in [unlParser.DOT, unlParser.COMMA, unlParser.SEMCOL, unlParser.COLON, unlParser.DASH]:
-                    self.state = 79
+                    self.state = 101
                     self.punctuation()
                     pass
                 elif token in [unlParser.LP, unlParser.RP, unlParser.LC, unlParser.RC]:
-                    self.state = 80
+                    self.state = 102
                     self.bracket()
                     pass
                 else:
                     raise NoViableAltException(self)
 
-                self.state = 85
+                self.state = 107
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
@@ -717,6 +770,9 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.w1 = None # WordContext
+            self.w2 = None # WordContext
 
         def word(self, i:int=None):
             if i is None:
@@ -752,20 +808,23 @@ class unlParser ( Parser ):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 86
-            self.word()
-            self.state = 91
+            self.state = 108
+            localctx.w1 = self.word()
+            ident = localctx.w1.out
+            self.state = 116
             self._errHandler.sync(self)
             _la = self._input.LA(1)
             while _la==unlParser.UNDERSCORE:
-                self.state = 87
+                self.state = 110
                 self.match(unlParser.UNDERSCORE)
-                self.state = 88
-                self.word()
-                self.state = 93
+                self.state = 111
+                localctx.w2 = self.word()
+                ident += "_" + localctx.w2.out
+                self.state = 118
                 self._errHandler.sync(self)
                 _la = self._input.LA(1)
 
+            localctx.out = unl.Ident(ident)
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -781,6 +840,9 @@ class unlParser ( Parser ):
         def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
             super().__init__(parent, invokingState)
             self.parser = parser
+            self.out = None
+            self.l = None # Token
+            self.w = None # Token
 
         def LETTER(self):
             return self.getToken(unlParser.LETTER, 0)
@@ -806,16 +868,25 @@ class unlParser ( Parser ):
 
         localctx = unlParser.WordContext(self, self._ctx, self.state)
         self.enterRule(localctx, 20, self.RULE_word)
-        self._la = 0 # Token type
         try:
-            self.enterOuterAlt(localctx, 1)
-            self.state = 94
-            _la = self._input.LA(1)
-            if not(_la==unlParser.LETTER or _la==unlParser.WORD):
-                self._errHandler.recoverInline(self)
+            self.state = 125
+            self._errHandler.sync(self)
+            token = self._input.LA(1)
+            if token in [unlParser.LETTER]:
+                self.enterOuterAlt(localctx, 1)
+                self.state = 121
+                localctx.l = self.match(unlParser.LETTER)
+                localctx.out = (None if localctx.l is None else localctx.l.text)
+                pass
+            elif token in [unlParser.WORD]:
+                self.enterOuterAlt(localctx, 2)
+                self.state = 123
+                localctx.w = self.match(unlParser.WORD)
+                localctx.out = (None if localctx.w is None else localctx.w.text)
+                pass
             else:
-                self._errHandler.reportMatch(self)
-                self.consume()
+                raise NoViableAltException(self)
+
         except RecognitionException as re:
             localctx.exception = re
             self._errHandler.reportError(self, re)
@@ -868,7 +939,7 @@ class unlParser ( Parser ):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 96
+            self.state = 127
             _la = self._input.LA(1)
             if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << unlParser.DOT) | (1 << unlParser.COMMA) | (1 << unlParser.SEMCOL) | (1 << unlParser.COLON) | (1 << unlParser.DASH))) != 0)):
                 self._errHandler.recoverInline(self)
@@ -924,7 +995,7 @@ class unlParser ( Parser ):
         self._la = 0 # Token type
         try:
             self.enterOuterAlt(localctx, 1)
-            self.state = 98
+            self.state = 129
             _la = self._input.LA(1)
             if not((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << unlParser.LP) | (1 << unlParser.RP) | (1 << unlParser.LC) | (1 << unlParser.RC))) != 0)):
                 self._errHandler.recoverInline(self)
diff --git a/parse.py b/parse.py
index 174ba842da2325c51935d16f4869d41ba80b130d..0c236429dcb1f3a4567dd520f5c24d5e6eae3132 100644
--- a/parse.py
+++ b/parse.py
@@ -97,7 +97,7 @@ def parse_org(input):
     parser = instantiate_lexer_parser(input, orgLexer, orgParser)
     print("--- Parse origin sentence")
     tree = parser.orgPart()
-    print("----- resulting tree:\n" + tree.toStringTree(recog=parser))   
+    print("----- resulting tree:\n" + tree.toStringTree(recog=parser))
     
 
 def parse_unl(input):
@@ -112,8 +112,11 @@ def parse_unl(input):
     # -- Parse UNL part
     parser = instantiate_lexer_parser(input, unlLexer, unlParser)
     print("--- Parse UNL representation")
-    tree = parser.unlPart()
+    tree = parser.unlGraph()
     print("----- resulting tree:\n" + tree.toStringTree(recog=parser))  
+    unl = tree.out
+    
+    return unl
     
      
 #==============================================================================
@@ -140,7 +143,8 @@ def main(argv):
     
     # -- UNL Parsing (Sentence UNL Part)
     print("-- UNL Parsing (UNL Representation) ")
-    parse_unl(InputStream(unl_part)) 
+    unl = parse_unl(InputStream(unl_part))
+    print("----- UNL string:\n" + unl.to_string())
 
 
 if __name__ == '__main__':