glaemscribe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +19 -0
- data/bin/glaemscribe +307 -0
- data/glaemresources/charsets/cirth_ds.cst +205 -0
- data/glaemresources/charsets/sarati_eldamar.cst +256 -0
- data/glaemresources/charsets/tengwar_ds.cst +318 -0
- data/glaemresources/charsets/unicode_gothic.cst +64 -0
- data/glaemresources/charsets/unicode_runes.cst +120 -0
- data/glaemresources/modes/adunaic.glaem +251 -0
- data/glaemresources/modes/blackspeech-annatar.glaem +318 -0
- data/glaemresources/modes/blackspeech.glaem +260 -0
- data/glaemresources/modes/gothic.glaem +78 -0
- data/glaemresources/modes/khuzdul.glaem +141 -0
- data/glaemresources/modes/mercian.glaem +419 -0
- data/glaemresources/modes/oldnorse-medieval.glaem +127 -0
- data/glaemresources/modes/quenya-sarati.glaem +320 -0
- data/glaemresources/modes/quenya.glaem +307 -0
- data/glaemresources/modes/sindarin-beleriand.glaem +285 -0
- data/glaemresources/modes/sindarin-classical.glaem +276 -0
- data/glaemresources/modes/sindarin-daeron.glaem +182 -0
- data/glaemresources/modes/telerin.glaem +302 -0
- data/glaemresources/modes/valarin-sarati.glaem +210 -0
- data/glaemresources/modes/westron.glaem +340 -0
- data/glaemresources/modes/westsaxon.glaem +342 -0
- data/lib/api/charset.rb +84 -0
- data/lib/api/charset_parser.rb +55 -0
- data/lib/api/constants.rb +29 -0
- data/lib/api/debug.rb +36 -0
- data/lib/api/eval.rb +268 -0
- data/lib/api/fragment.rb +113 -0
- data/lib/api/glaeml.rb +200 -0
- data/lib/api/if_tree.rb +96 -0
- data/lib/api/mode.rb +112 -0
- data/lib/api/mode_parser.rb +314 -0
- data/lib/api/option.rb +64 -0
- data/lib/api/post_processor/reverse.rb +36 -0
- data/lib/api/pre_processor/downcase.rb +35 -0
- data/lib/api/pre_processor/elvish_numbers.rb +47 -0
- data/lib/api/pre_processor/rxsubstitute.rb +40 -0
- data/lib/api/pre_processor/substitute.rb +38 -0
- data/lib/api/pre_processor/up_down_tehta_split.rb +138 -0
- data/lib/api/resource_manager.rb +130 -0
- data/lib/api/rule.rb +99 -0
- data/lib/api/rule_group.rb +159 -0
- data/lib/api/sheaf.rb +70 -0
- data/lib/api/sheaf_chain.rb +86 -0
- data/lib/api/sheaf_chain_iterator.rb +108 -0
- data/lib/api/sub_rule.rb +40 -0
- data/lib/api/transcription_pre_post_processor.rb +118 -0
- data/lib/api/transcription_processor.rb +137 -0
- data/lib/api/transcription_tree_node.rb +91 -0
- data/lib/glaemscribe.rb +70 -0
- metadata +112 -0
    
        data/lib/api/glaeml.rb
    ADDED
    
    | @@ -0,0 +1,200 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Glǽmscribe (also written Glaemscribe) is a software dedicated to
         | 
| 4 | 
            +
            # the transcription of texts between writing systems, and more 
         | 
| 5 | 
            +
            # specifically dedicated to the transcription of J.R.R. Tolkien's 
         | 
| 6 | 
            +
            # invented languages to some of his devised writing systems.
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            # Copyright (C) 2015 Benjamin Babut (Talagan).
         | 
| 9 | 
            +
            # 
         | 
| 10 | 
            +
            # This program is free software: you can redistribute it and/or modify
         | 
| 11 | 
            +
            # it under the terms of the GNU Affero General Public License as published by
         | 
| 12 | 
            +
            # the Free Software Foundation, either version 3 of the License, or
         | 
| 13 | 
            +
            # any later version.
         | 
| 14 | 
            +
            # 
         | 
| 15 | 
            +
            # This program is distributed in the hope that it will be useful,
         | 
| 16 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 17 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         | 
| 18 | 
            +
            # GNU Affero General Public License for more details.
         | 
| 19 | 
            +
            # 
         | 
| 20 | 
            +
            # You should have received a copy of the GNU Affero General Public License
         | 
| 21 | 
            +
            # along with this program.  If not, see <http://www.gnu.org/licenses/>.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module Glaemscribe
         | 
| 24 | 
            +
              module API
         | 
| 25 | 
            +
                module Glaeml
         | 
| 26 | 
            +
                  
         | 
| 27 | 
            +
                  class Error
         | 
| 28 | 
            +
                    attr_accessor :text
         | 
| 29 | 
            +
                    attr_accessor :line
         | 
| 30 | 
            +
                    
         | 
| 31 | 
            +
                    def initialize(line,text)
         | 
| 32 | 
            +
                      @line = line
         | 
| 33 | 
            +
                      @text = text
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  
         | 
| 37 | 
            +
                  class Document
         | 
| 38 | 
            +
                    attr_accessor :root_node
         | 
| 39 | 
            +
                    attr_accessor :errors
         | 
| 40 | 
            +
                      
         | 
| 41 | 
            +
                    def initialize
         | 
| 42 | 
            +
                      @errors = []
         | 
| 43 | 
            +
                    end        
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                  class Node
         | 
| 47 | 
            +
                    
         | 
| 48 | 
            +
                    class Type
         | 
| 49 | 
            +
                      Text            = 0
         | 
| 50 | 
            +
                      ElementInline   = 1
         | 
