lib/surpass/ExcelFormula.g in surpass-0.0.9 vs lib/surpass/ExcelFormula.g in surpass-0.1.0

- old
+ new

@@ -1,10 +1,10 @@ grammar ExcelFormula; options { - language = Ruby; - k = 2; + language = Ruby; + k = 2; } @header { RVA_DELTA = {"R" => 0, "V" => 0x20, "A" => 0x40} RVA_DELTA_REF = {"R" => 0, "V" => 0x20, "A" => 0x40, "D" => 0x20} @@ -73,16 +73,16 @@ prec3_expr[arg_type] { @rpn += op } )* ; prec3_expr[arg_type] - : prec5_expr[arg_type] + : prec4_expr[arg_type] ( ( POWER { op = [PTGPOWER].pack('C') } ) - prec5_expr[arg_type] { @rpn += op } + prec4_expr[arg_type] { @rpn += op } )* ; prec4_expr[arg_type] : prec5_expr[arg_type] @@ -124,35 +124,37 @@ @rpn += [PTGNUM, num_tok.text.to_f].pack("CE") } | ref2d_tok = REF2D { r, c = Utilities.cell_to_packed_rowcol(ref2d_tok.text) - ptg = PTGREFR + RVA_DELTA[arg_type] + ptg = PTGREFR + RVA_DELTA_REF[arg_type] @rpn += [ptg, r, c].pack("Cv2") } | ref2d1_tok = REF2D COLON ref2d2_tok = REF2D { 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] + ptg = PTGAREAR + RVA_DELTA_AREA[arg_type] @rpn += [ptg, r1, r2, c1, c2].pack("Cv4") } - + | LP expr[arg_type] RP + { + @rpn += [PTGPAREN].pack('C') + } | sheet1 = sheet { sheet2 = sheet1 } ( COLON sheet2 = sheet )? BANG ref3d_ref2d=REF2D { - ptg = PTGREF3DR + RVA_DELTA[arg_type] - rpn_ref2d = "" + ptg = PTGREF3DR + RVA_DELTA_REF[arg_type] 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] + ptg = PTGAREA3DR + RVA_DELTA_AREA[arg_type] r2, c2 = Utilities.cell_to_packed_rowcol(ref3d_ref2d2.text) rpn_ref2d = [0x0000, r1, r2, c1, c2].pack("v5") } )? { @@ -167,64 +169,62 @@ pos0 = @rpn.size - 2 } expr[arg_type] (SEMICOLON | COMMA) { @rpn += [PTGATTR, 0x08, 0].pack("C2v") # tAttrSkip - pos1 = @rpn.length - 2 + pos1 = @rpn.size - 2 - rem = @rpn.length - (pos0 + 2) - @rpn = @rpn[0..pos0] + [pos1-pos0].pack("v") + @rpn[pos0+2, rem] # TODO Check for OBO + @rpn = @rpn[0...pos0] + [pos1-pos0].pack("v") + @rpn[(pos0+2)...(@rpn.size)] } expr[arg_type] RP { @rpn += [PTGATTR, 0x08, 3].pack("C2v") # tAttrSkip @rpn += [PTGFUNCVARR, 3, 1].pack("C2v") # 3 = nargs, 1 = IF func - pos2 = @rpn.length + pos2 = @rpn.size - rem = @rpn.length - (pos1 + 2) - @rpn = @rpn[0..pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[pos1+2, rem] # TODO Check for OBO + @rpn = @rpn[0...pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[(pos1+2)...(@rpn.size)] } | FUNC_CHOOSE { arg_type = "R" rpn_chunks = [] } LP expr["V"] { - rpn_start = @rpn.length - ref_markers = [@sheet_references.length] + rpn_start = @rpn.size + ref_markers = [@sheet_references.size] } ( (SEMICOLON | COMMA) - { mark = @rpn.length } + { mark = @rpn.size } ( expr[arg_type] | { @rpn += [PTGMISSARG].pack("C") } ) { - rem = @rpn.length - mark + rem = @rpn.size - mark rpn_chunks << @rpn[mark, rem] ref_markers << @sheet_references.size } )* RP { - @rpn = @rpn[0..rpn_start] + @rpn = @rpn[0...rpn_start] nc = rpn_chunks.length chunklens = rpn_chunks.collect {|c| c.length} skiplens = [0] * nc - skiplens[-1] = 3 + skiplens[nc-1] = 3 (nc-1).downto(1) do |i| skiplens[i-1] = skiplens[i] + chunklens[i] + 4 end - jump_pos = [2 * nc + 2] + jump_pos = [2*nc + 2] (0...nc).each do |i| - jump_pos.append(jump_pos[-1] + chunklens[ic] + 4) + jump_pos << (jump_pos.last + chunklens[i] + 4) end - chunk_shift = 2 * nc + 6 # size of tAttrChoose + 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] = [ref[0], ref[1], ref[2] + chunk_shift] @@ -264,70 +264,81 @@ end } LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP { if (arg_count > max_argc) || (arg_count < min_argc) - raise "#{arg_count} parameters for function: #{func_tok.text}" + raise "incorrect number #{arg_count} of 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") + elsif (arg_count == 1) && (func_tok.text.upcase === "SUM") @rpn += [PTGATTR, 0x10, 0].pack("CCv") # tAttrSum else - func_ptg = PTGFUNCVARR + RVA_DELTA[func_type] + 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') - } ; - +// Process arguments to a function. expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt] -@members{ - arg_cnt = 0 - arg_type = arg_type_list[arg_cnt] +@init +{ + arg_cnt = 0 + + # Set these for processing first argument, + # it's simpler because first argument type can't be '...' + arg_type = arg_type_list.first + + # need to check for '-' for a fn with no arguments + arg_cnt += 1 unless arg_type === '-' } - : expr[arg_type] { arg_cnt += 1 } - ( - { - if arg_cnt < arg_type_list.size - arg_type = arg_type_list[arg_cnt] - else - arg_type = arg_type_list[-1] - end - if arg_type == "+" - arg_type = arg_type_list[-2] - end - } - (SEMICOLON | COMMA) - ( - expr[arg_type] - | { @rpn += [PTGMISSARG].pack("B") } - ) - { arg_cnt += 1 } - )* - | + : + (expr[arg_type] + ( + (SEMICOLON | COMMA) { arg_cnt += 1 } + ( + // *either* we find an argument after the comma/semicolon in which case process it... + expr[arg_type] + { + if arg_cnt - 2 < arg_type_list.size + arg_type = arg_type_list[arg_cnt - 2] + else + if arg_type_list.last === "..." + # e.g. "V R ..." arbitrary number of args of type R + # this will always be last element in arg_type_list + # 2nd to last element will provide type + arg_type = arg_type_list[arg_type_list.size - 2] + else + # Just read last element normally. + arg_type = arg_type_list[arg_cnt - 2] + end + end + } + // ... *or* we have a missing argument and need to insert a placeholder + | { @rpn += [PTGMISSARG].pack("C") } + ) + )* + ) + | // Some functions have no arguments e.g. pi() ; - + sheet returns[ref] : 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, len(sheet_ref_quote.text) - 1].replace("''", "'") } ; - + EQ: '='; LT: '<'; GT: '>'; NE: '<>'; LE: '<='; @@ -343,11 +354,11 @@ COMMA: ','; LP: '('; RP: ')'; CONCAT: '&'; -PERCENT: '%'; +PERCENT: '\%'; POWER: '^'; BANG: '!'; fragment DIGIT: '0'..'9'; @@ -355,10 +366,13 @@ 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'; +fragment ALPHA: 'a'..'z' | 'A'..'Z'; +NAME: ALPHA+ ; + +WS: (' ')+ {skip()};