lib/surpass/ExcelFormula.g in surpass-0.0.7 vs lib/surpass/ExcelFormula.g in surpass-0.0.9
- old
+ new
@@ -5,10 +5,12 @@
k = 2;
}
@header {
RVA_DELTA = {"R" => 0, "V" => 0x20, "A" => 0x40}
+ RVA_DELTA_REF = {"R" => 0, "V" => 0x20, "A" => 0x40, "D" => 0x20}
+ RVA_DELTA_AREA = {"R" => 0, "V" => 0x20, "A" => 0x40, "D" => 0}
}
@init {
@rpn = ''
@sheet_references = []
@@ -26,15 +28,15 @@
expr[arg_type]
: prec0_expr[arg_type]
(
(
EQ { op = [PTGEQ].pack('C') }
-/* | NE { op = [PTGNE].pack('C') } */
-/* | GT { op = [PTGGT].pack('C') } */
-/* | LT { op = [PTGLT].pack('C') } */
-/* | GE { op = [PTGGE].pack('C') } */
-/* | LE { op = [PTGLE].pack('C') } */
+ | NE { op = [PTGNE].pack('C') }
+ | GT { op = [PTGGT].pack('C') }
+ | LT { op = [PTGLT].pack('C') }
+ | GE { op = [PTGGE].pack('C') }
+ | LE { op = [PTGLE].pack('C') }
)
prec0_expr[arg_type] { @rpn += op }
)*
;
@@ -103,11 +105,12 @@
{
@rpn += [PTGBOOL, 0].pack("C2")
}
| str_tok = STR_CONST
{
- @rpn += [PTGSTR].pack('C') + upack1(str_tok.text[1, -1]) #TODO
+ s = str_tok.text.gsub("\"", "")
+ @rpn += [PTGSTR].pack("C") + [s.length].pack('v') + s
}
| int_tok = INT_CONST
{
int_value = int_tok.text.to_i
if int_value <= 65535
@@ -116,95 +119,100 @@
@rpn += [PTGNUM, int_value.to_f].pack("CE")
end
}
| num_tok = NUM_CONST
{
- @rpn += [ptgNum, num_tok.text.to_f].pack("CE")
+ @rpn += [PTGNUM, num_tok.text.to_f].pack("CE")
}
| ref2d_tok = REF2D
{
- r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text) # TODO
+ r, c = Utilities.cell_to_packed_rowcol(ref2d_tok.text)
ptg = PTGREFR + RVA_DELTA[arg_type]
@rpn += [ptg, r, c].pack("Cv2")
}
| ref2d1_tok = REF2D COLON ref2d2_tok = REF2D
{
- r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text) #TODO
- r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text) #TODO
+ r1, c1 = Utilities.cell_to_packed_rowcol(ref2d1_tok.text)
+ r2, c2 = Utilities.cell_to_packed_rowcol(ref2d2_tok.text)
ptg = PTGAREAR + RVA_DELTA[arg_type]
- self.rpn += struct.pack("Cv4", ptg, r1, r2, c1, c2)
+ @rpn += [ptg, r1, r2, c1, c2].pack("Cv4")
}
-/* | sheet1 = sheet
+
+ | sheet1 = sheet
{
sheet2 = sheet1
}
( COLON sheet2 = sheet )? BANG ref3d_ref2d=REF2D
{
ptg = PTGREF3DR + RVA_DELTA[arg_type]
rpn_ref2d = ""
- r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text) #TODO
+ r1, c1 = Utilities.cell_to_packed_rowcol(ref3d_ref2d.text)
rpn_ref2d = [0x0000, r1, c1].pack("v3")
}
( COLON ref3d_ref2d2= REF2D
{
ptg = PTGAREA3DR + RVA_DELTA[arg_type]
- r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text) #TODO
+ r2, c2 = Utilities.cell_to_packed_rowcol(ref3d_ref2d2.text)
rpn_ref2d = [0x0000, r1, r2, c1, c2].pack("v5")
}
)?
{
@rpn += [ptg].pack("C")
@sheet_references << [sheet1, sheet2, @rpn.size]
@rpn += rpn_ref2d
}
- | FUNC_IF
+ | FUNC_IF
LP expr["V"] (SEMICOLON | COMMA)
{
@rpn += [PTGATTR, 0x02, 0].pack("C2v") # tAttrIf
pos0 = @rpn.size - 2
}
expr[arg_type] (SEMICOLON | COMMA)
{
@rpn += [PTGATTR, 0x08, 0].pack("C2v") # tAttrSkip
- pos1 = @rpn.size - 2
- @rpn = @rpn[0...pos0] + [pos1-pos0].pack("v") + @rpn[pos0+2...-1] # TODO
+ pos1 = @rpn.length - 2
+
+ rem = @rpn.length - (pos0 + 2)
+ @rpn = @rpn[0..pos0] + [pos1-pos0].pack("v") + @rpn[pos0+2, rem] # TODO Check for OBO
}
expr[arg_type] RP
{
@rpn += [PTGATTR, 0x08, 3].pack("C2v") # tAttrSkip
@rpn += [PTGFUNCVARR, 3, 1].pack("C2v") # 3 = nargs, 1 = IF func
- pos2 = @rpn.size
- @rpn = @rpn[0...pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[pos1+2...-1] # TODO
+ pos2 = @rpn.length
+
+ rem = @rpn.length - (pos1 + 2)
+ @rpn = @rpn[0..pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[pos1+2, rem] # TODO Check for OBO
}
- | FUNC_CHOOSE
+ | FUNC_CHOOSE
{
arg_type = "R"
rpn_chunks = []
}
- LP expr["V"] // first argument (the selector)
+ LP expr["V"]
{
- rpn_start = @rpn.size
- ref_markers = [@sheet_references.size]
+ rpn_start = @rpn.length
+ ref_markers = [@sheet_references.length]
}
(
(SEMICOLON | COMMA)
- { mark = @rpn.size }
+ { mark = @rpn.length }
(
expr[arg_type]
| { @rpn += [PTGMISSARG].pack("C") }
)
{
- rpn_chunks.append(@rpn[mark...-1])
- ref_markers.append(@sheet_references.size)
+ rem = @rpn.length - mark
+ rpn_chunks << @rpn[mark, rem]
+ ref_markers << @sheet_references.size
}
)*
RP
{
- # TODO test this, no idea if it works, just blindly translated
- @rpn = @rpn[0...rpn_start]
- nc = rpn_chunks.size
- chunklens = rpn_chunks.collect {|c| c.size}
+ @rpn = @rpn[0..rpn_start]
+ nc = rpn_chunks.length
+ chunklens = rpn_chunks.collect {|c| c.length}
skiplens = [0] * nc
skiplens[-1] = 3
(nc-1).downto(1) do |i|
skiplens[i-1] = skiplens[i] + chunklens[i] + 4
@@ -217,84 +225,77 @@
chunk_shift = 2 * nc + 6 # size of tAttrChoose
(0...nc).each do |i|
(ref_markers[i]...ref_markers[i+1]).each do |r|
ref = @sheet_references[r]
- @sheet_references[r] = [r[0], r[1], r[2] + chunk_shift]
+ @sheet_references[r] = [ref[0], ref[1], ref[2] + chunk_shift]
end
chunk_shift += 4 # size of tAttrSkip
end
choose_rpn = []
- choose_rpn.append([PTGATTR, 0x04, nc].pack("CCv")) # 0x04 is tAttrChoose
- choose_rpn.append(jump_pos.pack("v*"))
+ choose_rpn << [PTGATTR, 0x04, nc].pack("CCv") # 0x04 is tAttrChoose
+ choose_rpn << jump_pos.pack("v*")
(0...nc).each do |i|
choose_rpn << rpn_chunks[i]
choose_rpn << [PTGATTR, 0x08, skiplens[i]].pack("CCv") # 0x08 is tAttrSkip
end
- choose_rpn.append([PTGFUNCVARV, nc+1, 100].pack("CCv")) # 100 is CHOOSE fn
+ choose_rpn << [PTGFUNCVARV, nc+1, 100].pack("CCv") # 100 is CHOOSE fn
@rpn += choose_rpn.join
}
-
| name_tok = NAME
{
- raise "[formula] found unexpected NAME token #{name_tok.text}"
- # TODO: handle references to defined names here
+ raise "[formula] found unexpected NAME token #{name_tok.text}"
}
| func_tok = NAME
{
- raise "not implemented"
- # func_toku = func_tok.text.upper()
- # if func_toku in all_funcs_by_name:
- # (opcode,
- # min_argc,
- # max_argc,
- # func_type,
- # arg_type_str) = all_funcs_by_name[func_toku]
- # arg_type_list = list(arg_type_str)
- # else:
- # raise Exception("[formula] unknown function #{func_tok.text}")
- # xcall = opcode < 0
- # if xcall:
- # # The name of the add-in function is passed as the 1st arg
- # # of the hidden XCALL function
- # self.xcall_references.append((func_toku, len(self.rpn) + 1))
- # self.rpn += struct.pack("<BHHH",
- # ptgNameXR,
- # 0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record
- # 0xefbe, # ##PATCHME## one-based index to EXTERNNAME record
- # 0x0000) # unused
+ func_toku = func_tok.text.upcase
+ if STD_FUNC_BY_NAME.has_key?(func_toku)
+ opcode, min_argc, max_argc, func_type, arg_type_str = STD_FUNC_BY_NAME[func_toku]
+ arg_type_list = arg_type_str.split
+ else
+ raise "[formula] unknown function #{func_tok.text}"
+ end
+ xcall = (opcode < 0)
+
+ if xcall
+ @xcall_references << [func_toku, @rpn.size + 1]
+ @rpn += [PTGNAMEXR, 0xadde, 0xefbe, 0x0000].pack("Cvvv")
+ end
}
LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP
{
- raise "not implemented"
- # if arg_count > max_argc or arg_count < min_argc:
- # raise Exception, "#{arg_count} parameters for function: #{func_tok.text}"
- # if xcall:
- # func_ptg = ptgFuncVarR + _RVAdelta[func_type]
- # self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
- # elif min_argc == max_argc:
- # func_ptg = ptgFuncR + _RVAdelta[func_type]
- # self.rpn += struct.pack("<BH", func_ptg, opcode)
- # elif arg_count == 1 and func_tok.text.upper() == "SUM":
- # self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum
- # else:
- # func_ptg = ptgFuncVarR + _RVAdelta[func_type]
- # self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
+ if (arg_count > max_argc) || (arg_count < min_argc)
+ raise "#{arg_count} parameters for function: #{func_tok.text}"
+ end
+
+ if xcall
+ func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
+ @rpn += [func_ptg, arg_count + 1, 255].pack("CCv") # 255 is magic XCALL function
+ elsif (min_argc == max_argc)
+ func_ptg = PTGFUNCR + RVA_DELTA[func_type]
+ @rpn += [func_ptg, opcode].pack("Cv")
+ elsif (arg_count == 1) && (func_tok.text.upcase == "SUM")
+ @rpn += [PTGATTR, 0x10, 0].pack("CCv") # tAttrSum
+ else
+ func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
+ @rpn += [func_ptg, arg_count, opcode].pack("CCv")
+ end
}
| LP expr[arg_type] RP
{
@rpn += [PTGPAREN].pack('C')
}
;
+
expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt]
@members{
- arg_cnt = 0
- arg_type = arg_type_list[arg_cnt]
- }
+ arg_cnt = 0
+ arg_type = arg_type_list[arg_cnt]
+}
: expr[arg_type] { arg_cnt += 1 }
(
{
if arg_cnt < arg_type_list.size
arg_type = arg_type_list[arg_cnt]
@@ -319,18 +320,14 @@
: sheet_ref_name = NAME
{ ref = sheet_ref_name.text }
| sheet_ref_int = INT_CONST
{ ref = sheet_ref_int.text }
| sheet_ref_quote = QUOTENAME
- { ref = sheet_ref_quote.text[1..-1].replace("''", "'") # TODO }
+ { ref = sheet_ref_quote.text[1, len(sheet_ref_quote.text) - 1].replace("''", "'") }
;
-*/
-
-;
-
-fragment
+
EQ: '=';
LT: '<';
GT: '>';
NE: '<>';
LE: '<=';
@@ -350,17 +347,18 @@
CONCAT: '&';
PERCENT: '%';
POWER: '^';
BANG: '!';
-DIGIT: '0'..'9';
+fragment DIGIT: '0'..'9';
INT_CONST: DIGIT+ ;
-NUM_CONST: DIGIT* '.' DIGIT+ (('E'|'e') ('+'|'-')? DIGIT+)?; // \d*\.\d+(?:[Ee][+-]?\d+)?
-STR_CONST: '"' ~'"' '"'; // TODO add escape recognition
+NUM_CONST: DIGIT* '.' DIGIT+ (('E'|'e') ('+'|'-')? DIGIT+)?;
+STR_CONST: '"' (~'"')+ '"';
REF2D: '$'? ('A'..'I')? ('A'..'Z') '$'? DIGIT+;
TRUE_CONST: ('T'|'t') ('R'|'r') ('U'|'u') ('E'|'e') ;
FALSE_CONST: ('F'|'f') ('A'|'a') ('L'|'l') ('S'|'s') ('E'|'e') ;
NAME: '\w[\.\w]*' ;
QUOTENAME: '\'(?:[^\']|\'\')*\'';
FUNC_IF: 'IF';
FUNC_CHOOSE: 'CHOOSE';
+