| 51 | 
            +
                      ElementBlock    = 2
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                    
         | 
| 54 | 
            +
                    def text?
         | 
| 55 | 
            +
                      @type == Type::Text
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                    
         | 
| 58 | 
            +
                    def element?
         | 
| 59 | 
            +
                      @type == Type::ElementInline || @type == Type::ElementBlock
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                    
         | 
| 62 | 
            +
                    def initialize(line, type, name)
         | 
| 63 | 
            +
                      @line         = line
         | 
| 64 | 
            +
                      @type         = type
         | 
| 65 | 
            +
                      @name         = name
         | 
| 66 | 
            +
                      @args         = []
         | 
| 67 | 
            +
                      @children     = []
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    
         | 
| 70 | 
            +
                    def pathfind_crawl(apath, found)
         | 
| 71 | 
            +
                    
         | 
| 72 | 
            +
                      children.each{ |c|
         | 
| 73 | 
            +
                        if(c.name == apath[0])
         | 
| 74 | 
            +
                          if apath.count == 1
         | 
| 75 | 
            +
                            found << c
         | 
| 76 | 
            +
                          else
         | 
| 77 | 
            +
                            bpath = apath.dup
         | 
| 78 | 
            +
                            bpath.shift
         | 
| 79 | 
            +
                            c.pathfind_crawl(bpath, found)
         | 
| 80 | 
            +
                          end
         | 
| 81 | 
            +
                        end
         | 
| 82 | 
            +
                      }
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                    
         | 
| 85 | 
            +
                    def gpath(path)
         | 
| 86 | 
            +
                      apath = path.split(".")
         | 
| 87 | 
            +
                      found = []
         | 
| 88 | 
            +
                      pathfind_crawl(apath, found)
         | 
| 89 | 
            +
                      found
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                    
         | 
| 92 | 
            +
                    attr_accessor :line
         | 
| 93 | 
            +
                    
         | 
| 94 | 
            +
                    attr_reader   :type
         | 
| 95 | 
            +
                    attr_reader   :name
         | 
| 96 | 
            +
                   
         | 
| 97 | 
            +
                    attr_accessor :args    
         | 
| 98 | 
            +
                    attr_reader   :children
         | 
| 99 | 
            +
                    
         | 
| 100 | 
            +
                    attr_accessor :parent_node
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                  
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  
         | 
| 105 | 
            +
                  class Parser   
         | 
| 106 | 
            +
                     
         | 
| 107 | 
            +
                    def add_text_node(lnum, text)
         | 
| 108 | 
            +
                      n             = Node.new(lnum, Node::Type::Text, "root")
         | 
| 109 | 
            +
                      n.args        << text
         | 
| 110 | 
            +
                      n.parent_node = @current_parent_node     
         | 
| 111 | 
            +
                      @current_parent_node.children << n   
         | 
| 112 | 
            +
                    end
         | 
| 113 | 
            +
                  
         | 
| 114 | 
            +
                    def parse(raw_data)
         | 
| 115 | 
            +
                      raw_data.gsub!(/\\\*\*(.*?)\*\*\\/m) { |found|
         | 
| 116 | 
            +
                        "\n" * found.count("\n")
         | 
| 117 | 
            +
                      }
         | 
| 118 | 
            +
                    
         | 
| 119 | 
            +
                      lnum = 0   
         | 
| 120 | 
            +
                    
         | 
| 121 | 
            +
                      @document             = Document.new
         | 
| 122 | 
            +
                      @document.root_node   = Node.new(lnum, Node::Type::ElementBlock, nil)
         | 
| 123 | 
            +
                    
         | 
| 124 | 
            +
                      @current_parent_node  = @document.root_node
         | 
| 125 | 
            +
                       
         | 
