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