class Glaemscribe::API::ModeParser
Public Class Methods
new()
click to toggle source
# File lib/api/mode_parser.rb, line 28 def initialize @mode = nil end
Public Instance Methods
create_if_cond_for_if_term(line, if_term, cond)
click to toggle source
# File lib/api/mode_parser.rb, line 89 def create_if_cond_for_if_term(line, if_term, cond) ifcond = IfTree::IfCond.new(line, if_term, cond) child_code_block = IfTree::CodeBlock.new(ifcond) ifcond.child_code_block = child_code_block if_term.if_conds << ifcond ifcond end
parse(file_path, mode_options = {})
click to toggle source
# File lib/api/mode_parser.rb, line 260 def parse(file_path, mode_options = {}) @mode = Mode.new(ResourceManager::mode_name_from_file_path(file_path)) raw = File.open(file_path,"rb:utf-8").read doc = Glaeml::Parser.new.parse(raw) if(doc.errors.any?) @mode.errors = doc.errors return @mode end verify_mode_glaeml(doc) return @mode if(@mode.errors.any?) # Get the attributes of the mode @mode.language = doc.root_node.gpath('language').first.args.first @mode.writing = doc.root_node.gpath('writing').first.args.first @mode.human_name = doc.root_node.gpath('mode').first.args.first @mode.authors = doc.root_node.gpath('authors').first.args.first @mode.version = doc.root_node.gpath('version').first.args.first @mode.raw_mode_name = doc.root_node.gpath('raw_mode').first.args.first if doc.root_node.gpath('raw_mode').first @mode.invention = doc.root_node.gpath('invention').first.args.first if doc.root_node.gpath('invention').first @mode.world = doc.root_node.gpath('world').first.args.first if doc.root_node.gpath('world').first doc.root_node.gpath("options.option").each{ |option_element| values = {} visibility = nil is_radio = false option_element.gpath("value").each{ |value_element| value_name = value_element.args.first values[value_name] = value_element.args.last.to_i } option_element.gpath("visible_when").each{ |visible_element| visibility = visible_element.args.first } option_element.gpath('radio').each{|e| is_radio = true} option_name_at = option_element.args[0] option_default_val_at = option_element.args[1] # TODO: check syntax of the option name if(option_default_val_at.nil?) @mode.errors << Glaeml::Error.new(option_element.line, "Missing option default value.") end option = Option.new(@mode, option_name_at, option_default_val_at, values, option_element.line, visibility) option.is_radio = is_radio @mode.options[option.name] = option } # Read the supported font list doc.root_node.gpath("charset").each { |charset_element| charset_name = charset_element.args.first charset = ResourceManager::charset(charset_name) # Load the charset if we don't have it if !charset ResourceManager::load_charsets([charset_name]) charset = ResourceManager::charset(charset_name) end if charset if charset.errors.any? charset.errors.each{ |e| @mode.errors << Glaeml::Error.new(charset_element.line, "#{charset_name}:#{e.line}:#{e.text}") } return @mode end @mode.supported_charsets[charset_name] = charset @mode.default_charset = charset if charset_element.args[1] && charset_element.args[1] == "true" else @mode.warnings << Glaeml::Error.new(charset_element.line,"Failed to load charset '#{charset_name}'.") end } if !@mode.default_charset @mode.warnings << Glaeml::Error.new(0,"No default charset defined!!") end # Read the preprocessor doc.root_node.gpath("preprocessor").each{ |preprocessor_element| self.parse_pre_post_processor(preprocessor_element, true) } # Read the postprocessor doc.root_node.gpath("postprocessor").each{ |postprocessor_element| self.parse_pre_post_processor(postprocessor_element, false) } # Read the processor doc.root_node.gpath("outspace").each{ |outspace_element| val = outspace_element.args[0] @mode.post_processor.out_space = val.split.reject{|token| token.empty? } } doc.root_node.gpath("processor.rules").each{ |rules_element| rule_group_name = rules_element.args.first rule_group = RuleGroup.new(@mode,rule_group_name) @mode.processor.rule_groups[rule_group_name] = rule_group text_procedure = Proc.new { |current_parent_code_block, element| # A block of code lines. Put them in a codelinesterm. term = current_parent_code_block.terms.last if(!term || !term.is_code_lines?) term = IfTree::CodeLinesTerm.new(current_parent_code_block) current_parent_code_block.terms << term end lcount = element.line element.args[0].lines.to_a.each{ |l| # Split into lines of code and count the lines l = l.strip term.code_lines << IfTree::CodeLine.new(l, lcount) lcount += 1 } } element_procedure = Proc.new { |current_parent_code_block, element| # This is fatal. @mode.errors << Glaeml::Error.new(element.line, "Unknown directive #{element.name}.") } processor_context = { owner: rule_group, root_element: rules_element, rule_group: rule_group } traverse_if_tree(processor_context, text_procedure, element_procedure ) } espeak_option = @mode.options['espeak_voice'] if espeak_option # Singleton lazy load the TTS engine # If the mode relies on espeak TTS::load_engine @mode.has_tts = true # Check if all voices are supported espeak_option.values.keys.each { |vname| voice = TTS::option_name_to_voice(vname) if !(TTS::voice_list.include? voice) @mode.errors << Glaeml::Error.new(espeak_option.line, "Option has unhandled voice #{voice}.") end } end @mode.finalize(mode_options) if !@mode.errors.any? @mode end
parse_pre_post_processor(processor_element, pre_not_post)
click to toggle source
# File lib/api/mode_parser.rb, line 225 def parse_pre_post_processor(processor_element, pre_not_post) # Do nothing with text elements text_procedure = Proc.new { |current_parent_code_block, element| } element_procedure = Proc.new { |current_parent_code_block, element| # A block of code lines. Put them in a codelinesterm. term = current_parent_code_block.terms.last if(!term || !term.is_pre_post_processor_operators?) term = IfTree::PrePostProcessorOperatorsTerm.new(current_parent_code_block) current_parent_code_block.terms << term end operator_name = element.name operator_class = if pre_not_post ResourceManager::class_for_pre_processor_operator_name(operator_name) else ResourceManager::class_for_post_processor_operator_name(operator_name) end if !operator_class @mode.errors << Glaeml::Error.new(element.line,"Operator #{operator_name} is unknown.") else term.operators << operator_class.new(element.clone) end } processor_context = { owner: ((pre_not_post)?(@mode.pre_processor):(@mode.post_processor)), root_element: processor_element, rule_group: nil } traverse_if_tree(processor_context, text_procedure, element_procedure ) end
traverse_if_tree(context, text_procedure, element_procedure)
click to toggle source
# File lib/api/mode_parser.rb, line 97 def traverse_if_tree(context, text_procedure, element_procedure) owner = context[:owner] # The root object of the if tree root_element = context[:root_element] # The glaeml root_element of that if tree rule_group = context[:rule_group] # The rule group in which this traversal happens (may be null for pre/post processors) root_code_block = owner.root_code_block current_parent_code_block = root_code_block root_element.children.each{ |child| if(child.text?) # Do something with this text text_procedure.call(current_parent_code_block, child) elsif(child.element?) case child.name when 'if' cond_attribute = child.args.first if_term = IfTree::IfTerm.new(current_parent_code_block) current_parent_code_block.terms << if_term if_cond = create_if_cond_for_if_term(child.line, if_term, cond_attribute) current_parent_code_block = if_cond.child_code_block when 'elsif' cond_attribute = child.args.first if_term = current_parent_code_block.parent_if_cond.parent_if_term if !if_term @mode.errors << Glaeml::Error.new(child.line, " 'elsif' without a 'if'.") return end # TODO : check that precendent one is a elsif, not a else if_cond = create_if_cond_for_if_term(child.line, if_term, cond_attribute) current_parent_code_block = if_cond.child_code_block when 'else' if_term = current_parent_code_block.parent_if_cond.parent_if_term if !if_term @mode.errors << Glaeml::Error.new(child.line, " 'else' without a 'if'.") return end if_cond = create_if_cond_for_if_term(child.line, if_term, "true") current_parent_code_block = if_cond.child_code_block when 'endif' if_term = current_parent_code_block.parent_if_cond.parent_if_term if !if_term @mode.errors << Glaeml::Error.new(child.line, " 'endif' without a 'if'.") return end current_parent_code_block = if_term.parent_code_block when 'macro' # Macro definition, cannot be defined in conditional blocks if current_parent_code_block.parent_if_cond || root_element.name != "rules" @mode.errors << Glaeml::Error.new(child.line, "Macros can only defined in the 'rules' scope, not in a conditional block (because they are replaced and used at parsing time) or a macro block (local macros are not handled).") return end if !child.args || child.args.count == 0 @mode.errors << Glaeml::Error.new(child.line, "Macro misses a name.") return end macro_args = child.args.clone macro_name = macro_args.shift macro_args.each{ |arg| if(!arg =~ /[0-9A-Z_]+/) @mode.errors << Glaeml::Error.new(child.line, "Macro argument name #{arg} has wrong format.") return end } if rule_group.macros[macro_name] @mode.errors << Glaeml::Error.new(child.line, "Redefining macro #{macro_name}.") return end macro = Macro.new(rule_group,macro_name,macro_args) macro_context = {:owner => macro, :root_element => child, :rule_group => rule_group} traverse_if_tree(macro_context, text_procedure, element_procedure) rule_group.macros[macro_name] = macro when 'deploy' if !rule_group @mode.errors << Glaeml::Error.new(child.line, "Macros can only be deployed in a rule group.") return end macro_args = child.args.clone macro_name = macro_args.shift macro = rule_group.macros[macro_name] if !macro @mode.errors << Glaeml::Error.new(child.line, "Macro '#{macro_name}' not found in rule group '#{rule_group.name}'.") return end wanted_argcount = macro.arg_names.count given_argcount = macro_args.count if wanted_argcount != given_argcount @mode.errors << Glaeml::Error.new(child.line, "Macro '#{macro_name}' takes #{wanted_argcount} arguments, not #{given_argcount}.") return end macro_node = IfTree::MacroDeployTerm.new(macro, child.line, current_parent_code_block, macro_args) current_parent_code_block.terms << macro_node else # Do something with this child element element_procedure.call(current_parent_code_block, child) end end } if(current_parent_code_block.parent_if_cond) @mode.errors << Glaeml::Error.new(root_element.line, "Unended 'if' at the end of this '#{root_element.name}' element.") end end
validate_presence_of_args(node, arg_count = nil)
click to toggle source
# File lib/api/mode_parser.rb, line 32 def validate_presence_of_args(node, arg_count = nil) if(arg_count) if(node.args.count != arg_count) @mode.errors << Glaeml::Error.new(node.line,"Element '#{node.name}' should have #{arg_count} arguments.") end end end
validate_presence_of_children(parent_node, elt_name, elt_count = nil, arg_count = nil)
click to toggle source
# File lib/api/mode_parser.rb, line 40 def validate_presence_of_children(parent_node, elt_name, elt_count = nil, arg_count = nil) res = parent_node.gpath(elt_name) if(elt_count) if(res.count != elt_count) @mode.errors << Glaeml::Error.new(parent_node.line,"Element '#{parent_node.name}' should have exactly #{elt_count} children of type '#{elt_name}'.") end end if(arg_count) res.each{ |child_node| validate_presence_of_args(child_node, arg_count) } end end
verify_mode_glaeml(doc)
click to toggle source
Very simplified 'dtd' like verification
# File lib/api/mode_parser.rb, line 55 def verify_mode_glaeml(doc) validate_presence_of_children(doc.root_node, "language", 1, 1) validate_presence_of_children(doc.root_node, "writing", 1, 1) validate_presence_of_children(doc.root_node, "mode", 1, 1) validate_presence_of_children(doc.root_node, "authors", 1, 1) validate_presence_of_children(doc.root_node, "version", 1, 1) doc.root_node.gpath("charset").each{ |charset_element| validate_presence_of_args(charset_element, 2) } doc.root_node.gpath("options.option").each{ |option_element| validate_presence_of_args(option_element, 2) option_element.gpath("value").each{ |value_element| validate_presence_of_args(value_element, 2) } } doc.root_node.gpath("postprocessor.outspace").each{ |outspace_element| validate_presence_of_args(outspace_element, 1) } doc.root_node.gpath("processor.rules").each{ |rules_element| validate_presence_of_args(rules_element, 1) validate_presence_of_children(rules_element,"if",nil,1); validate_presence_of_children(rules_element,"elsif",nil,1); } doc.root_node.gpath("preprocessor.if").each{ |e| validate_presence_of_args(e, 1) } doc.root_node.gpath("preprocessor.elsif").each{ |e| validate_presence_of_args(e, 1) } doc.root_node.gpath("postprocessor.if").each{ |e| validate_presence_of_args(e, 1) } doc.root_node.gpath("postprocessor.elsif").each{ |e| validate_presence_of_args(e, 1) } end