| 126 | 
            +
                      raw_data.lines.each{ |l|
         | 
| 127 | 
            +
                        lnum  += 1
         | 
| 128 | 
            +
               
         | 
| 129 | 
            +
                        l     = l.strip
         | 
| 130 | 
            +
                        add_text_node(lnum, l) and next if l.empty?
         | 
| 131 | 
            +
                              
         | 
| 132 | 
            +
                        if(l[0..0] == "\\")
         | 
| 133 | 
            +
                          if(l.length == 1)
         | 
| 134 | 
            +
                            @document.errors << Error.new(lnum, "Incomplete node.")
         | 
| 135 | 
            +
                          else
         | 
| 136 | 
            +
                            if(l[1..1] == "\\") # First backslash is escaped              
         | 
| 137 | 
            +
                            
         | 
| 138 | 
            +
                              add_text_node(lnum, l[1..-1])              
         | 
| 139 | 
            +
                            
         | 
| 140 | 
            +
                            elsif l =~ /^(\\beg\s+)/
         | 
| 141 | 
            +
                            
         | 
| 142 | 
            +
                              rest    = $'.strip
         | 
| 143 | 
            +
                              args    = []
         | 
| 144 | 
            +
                              name    = "???"
         | 
| 145 | 
            +
                              if(! (rest =~ /^([a-z_]+)/) )
         | 
| 146 | 
            +
                                @document.errors << Error.new(lnum, "Bad element name #{rest}.")
         | 
| 147 | 
            +
                              else
         | 
| 148 | 
            +
                                name = $1
         | 
| 149 | 
            +
                                args = $'.shellsplit                
         | 
| 150 | 
            +
                              end
         | 
| 151 | 
            +
                            
         | 
| 152 | 
            +
                              n             = Node.new(lnum, Node::Type::ElementBlock, name)
         | 
| 153 | 
            +
                              n.args        += args
         | 
| 154 | 
            +
                              n.parent_node = @current_parent_node
         | 
| 155 | 
            +
                          
         | 
| 156 | 
            +
                              @current_parent_node.children << n
         | 
| 157 | 
            +
                              @current_parent_node           = n                
         | 
| 158 | 
            +
                                          
         | 
| 159 | 
            +
                            elsif l =~ /^(\\end(\s|$))/              
         | 
| 160 | 
            +
                            
         | 
| 161 | 
            +
                              if !@current_parent_node.parent_node
         | 
| 162 | 
            +
                                @document.errors << Error.new(lnum, "Element 'end' unexpected.")
         | 
| 163 | 
            +
                              elsif $'.strip != ""
         | 
| 164 | 
            +
                                @document.errors << Error.new(lnum, "Element 'end' should not have arguments.")
         | 
| 165 | 
            +
                              else
         | 
| 166 | 
            +
                                @current_parent_node = @current_parent_node.parent_node
         | 
| 167 | 
            +
                              end
         | 
| 168 | 
            +
                          
         | 
| 169 | 
            +
                            else
         | 
| 170 | 
            +
                            
         | 
| 171 | 
            +
                              # Read the name of the node
         | 
| 172 | 
            +
                              l             = l[1..-1]         
         | 
| 173 | 
            +
                              l             =~ /^([a-z_]+)/
         | 
| 174 | 
            +
                              name          = $1
         | 
| 175 | 
            +
                              args          = $'.shellsplit
         | 
| 176 | 
            +
                            
         | 
| 177 | 
            +
                              n             = Node.new(lnum, Node::Type::ElementInline, name)
         | 
| 178 | 
            +
                              n.args        += args
         | 
| 179 | 
            +
                              n.parent_node = @current_parent_node
         | 
| 180 | 
            +
                            
         | 
| 181 | 
            +
                              @current_parent_node.children << n     
         | 
| 182 | 
            +
                                       
         | 
| 183 | 
            +
                            end
         | 
| 184 | 
            +
                          end
         | 
| 185 | 
            +
                        else
         | 
| 186 | 
            +
                          add_text_node(lnum, l)
         | 
| 187 | 
            +
                        end
         | 
| 188 | 
            +
                      }
         | 
| 189 | 
            +
                    
         | 
| 190 | 
            +
                      if @current_parent_node != @document.root_node
         | 
| 191 | 
            +
                        @document.errors << Error.new(lnum, "Missing 'end' element.")
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
                    
         | 
| 194 | 
            +
                      return @document
         | 
| 195 | 
            +
                    
         | 
| 196 | 
            +
                    end
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
              end
         | 
| 200 | 
            +
            end
         | 
    
        data/lib/api/if_tree.rb
    ADDED
    
    | @@ -0,0 +1,96 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Glǽmscribe (also written Glaemscribe) is a software dedicated to
         | 
| 4 | 
            +
            # the transcription of texts between writing systems, and more 
         | 
| 5 | 
            +
            # specifically dedicated to the transcription of J.R.R. Tolkien's 
         | 
| 6 | 
            +
            # invented languages to some of his devised writing systems.
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            # Copyright (C) 2015 Benjamin Babut (Talagan).
         | 
| 9 | 
            +
            # 
         | 
| 10 | 
            +
            # This program is free software: you can redistribute it and/or modify
         | 
| 11 | 
            +
            # it under the terms of the GNU Affero General Public License as published by
         | 
| 12 | 
            +
            # the Free Software Foundation, either version 3 of the License, or
         | 
| 13 | 
            +
            # any later version.
         | 
| 14 | 
            +
            # 
         | 
| 15 | 
            +
            # This program is distributed in the hope that it will be useful,
         | 
| 16 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 17 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         | 
| 18 | 
            +
            # GNU Affero General Public License for more details.
         | 
| 19 | 
            +
            # 
         | 
| 20 | 
            +
            # You should have received a copy of the GNU Affero General Public License
         | 
| 21 | 
            +
            # along with this program.  If not, see <http://www.gnu.org/licenses/>.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module Glaemscribe
         | 
| 24 | 
            +
              module API
         | 
| 25 | 
            +
                module IfTree
         | 
| 26 | 
            +
                  
         | 
| 27 | 
            +
                  class IfCond
         | 
| 28 | 
            +
                    attr_accessor :line, :expression, :parent_if_term, :child_code_block
         | 
| 29 | 
            +
                    def initialize(line, parent_if_term, expression)
         | 
| 30 | 
            +
                      @parent_if_term     = parent_if_term
         | 
| 31 | 
            +
                      @expression         = expression
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                  
         | 
| 35 | 
            +
                  class Term
         | 
| 36 | 
            +
                    attr_accessor :parent_code_block
         | 
| 37 | 
            +
                    def initialize(parent_code_block)
         | 
| 38 | 
            +
                      @parent_code_block = parent_code_block  
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                    def is_code_lines?
         | 
| 41 | 
            +
                      false
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                    def is_pre_post_processor_operators?
         | 
| 44 | 
            +
                      false
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             
         | 
| 48 | 
            +
                  class IfTerm < Term
         | 
| 49 | 
            +
                    attr_accessor :if_conds
         | 
| 50 | 
            +
                    def initialize(parent_code_block)
         | 
| 51 | 
            +
                      super(parent_code_block)  
         | 
| 52 | 
            +
                      @if_conds = []
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                  
         | 
| 56 | 
            +
                  class CodeLine
         | 
| 57 | 
            +
                    attr_accessor :expression, :line
         | 
| 58 | 
            +
                    def initialize(expression, line)
         | 
| 59 | 
            +
                      @expression   = expression
         | 
| 60 | 
            +
                      @line         = line
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                  
         | 
| 64 | 
            +
                  class PrePostProcessorOperatorsTerm < Term
         | 
| 65 | 
            +
                    attr_accessor :operators
         | 
