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'; +