glaemscribe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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