| 66 | 
            +
                    def initialize(parent_code_block)
         | 
| 67 | 
            +
                      super(parent_code_block)
         | 
| 68 | 
            +
                      @operators = []
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                    def is_pre_post_processor_operators?
         | 
| 71 | 
            +
                      true
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                  
         | 
| 75 | 
            +
                  class CodeLinesTerm < Term
         | 
| 76 | 
            +
                    attr_accessor :code_lines
         | 
| 77 | 
            +
                    def initialize(parent_code_block)
         | 
| 78 | 
            +
                      super(parent_code_block)
         | 
| 79 | 
            +
                      @code_lines      = []
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                    def is_code_lines?
         | 
| 82 | 
            +
                      true
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                  
         | 
| 86 | 
            +
                  class CodeBlock
         | 
| 87 | 
            +
                    attr_accessor :terms, :parent_if_cond
         | 
| 88 | 
            +
                    def initialize(parent_if_cond = nil)
         | 
| 89 | 
            +
                      @parent_if_cond = parent_if_cond
         | 
| 90 | 
            +
                      @terms          = []
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
            end
         | 
    
        data/lib/api/mode.rb
    ADDED
    
    | @@ -0,0 +1,112 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Glǽmscribe (also written Glaemscribe) is a software dedicated to
         | 
| 4 | 
            +
            # the transcription of texts between writing systems, and more 
         | 
| 5 | 
            +
            # specifically dedicated to the transcription of J.R.R. Tolkien's 
         | 
| 6 | 
            +
            # invented languages to some of his devised writing systems.
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            # Copyright (C) 2015 Benjamin Babut (Talagan).
         | 
| 9 | 
            +
            # 
         | 
| 10 | 
            +
            # This program is free software: you can redistribute it and/or modify
         | 
| 11 | 
            +
            # it under the terms of the GNU Affero General Public License as published by
         | 
| 12 | 
            +
            # the Free Software Foundation, either version 3 of the License, or
         | 
| 13 | 
            +
            # any later version.
         | 
| 14 | 
            +
            # 
         | 
| 15 | 
            +
            # This program is distributed in the hope that it will be useful,
         | 
| 16 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 17 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         | 
| 18 | 
            +
            # GNU Affero General Public License for more details.
         | 
| 19 | 
            +
            # 
         | 
| 20 | 
            +
            # You should have received a copy of the GNU Affero General Public License
         | 
| 21 | 
            +
            # along with this program.  If not, see <http://www.gnu.org/licenses/>.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module Glaemscribe
         | 
| 24 | 
            +
              module API
         | 
| 25 | 
            +
                class Mode
         | 
| 26 | 
            +
                  
         | 
| 27 | 
            +
                  attr_accessor :errors
         | 
| 28 | 
            +
                  attr_accessor :warnings
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                  attr_accessor :name
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  attr_accessor :language, :writing, :human_name, :authors, :version 
         | 
| 33 | 
            +
                  attr_accessor :pre_processor, :processor, :post_processor
         | 
| 34 | 
            +
                  
         | 
| 35 | 
            +
                  attr_accessor :options
         | 
| 36 | 
            +
                  
         | 
| 37 | 
            +
                  attr_accessor :supported_charsets
         | 
| 38 | 
            +
                  attr_accessor :default_charset
         | 
| 39 | 
            +
                     
         | 
| 40 | 
            +
                  def initialize(name)
         | 
| 41 | 
            +
                    @name               = name
         | 
| 42 | 
            +
                    @errors             = []
         | 
| 43 | 
            +
                    @warnings           = []
         | 
| 44 | 
            +
                    @supported_charsets = {}
         | 
| 45 | 
            +
                    @options            = {}
         | 
| 46 | 
            +
                   
         | 
| 47 | 
            +
                    @pre_processor    = TranscriptionPreProcessor.new(self)
         | 
| 48 | 
            +
                    @processor        = TranscriptionProcessor.new(self)
         | 
| 49 | 
            +
                    @post_processor   = TranscriptionPostProcessor.new(self)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                  
         | 
| 52 | 
            +
                  def to_s
         | 
| 53 | 
            +
                    " <Mode #{name}: Language '#{language}', Writing '#{writing}', Human Name '#{human_name}', Authors '#{authors}', Version '#{version}'> "
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                  
         | 
| 56 | 
            +
                  def inspect
         | 
| 57 | 
            +
                    to_s
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  
         | 
| 60 | 
            +
                  def finalize(options={})
         | 
| 61 | 
            +
                          
         | 
| 62 | 
            +
                    # Hash: option_name => value_name
         | 
| 63 | 
            +
                    trans_options = {}
         | 
| 64 | 
            +
                    
         | 
| 65 | 
            +
                    # Build default options
         | 
| 66 | 
            +
                    @options.each { |oname, o|
         | 
| 67 | 
            +
                      trans_options[oname] = o.default_value_name
         | 
| 68 | 
            +
                    }   
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    # Push user options
         | 
| 71 | 
            +
                    options.each { |oname, valname|
         | 
| 72 | 
            +
                      # Check if option exists
         | 
| 73 | 
            +
                      opt = @options[oname]
         | 
| 74 | 
            +
                      next if(!opt)
         | 
| 75 | 
            +
                        
         | 
| 76 | 
            +
                      val = opt.value_for_value_name(valname)
         | 
| 77 | 
            +
                      next if val == nil
         | 
| 78 | 
            +
                        
         | 
| 79 | 
            +
                      trans_options[oname] = valname
         | 
| 80 | 
            +
                    }
         | 
| 81 | 
            +
                
         | 
| 82 | 
            +
                    trans_options_converted = {}
         | 
| 83 | 
            +
             
         | 
| 84 | 
            +
                    # Do a conversion to values space
         | 
