class Glaemscribe::API::RuleGroup
Constants
- CROSS_RULE_REGEXP
- CROSS_SCHEMA_REGEXP
- POINTER_VAR_DECL_REGEXP
- RULE_REGEXP
- UNICODE_VAR_NAME_REGEXP_IN
- UNICODE_VAR_NAME_REGEXP_OUT
- VAR_DECL_REGEXP
- VAR_NAME_REGEXP
Attributes
in_charset[R]
macros[R]
mode[R]
name[R]
root_code_block[R]
rules[R]
Public Class Methods
new(mode,name)
click to toggle source
# File lib/api/rule_group.rb, line 56 def initialize(mode,name) @name = name @mode = mode @macros = {} @root_code_block = IfTree::CodeBlock.new end
Public Instance Methods
add_var(var_name, value, is_pointer)
click to toggle source
# File lib/api/rule_group.rb, line 63 def add_var(var_name, value, is_pointer) @vars[var_name] = RuleGroupVar.new(var_name, value, is_pointer) end
apply_vars(line, string, allow_unicode_vars=false)
click to toggle source
Replace all vars in expression
# File lib/api/rule_group.rb, line 68 def apply_vars(line, string, allow_unicode_vars=false) ret = string stack_depth = 0 had_replacements = true while had_replacements had_replacements = false ret = ret.gsub(VAR_NAME_REGEXP) { |cap_var| vname = $1 v = @vars[vname] if !v if vname =~ UNICODE_VAR_NAME_REGEXP_IN # A unicode variable. if allow_unicode_vars # Just keep this variable intact, it will be replaced at the last moment of the parsing rep = cap_var else @mode.errors << Glaeml::Error.new(line, "In expression: #{string}: making wrong use of unicode variable: #{cap_var}. Unicode vars can only be used in source members of a rule or in the definition of another variable.") return nil end else @mode.errors << Glaeml::Error.new(line, "In expression: #{string}: failed to evaluate variable: #{cap_var}.") return nil end else rep = v.value # Only count replacements on non unicode vars had_replacements = true end rep } stack_depth += 1 break if !had_replacements if stack_depth > 16 @mode.errors << Glaeml::Error.new(line, "In expression: #{string}: evaluation stack overflow.") return nil end end ret end
descend_if_tree(code_block, trans_options)
click to toggle source
# File lib/api/rule_group.rb, line 114 def descend_if_tree(code_block, trans_options) code_block.terms.each{ |term| if(term.is_code_lines?) term.code_lines.each{ |cl| finalize_code_line(cl) } elsif(term.is_macro_deploy?) # Ok this is a bit dirty but I don't want to rewrite the error managamenet # So add an error and if it's still the last (meaning there were no error) one remove it possible_error = Glaeml::Error.new(term.line, ">> Macro backtrace : #{term.macro.name}") @mode.errors << possible_error # First, test if variable is pushable arg_values = [] term.macro.arg_names.each_with_index { |arg_name, i| var_value = nil if @vars[arg_name] @mode.errors << Glaeml::Error.new(term.line, "Local variable #{arg_name} hinders a variable with the same name in this context. Use only local variable names in macros!") else # Evaluate local var var_value_ex = term.arg_value_expressions[i] var_value = apply_vars(term.line, var_value_ex, true) if !var_value @mode.errors << Glaeml::Error.new(term.line, "Thus, variable {#{var_name}} could not be declared.") end end arg_values << {name: arg_name, val: var_value} } # We push local vars after the whole loop to avoid interferences between them when evaluating them arg_values.each { |v| if v[:val] add_var(v[:name],v[:val],false) end } descend_if_tree(term.macro.root_code_block, trans_options) # Remove the local vars from the scope (only if they were leggit) arg_values.each { |v| if v[:val] @vars[v[:name]] = nil end } if mode.errors.last == possible_error # Remove the error scope if there were no errors mode.errors.pop else # Add another one to close the context @mode.errors << Glaeml::Error.new(term.line, "<< Macro backtrace : #{term.macro.name}") end else term.if_conds.each{ |if_cond| if_eval = Eval::Parser.new() begin if(if_eval.parse(if_cond.expression,trans_options) == true) descend_if_tree(if_cond.child_code_block, trans_options) break end rescue Eval::IfEvalError => e @mode.errors << Glaeml::Error.new(if_cond.line, "Failed to evaluate condition '#{if_cond.expression}' (#{e})") end } end } end
finalize(trans_options)
click to toggle source
# File lib/api/rule_group.rb, line 268 def finalize(trans_options) @vars = {} @in_charset = {} @rules = [] add_var("NULL","",false) # Characters that are not easily entered or visible in a text editor add_var("NBSP", "{UNI_A0}", false) add_var("WJ", "{UNI_2060}", false) add_var("ZWSP", "{UNI_200B}", false) add_var("ZWNJ", "{UNI_200C}", false) # The following characters are used by the mode syntax. # Redefine some convenient tools. add_var("UNDERSCORE", "{UNI_5F}", false) add_var("ASTERISK", "{UNI_2A}", false) add_var("COMMA", "{UNI_2C}", false) add_var("LPAREN", "{UNI_28}", false) add_var("RPAREN", "{UNI_29}", false) add_var("LBRACKET", "{UNI_5B}", false) add_var("RBRACKET", "{UNI_5D}", false) descend_if_tree(@root_code_block, trans_options) # Now that we have selected our rules, create the in_charset of the rule_group rules.each{ |r| r.sub_rules.each { |sr| sr.src_combination.join("").split(//).each{ |inchar| # Add the character to the map of input characters # Ignore '\u0000' (bounds of word) and '|' (word breaker) @in_charset[inchar] = self if inchar != WORD_BREAKER && inchar != WORD_BOUNDARY_TREE } } } end
finalize_code_line(code_line)
click to toggle source
# File lib/api/rule_group.rb, line 207 def finalize_code_line(code_line) begin if code_line.expression =~ VAR_DECL_REGEXP var_name = $1 var_value_ex = $2 var_value = apply_vars(code_line.line, var_value_ex, true) if !var_value @mode.errors << Glaeml::Error.new(code_line.line, "Thus, variable {#{var_name}} could not be declared.") return end add_var(var_name, var_value, false) elsif code_line.expression =~ POINTER_VAR_DECL_REGEXP var_name = $1 var_value_ex = $2 add_var(var_name, var_value_ex, true) elsif code_line.expression =~ CROSS_RULE_REGEXP match = $1 cross = $2 var_name = $4 replacement = $5 if var_name # This was a variable declaration var_value = apply_vars(code_line.line, cross, false) if !var_value @mode.errors << Glaeml::Error.new(code_line.line, "Thus, variable {#{var_name}} could not be declared.") return end cross = var_value end if cross == "identity" cross = nil end finalize_rule(code_line.line, match, replacement, cross) elsif code_line.expression =~ RULE_REGEXP match = $1 replacement = $2 finalize_rule(code_line.line, match, replacement) elsif code_line.expression.empty? # puts "Empty" else @mode.errors << Glaeml::Error.new(code_line.line,"Cannot understand: #{code_line.expression}") end end end
finalize_rule(line, match_exp, replacement_exp, cross_schema = nil)
click to toggle source
# File lib/api/rule_group.rb, line 191 def finalize_rule(line, match_exp, replacement_exp, cross_schema = nil) match = apply_vars(line, match_exp, true) replacement = apply_vars(line, replacement_exp, false) return if !match || !replacement # Failed rule = Rule.new(line, self) rule.src_sheaf_chain = SheafChain.new(rule,match,true) rule.dst_sheaf_chain = SheafChain.new(rule,replacement,false) rule.finalize(cross_schema) self.rules << rule end