class PosxmlCompiler::Parser

Constants

LIMIT_DEFAULT

Attributes

bytecodes[RW]
limit[RW]
text[RW]
variables[RW]
xsd[RW]

Public Class Methods

new(source, xsd) click to toggle source
# File lib/posxml_compiler/parser.rb, line 12
def initialize(source, xsd)
  @xsd       = xsd
  @text      = source
  @variables = PosxmlCompiler::Variable.new(xsd, PosxmlCompiler::Function.new)
  @bytecodes = parse(self.text)
  @limit     = LIMIT_DEFAULT
end

Public Instance Methods

posxml() click to toggle source
# File lib/posxml_compiler/parser.rb, line 20
def posxml
  jump = PosxmlCompiler::Jump.new(self.bytecodes.dup)
  jump.persist!
  txt = jump.instructions.inject("") { |string, instruction| string << instruction[:line] }
  if txt.size > self.limit
    puts "Application size limit(#{self.limit}) exceed"
  end
  txt
rescue => e
  raise e.class.new("#{number}:#{e.message}")
end

Private Instance Methods

add_line_number(line, string) click to toggle source
# File lib/posxml_compiler/parser.rb, line 79
def add_line_number(line, string)
  # necessary check to avoid add line on commands devide in two lines
  if string[0] == "<"
    "#{line+1}\x00#{string}"
  else
    string
  end
end
instruction_check_close(str) click to toggle source
# File lib/posxml_compiler/parser.rb, line 88
def instruction_check_close(str)
  if str[-1] == ">"
    "#{str[0..-2]}\n"
  else
    str
  end
end
parse(txt) click to toggle source
# File lib/posxml_compiler/parser.rb, line 96
def parse(txt)
  point = 0
  number = 0
  # Split in lines and remove nil's
  split_instructions(sanitize(txt)).inject([]) do |array, str|
    if str.include?("\x00")
      number, value = str.split("\x00", 2)
    else
      value = str
    end
    value = value.strip
    next(array) if value.empty?
    # => "if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}
    name, parameters = parse_line(value)

    # => "if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string}
    parse_references(name, parameters)

    line = parse_instruction(name, parameters)
    size = line.size
    array << {
      :name => name, :parameters => parameters, :line => line, :size => size, :point => point, :line_number => number
    }
    point += size
    array
  end
rescue => e
  raise e.class.new("#{number}:#{e.message}")
end
parse_instruction(name, parameters) click to toggle source

Receive:

("if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string})

Return:

"I$(1)\nequalto\nKEY_CLEAR"
# File lib/posxml_compiler/parser.rb, line 131
def parse_instruction(name, parameters)
  self.xsd.to_bytecode(name, parameters)
end
parse_line(str) click to toggle source

Parse line and return instruction name and parameters from:

<if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"

to:

["if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}]
# File lib/posxml_compiler/parser.rb, line 155
def parse_line(str)
  command = (str.strip[-1] == "/") ? str.strip[0..-2] : str.strip
  index   = command.index(" ")

  if index
    [command[1..(index - 1)], parse_parameters(command[index..-1])]
  else # Common to instruction without parameters
    [command[1..-1], {}]
  end
end
parse_parameters(str) click to toggle source

Parse only parameters from:

variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"

to:

{"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}
# File lib/posxml_compiler/parser.rb, line 172
def parse_parameters(str)
  value = ""
  variable = ""
  separator = nil
  bVariable = true
  bValue = false
  str.strip.chars.inject({}) do |hash, ch|
    if ch == separator
      separator = nil
      bVariable = true
      bValue    = false
      hash[variable.strip] = replace_xml_constants(value)
      variable = ""
      value = ""

    elsif ! separator && (ch == "'" || ch == "\"")
      separator = ch
      bVariable = false
      bValue    = true

    elsif bVariable && ch == "="
      bVariable = false

    elsif bValue
      value << ch

    elsif bVariable
      variable << ch
    end
    hash
  end
end
parse_references(name, parameters) click to toggle source

Receive:

("if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"})
("function", {"name" => "dosomething"})

Return (changing parameter key value):

"if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string})
"function", {"name" => {:original => "dosomething", :value => "1", :type => :string}})
# File lib/posxml_compiler/parser.rb, line 142
def parse_references(name, parameters)
  raise InstructionNotFoundError.new("Instruction #{name.inspect} not found") unless xsd.find(name)
  parameters.each do |param_name, value|
    parameters[param_name] = self.variables.get(name, param_name, value)
  end
end
replace_xml_constants(str) click to toggle source
# File lib/posxml_compiler/parser.rb, line 205
def replace_xml_constants(str)
  str.gsub("&quot;", "\"").gsub("&lt;", "<").gsub("&gt;", ">").gsub("&amp;", "&")
end
sanitize(txt) click to toggle source

Clean text from:

<if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR">
<else />
</if>

to:

<if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"

<else

</if
# File lib/posxml_compiler/parser.rb, line 44
def sanitize(txt)
  string = txt.gsub("\r\n", "\n").gsub("\n\r", "\n").gsub("><", ">\n<")
  characters = ['ç', 'Ç', 'ã', 'Ã', 'â', 'Â', 'á', 'Á', 'à', 'À', 'é', 'É', 'è', 'È', 'ê', 'Ê', 'í', 'Í', 'ì', 'Ì', 'ó', 'Ò', 'ò', 'Ò', 'ô', 'Ô', 'ú', 'Ú', 'ù', 'Ù']
  normalized = 'cCaAaAaAaAeEeEeEiIiIoOoOoOuUuU'
  characters.each_with_index do |char, index|
    str = normalized[index]
    string = string.gsub(char, str) if str
  end
  string
end
split_instructions(txt) click to toggle source

Check if an instruction end in the next line, then split by n

# File lib/posxml_compiler/parser.rb, line 56
def split_instructions(txt)
  txt_new = ""
  ignore = false
  txt.split("\n").compact.each_with_index do |str, number|
    line = str.strip
    if line[0..3] == "<!--" || ignore # Check comentaries
      ignore = true
      ignore = false if line.include?("-->")
    elsif line.include?("<!--") # Check end of comentaries
      line_without_comment = line.split("<!--")[0].strip
      txt_new << add_line_number(number, instruction_check_close(line_without_comment))
    elsif line.include?("<") || line.include?(">")
      txt_new << add_line_number(number, instruction_check_close(line))
    else !ignore # For middle of instruction between lines
      # Avoid this <pinpad.getpindukptmessage="$(sDisplayMsg)"type="3"pan="$(sPAN)"maxlen="12"variablereturnpin="$(sPIN)"variablereturnksn="$(sKSN)"variablereturn="$(iRet)"
      txt_new << " " if txt_new[-1] != " "
      txt_new << add_line_number(number, instruction_check_close(line))
    end
  end
  #p txt_new
  txt_new.split("\n")
end