| 85 | 
            +
                    trans_options.each{ |oname,valname| 
         | 
| 86 | 
            +
                      trans_options_converted[oname] = @options[oname].value_for_value_name(valname)
         | 
| 87 | 
            +
                    }
         | 
| 88 | 
            +
                       
         | 
| 89 | 
            +
                    @pre_processor.finalize(trans_options_converted)
         | 
| 90 | 
            +
                    @post_processor.finalize(trans_options_converted)
         | 
| 91 | 
            +
                    @processor.finalize(trans_options_converted)
         | 
| 92 | 
            +
                    
         | 
| 93 | 
            +
                    self
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                  
         | 
| 96 | 
            +
                  def transcribe(content, charset = nil)
         | 
| 97 | 
            +
                    charset = default_charset if !charset
         | 
| 98 | 
            +
                    return false, "*** No charset usable for transcription. Failed!" if !charset
         | 
| 99 | 
            +
              
         | 
| 100 | 
            +
                    ret = content.lines.map{ |l| 
         | 
| 101 | 
            +
                      l = l.strip # Clean the lines
         | 
| 102 | 
            +
                      l = @pre_processor.apply(l)
         | 
| 103 | 
            +
                      l = @processor.apply(l, charset)
         | 
| 104 | 
            +
                      l = @post_processor.apply(l)
         | 
| 105 | 
            +
                    }.join("\n")
         | 
| 106 | 
            +
                    
         | 
| 107 | 
            +
                    return true, ret
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                  
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
            end
         | 
| @@ -0,0 +1,314 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Glǽmscribe (also written Glaemscribe) is a software dedicated to
         | 
| 4 | 
            +
            # the transcription of texts between writing systems, and more 
         | 
| 5 | 
            +
            # specifically dedicated to the transcription of J.R.R. Tolkien's 
         | 
| 6 | 
            +
            # invented languages to some of his devised writing systems.
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            # Copyright (C) 2015 Benjamin Babut (Talagan).
         | 
| 9 | 
            +
            # 
         | 
| 10 | 
            +
            # This program is free software: you can redistribute it and/or modify
         | 
| 11 | 
            +
            # it under the terms of the GNU Affero General Public License as published by
         | 
| 12 | 
            +
            # the Free Software Foundation, either version 3 of the License, or
         | 
| 13 | 
            +
            # any later version.
         | 
| 14 | 
            +
            # 
         | 
| 15 | 
            +
            # This program is distributed in the hope that it will be useful,
         | 
| 16 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 17 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
         | 
| 18 | 
            +
            # GNU Affero General Public License for more details.
         | 
| 19 | 
            +
            # 
         | 
| 20 | 
            +
            # You should have received a copy of the GNU Affero General Public License
         | 
| 21 | 
            +
            # along with this program.  If not, see <http://www.gnu.org/licenses/>.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            module Glaemscribe
         | 
| 24 | 
            +
              module API
         | 
| 25 | 
            +
                
         | 
| 26 | 
            +
                class ModeParser
         | 
| 27 | 
            +
                
         | 
| 28 | 
            +
                  def initialize
         | 
| 29 | 
            +
                    @mode = nil
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  def validate_presence_of_args(node, arg_count = nil)
         | 
| 33 | 
            +
                    if(arg_count)
         | 
| 34 | 
            +
                      if(node.args.count != arg_count)
         | 
| 35 | 
            +
                        @mode.errors << Glaeml::Error.new(node.line,"Element '#{node.name}' should have #{arg_count} arguments.")
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                  
         | 
| 40 | 
            +
                  def validate_presence_of_children(parent_node, elt_name, elt_count = nil, arg_count = nil)
         | 
| 41 | 
            +
                    res = parent_node.gpath(elt_name)
         | 
| 42 | 
            +
                    if(elt_count)
         | 
| 43 | 
            +
                      if(res.count != elt_count)
         | 
| 44 | 
            +
                        @mode.errors << Glaeml::Error.new(parent_node.line,"Element '#{parent_node.name}' should have exactly #{elt_count} children of type '#{elt_name}'.")
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                    if(arg_count)
         | 
| 48 | 
            +
                      res.each{ |child_node|
         | 
| 49 | 
            +
                        validate_presence_of_args(child_node, arg_count)
         | 
| 50 | 
            +
                      }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                  
         | 
| 54 | 
            +
                  # Very simplified 'dtd' like verification
         | 
| 55 | 
            +
                  def verify_mode_glaeml(doc)
         | 
| 56 | 
            +
                    validate_presence_of_children(doc.root_node, "language", 1, 1)
         | 
| 57 | 
            +
                    validate_presence_of_children(doc.root_node, "writing", 1, 1)
         | 
| 58 | 
            +
                    validate_presence_of_children(doc.root_node, "mode", 1, 1)
         | 
| 59 | 
            +
                    validate_presence_of_children(doc.root_node, "authors", 1, 1)
         | 
| 60 | 
            +
                    validate_presence_of_children(doc.root_node, "version", 1, 1)
         | 
| 61 | 
            +
                   
         | 
| 62 | 
            +
                    doc.root_node.gpath("charset").each{ |charset_element|
         | 
| 63 | 
            +
                      validate_presence_of_args(charset_element, 2)        
         | 
| 64 | 
            +
                    }
         | 
| 65 | 
            +
                   
         | 
| 66 | 
            +
                    doc.root_node.gpath("options.option").each{ |option_element|
         | 
| 67 | 
            +
                      validate_presence_of_args(option_element, 2)
         | 
| 68 | 
            +
                      option_element.gpath("value").each{ |value_element|
         | 
| 69 | 
            +
                        validate_presence_of_args(value_element, 2)
         | 
| 70 | 
            +
                      }
         | 
| 71 | 
            +
                    }
         | 
