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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +19 -0
  3. data/bin/glaemscribe +307 -0
  4. data/glaemresources/charsets/cirth_ds.cst +205 -0
  5. data/glaemresources/charsets/sarati_eldamar.cst +256 -0
  6. data/glaemresources/charsets/tengwar_ds.cst +318 -0
  7. data/glaemresources/charsets/unicode_gothic.cst +64 -0
  8. data/glaemresources/charsets/unicode_runes.cst +120 -0
  9. data/glaemresources/modes/adunaic.glaem +251 -0
  10. data/glaemresources/modes/blackspeech-annatar.glaem +318 -0
  11. data/glaemresources/modes/blackspeech.glaem +260 -0
  12. data/glaemresources/modes/gothic.glaem +78 -0
  13. data/glaemresources/modes/khuzdul.glaem +141 -0
  14. data/glaemresources/modes/mercian.glaem +419 -0
  15. data/glaemresources/modes/oldnorse-medieval.glaem +127 -0
  16. data/glaemresources/modes/quenya-sarati.glaem +320 -0
  17. data/glaemresources/modes/quenya.glaem +307 -0
  18. data/glaemresources/modes/sindarin-beleriand.glaem +285 -0
  19. data/glaemresources/modes/sindarin-classical.glaem +276 -0
  20. data/glaemresources/modes/sindarin-daeron.glaem +182 -0
  21. data/glaemresources/modes/telerin.glaem +302 -0
  22. data/glaemresources/modes/valarin-sarati.glaem +210 -0
  23. data/glaemresources/modes/westron.glaem +340 -0
  24. data/glaemresources/modes/westsaxon.glaem +342 -0
  25. data/lib/api/charset.rb +84 -0
  26. data/lib/api/charset_parser.rb +55 -0
  27. data/lib/api/constants.rb +29 -0
  28. data/lib/api/debug.rb +36 -0
  29. data/lib/api/eval.rb +268 -0
  30. data/lib/api/fragment.rb +113 -0
  31. data/lib/api/glaeml.rb +200 -0
  32. data/lib/api/if_tree.rb +96 -0
  33. data/lib/api/mode.rb +112 -0
  34. data/lib/api/mode_parser.rb +314 -0
  35. data/lib/api/option.rb +64 -0
  36. data/lib/api/post_processor/reverse.rb +36 -0
  37. data/lib/api/pre_processor/downcase.rb +35 -0
  38. data/lib/api/pre_processor/elvish_numbers.rb +47 -0
  39. data/lib/api/pre_processor/rxsubstitute.rb +40 -0
  40. data/lib/api/pre_processor/substitute.rb +38 -0
  41. data/lib/api/pre_processor/up_down_tehta_split.rb +138 -0
  42. data/lib/api/resource_manager.rb +130 -0
  43. data/lib/api/rule.rb +99 -0
  44. data/lib/api/rule_group.rb +159 -0
  45. data/lib/api/sheaf.rb +70 -0
  46. data/lib/api/sheaf_chain.rb +86 -0
  47. data/lib/api/sheaf_chain_iterator.rb +108 -0
  48. data/lib/api/sub_rule.rb +40 -0
  49. data/lib/api/transcription_pre_post_processor.rb +118 -0
  50. data/lib/api/transcription_processor.rb +137 -0
  51. data/lib/api/transcription_tree_node.rb +91 -0
  52. data/lib/glaemscribe.rb +70 -0
  53. 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
@@ -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