posxml_parser 1.3.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/RELEASE_NOTES.md +8 -0
- data/lib/posxml_compiler.rb +18 -0
- data/lib/posxml_compiler/en_xsd.rb +4306 -0
- data/lib/posxml_compiler/function.rb +23 -0
- data/lib/posxml_compiler/instruction_not_found_error.rb +5 -0
- data/lib/posxml_compiler/jump.rb +97 -0
- data/lib/posxml_compiler/jump_point.rb +54 -0
- data/lib/posxml_compiler/parser.rb +182 -0
- data/lib/posxml_compiler/posxml_compiler_error.rb +4 -0
- data/lib/posxml_compiler/string_size_error.rb +5 -0
- data/lib/posxml_compiler/variable.rb +235 -0
- data/lib/posxml_compiler/variable_limit_error.rb +5 -0
- data/lib/posxml_compiler/variable_name_error.rb +5 -0
- data/lib/posxml_compiler/variable_string_limit_error.rb +5 -0
- data/lib/posxml_compiler/variable_type_error.rb +5 -0
- data/lib/posxml_compiler/variable_undefined_error.rb +5 -0
- data/lib/posxml_compiler/xsd_parser.rb +161 -0
- data/lib/posxml_parser/version.rb +1 -1
- data/posxml_parser.gemspec +2 -0
- metadata +18 -2
@@ -0,0 +1,23 @@
|
|
1
|
+
module PosxmlCompiler
|
2
|
+
class Function
|
3
|
+
attr_reader :number, :functions
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@functions = {}
|
7
|
+
@number = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(name)
|
11
|
+
return if name.to_s.empty?
|
12
|
+
if @functions[name]
|
13
|
+
@functions[name]
|
14
|
+
else
|
15
|
+
@functions[name] = next_number
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def next_number
|
20
|
+
(@number += 1).to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module PosxmlCompiler
|
2
|
+
class Jump
|
3
|
+
INSTRUCTION_FUNCTION = "function"
|
4
|
+
INSTRUCTION_CALLFUNCTION = "callfunction"
|
5
|
+
INSTRUCTION_FUNCTION_END = "/function"
|
6
|
+
|
7
|
+
INSTRUCTION_WHILE = "while"
|
8
|
+
INSTRUCTION_BREAK = "break"
|
9
|
+
INSTRUCTION_WHILE_END = "/while"
|
10
|
+
|
11
|
+
INSTRUCTION_IF = "if"
|
12
|
+
INSTRUCTION_ELSE = "else"
|
13
|
+
INSTRUCTION_IF_END = "/if"
|
14
|
+
|
15
|
+
attr_reader :instructions, :jumps
|
16
|
+
|
17
|
+
def initialize(instructions_array)
|
18
|
+
@instructions = instructions_array
|
19
|
+
@jumps = []
|
20
|
+
self.parse
|
21
|
+
end
|
22
|
+
|
23
|
+
def persist!
|
24
|
+
self.jumps.each do |jump|
|
25
|
+
line = self.instructions[jump.persistence][:line]
|
26
|
+
self.instructions[jump.persistence][:line] = line.insert(1, "#{jump.jump_value}\n")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse
|
31
|
+
instructions_indexed = self.instructions.each_with_index.to_a.reverse
|
32
|
+
stack_function = []
|
33
|
+
stack_while = []
|
34
|
+
stack_if = []
|
35
|
+
|
36
|
+
# It's necessary parse the list of functions first because a functions can
|
37
|
+
# be called from anywhere, already declared or not
|
38
|
+
tree_function = instructions_indexed.select do |instruction, index|
|
39
|
+
instruction[:name] == INSTRUCTION_FUNCTION
|
40
|
+
end.group_by do |instruction, index|
|
41
|
+
instruction[:parameters]["name"][:original]
|
42
|
+
end
|
43
|
+
|
44
|
+
# instruction[:point] + 1 - Because of "\n"
|
45
|
+
instructions_indexed.each do |instruction, index|
|
46
|
+
case instruction[:name]
|
47
|
+
when INSTRUCTION_FUNCTION_END
|
48
|
+
stack_function << [index, instruction[:point]]
|
49
|
+
when INSTRUCTION_FUNCTION
|
50
|
+
reference, point = stack_function.pop
|
51
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_FUNCTION, index, reference, point)
|
52
|
+
when INSTRUCTION_CALLFUNCTION
|
53
|
+
reference, point = check_function_tree(tree_function, instruction)
|
54
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_CALLFUNCTION, index, reference, point)
|
55
|
+
when INSTRUCTION_WHILE_END
|
56
|
+
stack_while << [index, instruction[:point]]
|
57
|
+
when INSTRUCTION_BREAK
|
58
|
+
reference, point = stack_while.last
|
59
|
+
raise PosxmlCompilerError.new("While not found for <break") unless reference
|
60
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_BREAK, index, reference, point)
|
61
|
+
when INSTRUCTION_WHILE
|
62
|
+
reference, point = stack_while.pop
|
63
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_WHILE, index, reference, point)
|
64
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_WHILE_END, reference, index, instruction[:point] - 1)
|
65
|
+
when INSTRUCTION_IF_END
|
66
|
+
stack_if << [index, instruction[:point]]
|
67
|
+
when INSTRUCTION_ELSE
|
68
|
+
reference, point = stack_if.pop
|
69
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_ELSE, index, reference, point)
|
70
|
+
stack_if << [index, instruction[:point], true]
|
71
|
+
when INSTRUCTION_IF
|
72
|
+
reference, point, is_else = stack_if.pop
|
73
|
+
@jumps << JumpPoint.new(self, INSTRUCTION_IF, index, reference, point)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
# TODO add treatment for open/end statments
|
77
|
+
check_addition
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def check_addition
|
82
|
+
self.jumps.each do |jump|
|
83
|
+
jump.check_itself!
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def check_function_tree(tree, instruction)
|
88
|
+
function_name = instruction[:parameters]["name"][:original]
|
89
|
+
unless function = tree[function_name]
|
90
|
+
raise PosxmlCompilerError.new("Function #{function_name} not found") unless reference
|
91
|
+
end
|
92
|
+
function_indexed = tree[function_name][0]
|
93
|
+
[function_indexed[1], function_indexed[0][:point]]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module PosxmlCompiler
|
2
|
+
class JumpPoint
|
3
|
+
INSTRUCTION_WHILE = "while"
|
4
|
+
|
5
|
+
attr_reader :persistence, :reference, :number, :type, :jump, :changed
|
6
|
+
|
7
|
+
def initialize(jump, type, persistence, reference, number)
|
8
|
+
@jump = jump
|
9
|
+
@type = type
|
10
|
+
@persistence = persistence
|
11
|
+
@reference = reference
|
12
|
+
@number = number
|
13
|
+
end
|
14
|
+
|
15
|
+
def changed?
|
16
|
+
@changed
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_itself!
|
20
|
+
chars = (@number.to_s.size + 1) # \n separator between parameters
|
21
|
+
# chars sizes already changed and propogate, so 1 char must not included
|
22
|
+
chars -= 1 if changed?
|
23
|
+
self.check_others(chars)
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(index, value)
|
27
|
+
return if index.nil? || value.nil?
|
28
|
+
if self.reference > index
|
29
|
+
old_number = @number
|
30
|
+
@number += value
|
31
|
+
check_chars(old_number, @number)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_others(value)
|
36
|
+
self.jump.jumps.each do |jump_point|
|
37
|
+
jump_point.add(self.persistence, value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_chars(old, new)
|
42
|
+
addition = new.to_s.size - old.to_s.size
|
43
|
+
if addition > 0
|
44
|
+
@changed = true
|
45
|
+
check_others(addition)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def jump_value
|
50
|
+
self.number.to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# Treat for variables - ok
|
2
|
+
# Treat for functions - ok
|
3
|
+
# Treat number of variables - ok
|
4
|
+
# Treat string size - ok
|
5
|
+
# Check if variable is created before execute
|
6
|
+
module PosxmlCompiler
|
7
|
+
class Parser
|
8
|
+
LIMIT_DEFAULT = 32000
|
9
|
+
|
10
|
+
attr_accessor :text, :xsd, :bytecodes, :variables, :limit
|
11
|
+
|
12
|
+
def initialize(source, xsd)
|
13
|
+
@xsd = xsd
|
14
|
+
@text = source
|
15
|
+
@variables = PosxmlCompiler::Variable.new(xsd, PosxmlCompiler::Function.new)
|
16
|
+
@bytecodes = parse(self.text)
|
17
|
+
@limit = LIMIT_DEFAULT
|
18
|
+
end
|
19
|
+
|
20
|
+
def posxml
|
21
|
+
jump = PosxmlCompiler::Jump.new(self.bytecodes.dup)
|
22
|
+
jump.persist!
|
23
|
+
txt = jump.instructions.inject("") { |string, instruction| string << instruction[:line] }
|
24
|
+
if txt.size > self.limit
|
25
|
+
puts "Application size limit(#{self.limit}) exceed"
|
26
|
+
end
|
27
|
+
txt
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Clean text
|
32
|
+
# from:
|
33
|
+
# <if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR">
|
34
|
+
# <else />
|
35
|
+
# </if>
|
36
|
+
# to:
|
37
|
+
# <if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"
|
38
|
+
#
|
39
|
+
# <else
|
40
|
+
#
|
41
|
+
# </if
|
42
|
+
def sanitize(txt)
|
43
|
+
txt.gsub("\r\n", "\n").gsub("\n\r", "\n").gsub("><", ">\n<")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if an instruction end in the next line, then split by \n
|
47
|
+
def split_instructions(txt)
|
48
|
+
txt_new = ""
|
49
|
+
ignore = false
|
50
|
+
txt.split("\n").compact.each do |str|
|
51
|
+
line = str.strip
|
52
|
+
if line[0..3] == "<!--" || ignore # Check comentaries
|
53
|
+
ignore = true
|
54
|
+
ignore = false if line.include?("-->")
|
55
|
+
elsif line.include?("<!--") # Check end of comentaries
|
56
|
+
line_without_comment = line.split("<!--")[0].strip
|
57
|
+
txt_new << instruction_check_close(line_without_comment)
|
58
|
+
elsif line.include?("<") || line.include?(">")
|
59
|
+
txt_new << instruction_check_close(line)
|
60
|
+
else !ignore # For middle of instruction between lines
|
61
|
+
# Avoid this <pinpad.getpindukptmessage="$(sDisplayMsg)"type="3"pan="$(sPAN)"maxlen="12"variablereturnpin="$(sPIN)"variablereturnksn="$(sKSN)"variablereturn="$(iRet)"
|
62
|
+
txt_new << " " if txt_new[-1] != " "
|
63
|
+
txt_new << instruction_check_close(line)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
txt_new.split("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
def instruction_check_close(str)
|
70
|
+
if str[-1] == ">"
|
71
|
+
"#{str[0..-2]}\n"
|
72
|
+
else
|
73
|
+
str
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse(txt)
|
78
|
+
point = 0
|
79
|
+
# Split in lines and remove nil's
|
80
|
+
split_instructions(sanitize(txt)).inject([]) do |array, str|
|
81
|
+
value = str.strip
|
82
|
+
next(array) if value.empty?
|
83
|
+
# => "if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}
|
84
|
+
name, parameters = parse_line(value)
|
85
|
+
|
86
|
+
# => "if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string}
|
87
|
+
parse_references(name, parameters)
|
88
|
+
|
89
|
+
line = parse_instruction(name, parameters)
|
90
|
+
size = line.size
|
91
|
+
array << {
|
92
|
+
:name => name, :parameters => parameters, :line => line, :size => size, :point => point
|
93
|
+
}
|
94
|
+
point += size
|
95
|
+
array
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Receive:
|
100
|
+
# ("if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string})
|
101
|
+
# Return:
|
102
|
+
# "I$(1)\nequalto\nKEY_CLEAR"
|
103
|
+
#
|
104
|
+
def parse_instruction(name, parameters)
|
105
|
+
self.xsd.to_bytecode(name, parameters)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Receive:
|
109
|
+
# ("if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"})
|
110
|
+
# ("function", {"name" => "dosomething"})
|
111
|
+
# Return (changing parameter key value):
|
112
|
+
# "if", {"variable" => {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}, "operator" => {:original => "equalto", :value => "equalto", :type => :string}, "value" => {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string})
|
113
|
+
# "function", {"name" => {:original => "dosomething", :value => "1", :type => :string}})
|
114
|
+
#
|
115
|
+
def parse_references(name, parameters)
|
116
|
+
parameters.each do |param_name, value|
|
117
|
+
parameters[param_name] = self.variables.get(name, param_name, value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Parse line and return instruction name and parameters
|
122
|
+
# from:
|
123
|
+
# <if variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"
|
124
|
+
# to:
|
125
|
+
# ["if", {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}]
|
126
|
+
#
|
127
|
+
def parse_line(str)
|
128
|
+
command = (str.strip[-1] == "/") ? str.strip[0..-2] : str.strip
|
129
|
+
index = command.index(" ")
|
130
|
+
|
131
|
+
if index
|
132
|
+
[command[1..(index - 1)], parse_parameters(command[index..-1])]
|
133
|
+
else # Common to instruction without parameters
|
134
|
+
[command[1..-1], {}]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Parse only parameters
|
139
|
+
# from:
|
140
|
+
# variable="$(sKeyTouchScreen)" operator="equalto" value="KEY_CLEAR"
|
141
|
+
# to:
|
142
|
+
# {"variable" => "$(sKeyTouchScreen)", "operator" => "equalto", "value" => "KEY_CLEAR"}
|
143
|
+
#
|
144
|
+
def parse_parameters(str)
|
145
|
+
value = ""
|
146
|
+
variable = ""
|
147
|
+
separator = nil
|
148
|
+
bVariable = true
|
149
|
+
bValue = false
|
150
|
+
str.strip.chars.inject({}) do |hash, ch|
|
151
|
+
if ch == separator
|
152
|
+
separator = nil
|
153
|
+
bVariable = true
|
154
|
+
bValue = false
|
155
|
+
hash[variable.strip] = replace_xml_constants(value)
|
156
|
+
variable = ""
|
157
|
+
value = ""
|
158
|
+
|
159
|
+
elsif ! separator && (ch == "'" || ch == "\"")
|
160
|
+
separator = ch
|
161
|
+
bVariable = false
|
162
|
+
bValue = true
|
163
|
+
|
164
|
+
elsif bVariable && ch == "="
|
165
|
+
bVariable = false
|
166
|
+
|
167
|
+
elsif bValue
|
168
|
+
value << ch
|
169
|
+
|
170
|
+
elsif bVariable
|
171
|
+
variable << ch
|
172
|
+
end
|
173
|
+
hash
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def replace_xml_constants(str)
|
178
|
+
str.gsub(""", "\"").gsub("<", "<").gsub(">", ">").gsub("&", "&")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
@@ -0,0 +1,235 @@
|
|
1
|
+
module PosxmlCompiler
|
2
|
+
class Variable
|
3
|
+
VARIABLE_REFERENCE = "$("
|
4
|
+
VARIABLE_STRING_MAX_SIZE = 2048
|
5
|
+
VARIABLE_STRING_LIMIT = 256
|
6
|
+
VARIABLE_LIMIT = 512
|
7
|
+
|
8
|
+
FUNCTION_NAME = "function"
|
9
|
+
FUNCTION_PARAMETER = "name"
|
10
|
+
CALLFUNCTION_NAME = "callfunction"
|
11
|
+
VARIABLE_STRING = "stringvariable"
|
12
|
+
VARIABLE_INTEGER = "integervariable"
|
13
|
+
VARIABLE_PARAMETER_VALUE = "value"
|
14
|
+
VARIABLE_PARAMETER_NAME = "variable"
|
15
|
+
|
16
|
+
attr_accessor :number_strings, :variables, :number, :xsd, :functions
|
17
|
+
|
18
|
+
def initialize(xsd, functions)
|
19
|
+
@functions = functions
|
20
|
+
@number = 0
|
21
|
+
@number_strings = 0
|
22
|
+
@variables = {}
|
23
|
+
@xsd = xsd
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_typing_mismatch(instruction, parameter_name, variable_struct)
|
27
|
+
type = self.xsd.parameter_type(instruction, parameter_name)
|
28
|
+
|
29
|
+
return true if type == :string_or_integer
|
30
|
+
|
31
|
+
case variable_struct[:type]
|
32
|
+
when type # match everything is ok
|
33
|
+
when :string_or_integer # match everything is ok
|
34
|
+
when nil # not defined yet, it's ok for now
|
35
|
+
when :string
|
36
|
+
raise PosxmlCompiler::VariableTypeError.new("Variable type #{variable_struct[:type]} mismatch, instruction #{instruction}, parameter #{parameter_name}, xsd type #{type}.")
|
37
|
+
when :integer
|
38
|
+
raise PosxmlCompiler::VariableTypeError.new("Variable type #{variable_struct[:type]} mismatch, instruction #{instruction}, parameter #{parameter_name}, xsd type #{type}.")
|
39
|
+
else
|
40
|
+
raise PosxmlCompiler::VariableTypeError.new("Variable type #{variable_struct[:type]} not recognized, instruction #{instruction}, parameter #{parameter_name}, xsd type #{type}.")
|
41
|
+
end
|
42
|
+
|
43
|
+
return true
|
44
|
+
end
|
45
|
+
|
46
|
+
# reference true is a variable that is been referenced without been previously created
|
47
|
+
def create(instruction, parameter, value, reference = false)
|
48
|
+
validate_size(parameter, value)
|
49
|
+
case instruction
|
50
|
+
when FUNCTION_NAME
|
51
|
+
create_function_variable(value, instruction)
|
52
|
+
when CALLFUNCTION_NAME
|
53
|
+
create_function_variable(value, instruction)
|
54
|
+
when VARIABLE_STRING
|
55
|
+
create_type_variable(parameter, value, :string, reference)
|
56
|
+
when VARIABLE_INTEGER
|
57
|
+
create_type_variable(parameter, value, :integer, reference)
|
58
|
+
else
|
59
|
+
create_ordinary_variable(instruction, parameter, value, reference)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def find(instruction, parameter, name)
|
64
|
+
if instruction == VARIABLE_STRING || instruction == VARIABLE_INTEGER
|
65
|
+
if parameter == VARIABLE_PARAMETER_NAME
|
66
|
+
remove_reference_struct(self.variables[name])
|
67
|
+
else # VARIABLE_PARAMETER_VALUE can return a reference for other variable
|
68
|
+
self.variables[name]
|
69
|
+
end
|
70
|
+
else
|
71
|
+
self.variables[name]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def multiple_reference?(str, reference)
|
76
|
+
reference && str.include?("[")
|
77
|
+
end
|
78
|
+
|
79
|
+
# Receive:
|
80
|
+
# ("function", "name", "functionbla")
|
81
|
+
# ("if", "variable", "$(sKeyTouchScreen)")
|
82
|
+
# ("if", "operator", "equalto")
|
83
|
+
# ("if", "value", "KEY_CLEAR")
|
84
|
+
#
|
85
|
+
# Return:
|
86
|
+
# {:original => "functionbla", :value => "functionbla", :type => :string}
|
87
|
+
# {:original => "$(sKeyTouchScreen)", :value => "$(1)", :type => :string}
|
88
|
+
# {:original => "$(!sBufRecv[$(iFound)])", :value => "$(2.$&3)", :type => :string}
|
89
|
+
# {:original => "$(sBufRecv[$(iFound)])", :value => "$(2.$3)", :type => :string}
|
90
|
+
# {:original => "equalto", :value => "equalto", :type => :string}
|
91
|
+
# {:original => "KEY_CLEAR", :value => "KEY_CLEAR", :type => :string}
|
92
|
+
#
|
93
|
+
# - Important, the struct returned only has a parameter value with $()
|
94
|
+
# if isn't the stringvariable and integervariable value. Because those
|
95
|
+
# instructions require the variable number without $().
|
96
|
+
def get(instruction, parameter, value)
|
97
|
+
if value.include? VARIABLE_REFERENCE
|
98
|
+
value = remove_reference(value)
|
99
|
+
if variable_struct = self.find(instruction, parameter, value)
|
100
|
+
check_typing_mismatch(instruction, parameter, variable_struct)
|
101
|
+
variable_struct
|
102
|
+
else
|
103
|
+
self.create(instruction, parameter, value, true)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# Not a reference for other variable
|
107
|
+
self.create(instruction, parameter, value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def parameter_scheme(original = nil, value = nil, type = nil, reference = false)
|
112
|
+
if reference
|
113
|
+
value, index_variable, range_index = parse_index_variable(value, reference)
|
114
|
+
if index_variable
|
115
|
+
variable1 = self.get(VARIABLE_STRING, VARIABLE_PARAMETER_VALUE, value)
|
116
|
+
variable2 = self.get(VARIABLE_INTEGER, VARIABLE_PARAMETER_VALUE, index_variable)
|
117
|
+
format = format_multiple_reference(range_index, variable1[:value], variable2[:value])
|
118
|
+
|
119
|
+
{:original => original, :value => format, :type => type, :index_variable => index_variable}
|
120
|
+
else
|
121
|
+
{:original => original, :value => "#{VARIABLE_REFERENCE}#{value})", :type => type}
|
122
|
+
end
|
123
|
+
else
|
124
|
+
{:original => original, :value => value, :type => type}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def next_number(name, type)
|
129
|
+
value = self.number += 1
|
130
|
+
if type == :string
|
131
|
+
if (self.number_strings += 1) > VARIABLE_STRING_LIMIT
|
132
|
+
raise PosxmlCompiler::VariableStringLimitError.new("#{type} variable #{name} exceed the number of string variables")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
if value > VARIABLE_LIMIT
|
136
|
+
raise PosxmlCompiler::VariableLimitError.new("#{type} variable #{name} exceed the number of variables")
|
137
|
+
end
|
138
|
+
"$(#{value})"
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
def create_type_variable(parameter, value, type, reference)
|
143
|
+
if parameter == VARIABLE_PARAMETER_NAME
|
144
|
+
unless self.variables[value]
|
145
|
+
number = remove_reference(next_number(parameter, type))
|
146
|
+
self.variables[value] = self.parameter_scheme(value, number, type, true)
|
147
|
+
end
|
148
|
+
remove_reference_struct(self.variables[value])
|
149
|
+
else
|
150
|
+
# If is value, reference and the variable doesn't exists should raise an error because it wasn't declared
|
151
|
+
if parameter == VARIABLE_PARAMETER_VALUE && self.variables[value].nil? && reference && ! self.multiple_reference?(value, reference)
|
152
|
+
raise PosxmlCompiler::VariableUndefinedError.new("Variable #{value} undefined")
|
153
|
+
end
|
154
|
+
self.parameter_scheme(value, value, type, reference)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def create_function_variable(value, instruction)
|
159
|
+
raise PosxmlCompiler::VariableNameError.new("#{instruction} name #{value} can't be a reference") if value.include?(VARIABLE_REFERENCE)
|
160
|
+
self.parameter_scheme(value, self.functions.get(value), :string)
|
161
|
+
end
|
162
|
+
|
163
|
+
def create_ordinary_variable(instruction, parameter, value, reference)
|
164
|
+
if reference
|
165
|
+
type = self.xsd.parameter_type(instruction, parameter)
|
166
|
+
if self.multiple_reference?(value, reference)
|
167
|
+
self.parameter_scheme(value, value, type, reference)
|
168
|
+
else
|
169
|
+
raise PosxmlCompiler::VariableUndefinedError.new("Variable #{value} undefined") unless self.variables[value]
|
170
|
+
self.variables[value] = self.parameter_scheme(value, value, type, reference)
|
171
|
+
end
|
172
|
+
else
|
173
|
+
# isn't important consider a type in this case
|
174
|
+
self.parameter_scheme(value, value)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# parse_index_variable
|
179
|
+
# receive "!sBufRecv[$(iFound)]"
|
180
|
+
# return ["!sBufRecv", iFound, true]
|
181
|
+
# or
|
182
|
+
# receive "sBufRecv[$(iFound)]"
|
183
|
+
# return ["sBufRecv", iFound, false]
|
184
|
+
def parse_index_variable(value, reference)
|
185
|
+
#if reference && value.include?("[")
|
186
|
+
if self.multiple_reference?(value, reference)
|
187
|
+
index = "$(#{value.match(/\[\$\((.+)\)\]/)[1]})"
|
188
|
+
str = "$(#{value.split("[")[0]})"
|
189
|
+
|
190
|
+
if str[2] == "!"
|
191
|
+
str[2] = ""
|
192
|
+
[str, index, true]
|
193
|
+
else
|
194
|
+
[str, index, false]
|
195
|
+
end
|
196
|
+
else
|
197
|
+
value
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def format_multiple_reference(range_index, variable1, variable2)
|
202
|
+
str1 = variable1.sub(VARIABLE_REFERENCE, "").sub(")", "") # Format value to be like "123"
|
203
|
+
str2 = variable2.sub("(", "").sub(")", "") # format value to be "$123")
|
204
|
+
# At the end "$(123.$&456)"
|
205
|
+
if range_index
|
206
|
+
format = "#{VARIABLE_REFERENCE}#{str1}.#{str2.insert(1, "&")})"
|
207
|
+
else
|
208
|
+
format = "#{VARIABLE_REFERENCE}#{str1}.#{str2})"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def validate_size(name, original)
|
213
|
+
if original.size > VARIABLE_STRING_MAX_SIZE
|
214
|
+
raise PosxmlCompiler::StringSizeError.new("string variable #{name} exceed #{VARIABLE_STRING_MAX_SIZE} bytes [#{original}]")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def remove_reference_struct(data)
|
219
|
+
return unless data
|
220
|
+
struct = data.dup
|
221
|
+
struct[:value] = remove_reference(struct[:value])
|
222
|
+
struct
|
223
|
+
end
|
224
|
+
|
225
|
+
def remove_reference(str)
|
226
|
+
string = str.to_s
|
227
|
+
if string.include? VARIABLE_REFERENCE
|
228
|
+
string.to_s[2..-2]
|
229
|
+
else
|
230
|
+
string
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|