posxml_parser 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|