| 72 | 
            +
                    
         | 
| 73 | 
            +
                    doc.root_node.gpath("processor.outspace").each{ |outspace_element|
         | 
| 74 | 
            +
                      validate_presence_of_args(outspace_element, 1)          
         | 
| 75 | 
            +
                    }
         | 
| 76 | 
            +
                    
         | 
| 77 | 
            +
                    doc.root_node.gpath("processor.rules").each{ |rules_element|
         | 
| 78 | 
            +
                      validate_presence_of_args(rules_element, 1)          
         | 
| 79 | 
            +
                    }        
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
                  
         | 
| 82 | 
            +
                  def create_if_cond_for_if_term(line, if_term, cond)      
         | 
| 83 | 
            +
                    ifcond                          = IfTree::IfCond.new(line, if_term, cond)
         | 
| 84 | 
            +
                    child_code_block                = IfTree::CodeBlock.new(ifcond)
         | 
| 85 | 
            +
                    ifcond.child_code_block         = child_code_block                
         | 
| 86 | 
            +
                    if_term.if_conds << ifcond 
         | 
| 87 | 
            +
                    ifcond            
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                  
         | 
| 90 | 
            +
                  def traverse_if_tree(root_code_block, root_element, text_procedure, element_procedure)
         | 
| 91 | 
            +
                    current_parent_code_block = root_code_block
         | 
| 92 | 
            +
                    
         | 
| 93 | 
            +
                    root_element.children.each{ |child|
         | 
| 94 | 
            +
                      if(child.text?)
         | 
| 95 | 
            +
                        # Do something with this text 
         | 
| 96 | 
            +
                        text_procedure.call(current_parent_code_block, child)           
         | 
| 97 | 
            +
                      elsif(child.element?)
         | 
| 98 | 
            +
                        case child.name
         | 
| 99 | 
            +
                        when 'if'
         | 
| 100 | 
            +
                          cond_attribute                  = child.args.first
         | 
| 101 | 
            +
                          if_term                         = IfTree::IfTerm.new(current_parent_code_block)
         | 
| 102 | 
            +
                          current_parent_code_block.terms << if_term                  
         | 
| 103 | 
            +
                          if_cond                         = create_if_cond_for_if_term(child.line, if_term, cond_attribute)             
         | 
| 104 | 
            +
                          current_parent_code_block       = if_cond.child_code_block
         | 
| 105 | 
            +
                         
         | 
| 106 | 
            +
                        when 'elsif'
         | 
| 107 | 
            +
                         
         | 
| 108 | 
            +
                          cond_attribute                  = child.args.first
         | 
| 109 | 
            +
                          if_term                         = current_parent_code_block.parent_if_cond.parent_if_term  
         | 
| 110 | 
            +
                          
         | 
| 111 | 
            +
                          if !if_term
         | 
| 112 | 
            +
                            @mode.errors << Glaeml::Error.new(child.line, " 'elsif' without a 'if'.")
         | 
| 113 | 
            +
                            return
         | 
| 114 | 
            +
                          end
         | 
| 115 | 
            +
                          
         | 
| 116 | 
            +
                          # TODO : check that precendent one is a elsif, not a else
         | 
| 117 | 
            +
                          if_cond                         = create_if_cond_for_if_term(child.line, if_term, cond_attribute)             
         | 
| 118 | 
            +
                          current_parent_code_block       = if_cond.child_code_block                                       
         | 
| 119 | 
            +
                          
         | 
| 120 | 
            +
                        when 'else'
         | 
| 121 | 
            +
                          
         | 
| 122 | 
            +
                          if_term                         = current_parent_code_block.parent_if_cond.parent_if_term  
         | 
| 123 | 
            +
                          
         | 
| 124 | 
            +
                          if !if_term
         | 
| 125 | 
            +
                            @mode.errors << Glaeml::Error.new(child.line, " 'else' without a 'if'.")
         | 
| 126 | 
            +
                            return
         | 
| 127 | 
            +
                          end
         | 
| 128 | 
            +
                          
         | 
| 129 | 
            +
                          if_cond                         = create_if_cond_for_if_term(child.line, if_term, "true")             
         | 
| 130 | 
            +
                          current_parent_code_block       = if_cond.child_code_block
         | 
| 131 | 
            +
                                                                          
         | 
| 132 | 
            +
                        when 'endif'
         | 
| 133 | 
            +
                          if_term                         = current_parent_code_block.parent_if_cond.parent_if_term  
         | 
| 134 | 
            +
                        
         | 
| 135 | 
            +
                          if !if_term
         | 
| 136 | 
            +
                            @mode.errors << Glaeml::Error.new(child.line, " 'endif' without a 'if'.")
         | 
| 137 | 
            +
                            return
         | 
| 138 | 
            +
                          end
         | 
| 139 | 
            +
                          
         | 
| 140 | 
            +
                          current_parent_code_block       = if_term.parent_code_block
         | 
| 141 | 
            +
                          
         | 
| 142 | 
            +
                        else
         | 
| 143 | 
            +
                          # Do something with this child element
         | 
| 144 | 
            +
                          element_procedure.call(current_parent_code_block, child)            
         | 
| 145 | 
            +
                        end
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
                    }
         | 
| 148 | 
            +
                    
         | 
| 149 | 
            +
                    if(current_parent_code_block.parent_if_cond)
         | 
| 150 | 
            +
                      @mode.errors <<  Glaeml::Error.new(root_element.line, "Unended 'if' at the end of this '#{root_element.name}' element.")
         | 
| 151 | 
            +
                    end
         | 
| 152 | 
            +
                  end
         | 
| 153 | 
            +
                        
         | 
| 154 | 
            +
                  def parse_pre_post_processor(processor_element, pre_not_post)
         | 
| 155 | 
            +
                    # Do nothing with text elements
         | 
| 156 | 
            +
                    text_procedure = Proc.new { |current_parent_code_block, element| }           
         | 
| 157 | 
            +
                    
         | 
| 158 | 
            +
                    element_procedure = Proc.new { |current_parent_code_block, element|
         | 
| 159 | 
            +
                      
         | 
| 160 | 
            +
                      # A block of code lines. Put them in a codelinesterm.   
         | 
| 161 | 
            +
                      term = current_parent_code_block.terms.last
         | 
| 162 | 
            +
                      if(!term || !term.is_pre_post_processor_operators?)
         | 
| 163 | 
            +
                        term = IfTree::PrePostProcessorOperatorsTerm.new(current_parent_code_block) 
         | 
| 164 | 
            +
                        current_parent_code_block.terms << term
         | 
| 165 | 
            +
                      end
         | 
| 166 | 
            +
                      
         | 
| 167 | 
            +
                      operator_name   = element.name      
         | 
| 168 | 
            +
                      operator_class  = if pre_not_post
         | 
| 169 | 
            +
                        ResourceManager::class_for_pre_processor_operator_name(operator_name)
         | 
| 170 | 
            +
                      else
         | 
| 171 | 
            +
                        ResourceManager::class_for_post_processor_operator_name(operator_name)
         | 
| 172 | 
            +
                      end
         | 
| 173 | 
            +
                      
         | 
| 174 | 
            +
                      if !operator_class
         | 
| 175 | 
            +
                        @mode.errors << Glaeml::Error.new(element.line,"Operator #{operator_name} is unknown.")
         | 
| 176 | 
            +
                      else
         | 
| 177 | 
            +
                        arg0 = element.args[0]
         | 
| 178 | 
            +
                        arg1 = element.args[1]
         | 
| 179 | 
            +
                        arg2 = element.args[2]
         | 
| 180 | 
            +
                        arg3 = element.args[3]
         | 
| 181 | 
            +
                        
         | 
| 182 | 
            +
                        term.operators << operator_class.new([arg0,arg1,arg2,arg3])
         | 
| 183 | 
            +
                      end  
         | 
| 184 | 
            +
                    }  
         | 
| 185 | 
            +
                    
         | 
| 186 | 
            +
                    root_code_block = ((pre_not_post)?(@mode.pre_processor.root_code_block):(@mode.post_processor.root_code_block))
         | 
| 187 | 
            +
                           
         | 
| 188 | 
            +
                    self.traverse_if_tree(root_code_block, processor_element, text_procedure, element_procedure )                       
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
                  
         | 
| 191 | 
            +
                  def parse(file_path, mode_options = {})
         | 
| 192 | 
            +
                          
         | 
| 193 | 
            +
                    @mode = Mode.new(ResourceManager::mode_name_from_file_path(file_path))        
         | 
| 194 | 
            +
                          
         | 
| 195 | 
            +
                    raw   = File.open(file_path,"rb:utf-8").read
         | 
| 196 | 
            +
                    doc   = Glaeml::Parser.new.parse(raw)
         | 
| 197 | 
            +
                    
         | 
| 198 | 
            +
                    if(doc.errors.any?)
         | 
| 199 | 
            +
                      @mode.errors = doc.errors
         | 
| 200 | 
            +
                      return @mode
         | 
| 201 | 
            +
                    end
         | 
| 202 | 
            +
                    
         | 
| 203 | 
            +
                    verify_mode_glaeml(doc)
         | 
| 204 | 
            +
                    
         | 
| 205 | 
            +
                    return @mode if(@mode.errors.any?)
         | 
| 206 | 
            +
                    
         | 
| 207 | 
            +
                    # Get the attributes of the mode
         | 
| 208 | 
            +
                    @mode.language    = doc.root_node.gpath('language').first.args.first
         | 
| 209 | 
            +
                    @mode.writing     = doc.root_node.gpath('writing').first.args.first
         | 
| 210 | 
            +
                    @mode.human_name  = doc.root_node.gpath('mode').first.args.first
         | 
| 211 | 
            +
                    @mode.authors     = doc.root_node.gpath('authors').first.args.first
         | 
| 212 | 
            +
                    @mode.version     = doc.root_node.gpath('version').first.args.first
         | 
| 213 | 
            +
                            
         | 
| 214 | 
            +
                    doc.root_node.gpath("options.option").each{ |option_element|
         | 
| 215 | 
            +
                      values  = {}
         | 
| 216 | 
            +
                      option_element.gpath("value").each{ |value_element|
         | 
| 217 | 
            +
                        value_name                = value_element.args.first
         | 
| 218 | 
            +
                        values[value_name]        = value_element.args.last.to_i
         | 
| 219 | 
            +
                      }
         | 
| 220 | 
            +
                    
         | 
| 221 | 
            +
                      option_name_at          = option_element.args[0]
         | 
| 222 | 
            +
                      option_default_val_at   = option_element.args[1]
         | 
| 223 | 
            +
                      # TODO: check syntax of the option name
         | 
| 224 | 
            +
                      
         | 
| 225 | 
            +
                      if(option_default_val_at.nil?)
         | 
| 226 | 
            +
                        @mode.errors << Glaeml::Error.new(option_element.line, "Missing option default value.")
         | 
| 227 | 
            +
                      end
         | 
| 228 | 
            +
                        
         | 
| 229 | 
            +
                      option        = Option.new(option_name_at, option_default_val_at, values)
         | 
| 230 | 
            +
                      @mode.options[option.name] = option
         | 
| 231 | 
            +
                    }
         | 
| 232 | 
            +
                    
         | 
| 233 | 
            +
                    # Read the supported font list
         | 
| 234 | 
            +
                    doc.root_node.gpath("charset").each { |charset_element|
         | 
| 235 | 
            +
                      charset_name                      = charset_element.args.first
         | 
| 236 | 
            +
                      charset                           = ResourceManager::charset(charset_name)
         | 
| 237 | 
            +
                      
         | 
| 238 | 
            +
                      # Load the charset if we don't have it
         | 
| 239 | 
            +
                      if !charset
         | 
| 240 | 
            +
                        ResourceManager::load_charsets([charset_name])
         | 
| 241 | 
            +
                        charset                         = ResourceManager::charset(charset_name)  
         | 
| 242 | 
            +
                      end
         | 
| 243 | 
            +
                      if charset
         | 
| 244 | 
            +
                        if charset.errors.any?
         | 
| 245 | 
            +
                          charset.errors.each{ |e|
         | 
| 246 | 
            +
                            @mode.errors << Glaeml::Error.new(charset_element.line, "#{charset_name}:#{e.line}:#{e.text}")
         | 
| 247 | 
            +
                          }
         | 
| 248 | 
            +
                          return @mode
         | 
| 249 | 
            +
                        end
         | 
| 250 | 
            +
                        
         | 
| 251 | 
            +
                        @mode.supported_charsets[charset_name]    = charset
         | 
| 252 | 
            +
                        @mode.default_charset                     = charset if  charset_element.args[1] &&  charset_element.args[1] == "true"
         | 
| 253 | 
            +
                      else
         | 
| 254 | 
            +
                        @mode.warnings << Glaeml::Error.new(charset_element.line,"Failed to load charset '#{charset_name}'.")
         | 
| 255 | 
            +
                      end
         | 
| 256 | 
            +
                    }
         | 
| 257 | 
            +
                    
         | 
| 258 | 
            +
                    if !@mode.default_charset
         | 
| 259 | 
            +
                      @mode.warnings << Glaeml::Error.new(0,"No default charset defined!!")
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
                     
         | 
| 262 | 
            +
                    # Read the preprocessor
         | 
| 263 | 
            +
                    doc.root_node.gpath("preprocessor").each{ |preprocessor_element|
         | 
| 264 | 
            +
                      self.parse_pre_post_processor(preprocessor_element, true)
         | 
| 265 | 
            +
                    }
         | 
| 266 | 
            +
                    
         | 
| 267 | 
            +
                    # Read the postprocessor
         | 
| 268 | 
            +
                    doc.root_node.gpath("postprocessor").each{ |postprocessor_element|
         | 
| 269 | 
            +
                      self.parse_pre_post_processor(postprocessor_element, false)
         | 
| 270 | 
            +
                    }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                    # Read the processor
         | 
| 273 | 
            +
                    doc.root_node.gpath("processor.outspace").each{ |outspace_element|
         | 
| 274 | 
            +
                      val                         = outspace_element.args[0]
         | 
| 275 | 
            +
                      @mode.processor.out_space   = val.split.reject{|token| token.empty? }       
         | 
| 276 | 
            +
                    }      
         | 
| 277 | 
            +
                    
         | 
| 278 | 
            +
                    doc.root_node.gpath("processor.rules").each{ |rules_element|
         | 
| 279 | 
            +
                    
         | 
| 280 | 
            +
                      rule_group_name                               = rules_element.args.first
         | 
| 281 | 
            +
                      rule_group                                    = RuleGroup.new(@mode,rule_group_name)
         | 
| 282 | 
            +
                      @mode.processor.rule_groups[rule_group_name]  = rule_group
         | 
| 283 | 
            +
                      
         | 
| 284 | 
            +
                      text_procedure = Proc.new { |current_parent_code_block, element|            
         | 
| 285 | 
            +
                        # A block of code lines. Put them in a codelinesterm.   
         | 
| 286 | 
            +
                        term = current_parent_code_block.terms.last
         | 
| 287 | 
            +
                        if(!term || !term.is_code_lines?)
         | 
| 288 | 
            +
                          term = IfTree::CodeLinesTerm.new(current_parent_code_block) 
         | 
| 289 | 
            +
                          current_parent_code_block.terms << term
         | 
| 290 | 
            +
                        end
         | 
| 291 | 
            +
                                               
         | 
| 292 | 
            +
                        lcount  = element.line
         | 
| 293 | 
            +
                        element.args[0].lines.to_a.each{ |l| 
         | 
| 294 | 
            +
                          l       = l.strip
         | 
| 295 | 
            +
                          term.code_lines << IfTree::CodeLine.new(l, lcount)           
         | 
| 296 | 
            +
                          lcount  += 1                         
         | 
| 297 | 
            +
                        }
         | 
| 298 | 
            +
                      }
         | 
| 299 | 
            +
                      
         | 
| 300 | 
            +
                      element_procedure = Proc.new { |current_parent_code_block, element|
         | 
| 301 | 
            +
                        # This is fatal.
         | 
| 302 | 
            +
                        @mode.errors << Glaeml::Error.new(element.line, "Unknown directive #{element.name}.")      
         | 
| 303 | 
            +
                      }  
         | 
| 304 | 
            +
                      
         | 
| 305 | 
            +
                      self.traverse_if_tree( rule_group.root_code_block, rules_element, text_procedure, element_procedure )                 
         | 
| 306 | 
            +
                    }
         | 
| 307 | 
            +
                                                                     
         | 
| 308 | 
            +
                    @mode.finalize(mode_options) if !@mode.errors.any?
         | 
| 309 | 
            +
                    
         | 
| 310 | 
            +
                    @mode
         | 
| 311 | 
            +
                  end
         | 
| 312 | 
            +
                end
         | 
| 313 | 
            +
              end
         | 
| 314 | 
            +
            end
         |