From 382c157c110fe44e21f1ee025874a7bc031d6167 Mon Sep 17 00:00:00 2001 From: Todd Date: Fri, 15 Sep 2017 16:13:41 -0500 Subject: [PATCH] formula: add more math formulas - ABS - ACOS - ACOSH - ARABIC - ASIN - ASINH - ATAN - ATANH - ATAN2 - BASE - CEILING.MATH - CEILING.PRECISE - COMBIN - COMBINA - COS - COSH - DECIMAL - DEGREES - PI --- .../spreadsheet/formula-evaluation/main.go | 3 +- spreadsheet/formula/emptyexpr.go | 23 + spreadsheet/formula/fnmathtrig.go | 404 ++++++ spreadsheet/formula/fnsimple.go | 6 + spreadsheet/formula/gen.sh | 2 +- spreadsheet/formula/grammar.go | 49 +- spreadsheet/formula/grammar.y | 10 +- spreadsheet/formula/lexer.go | 1256 +++++++++-------- spreadsheet/formula/lexer.rl | 4 +- spreadsheet/formula/parser.go | 5 +- spreadsheet/formula/result.go | 34 +- spreadsheet/formula/resulttype_string.go | 4 +- .../formula/testdata/formulareference.xlsx | Bin 8283 -> 10709 bytes 13 files changed, 1216 insertions(+), 584 deletions(-) create mode 100644 spreadsheet/formula/emptyexpr.go create mode 100644 spreadsheet/formula/fnmathtrig.go diff --git a/_examples/spreadsheet/formula-evaluation/main.go b/_examples/spreadsheet/formula-evaluation/main.go index 290b79fb..39c126a3 100644 --- a/_examples/spreadsheet/formula-evaluation/main.go +++ b/_examples/spreadsheet/formula-evaluation/main.go @@ -4,9 +4,8 @@ package main import ( "fmt" - "baliance.com/gooxml/spreadsheet/formula" - "baliance.com/gooxml/spreadsheet" + "baliance.com/gooxml/spreadsheet/formula" ) func main() { diff --git a/spreadsheet/formula/emptyexpr.go b/spreadsheet/formula/emptyexpr.go new file mode 100644 index 00000000..626f1542 --- /dev/null +++ b/spreadsheet/formula/emptyexpr.go @@ -0,0 +1,23 @@ +// Copyright 2017 Baliance. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. + +package formula + +type EmptyExpr struct { +} + +func NewEmptyExpr() Expression { + return EmptyExpr{} +} + +func (e EmptyExpr) Eval(ctx Context, ev Evaluator) Result { + return MakeEmptyResult() +} + +func (e EmptyExpr) Reference(ctx Context, ev Evaluator) Reference { + return ReferenceInvalid +} diff --git a/spreadsheet/formula/fnmathtrig.go b/spreadsheet/formula/fnmathtrig.go new file mode 100644 index 00000000..9be16327 --- /dev/null +++ b/spreadsheet/formula/fnmathtrig.go @@ -0,0 +1,404 @@ +// Copyright 2017 Baliance. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. + +package formula + +import ( + "fmt" + "math" + "strconv" + "strings" +) + +func init() { + RegisterFunction("ABS", makeMathWrapper("ASIN", math.Abs)) + RegisterFunction("ACOS", makeMathWrapper("ASIN", math.Acos)) + RegisterFunction("ACOSH", makeMathWrapper("ASIN", math.Acosh)) + // TODO: RegisterFunction("ACOT", Acot) /// Excel 2013+ + // TODO: RegisterFunction("ACOTH", Acoth) /// Excel 2013+ + // TODO: RegisterFunction("_xlfn.AGGREGATE", Aggregate) // lots of dependencies + RegisterFunction("_xlfn.ARABIC", Arabic) + RegisterFunction("ASIN", makeMathWrapper("ASIN", math.Asin)) + RegisterFunction("ASINH", makeMathWrapper("ASINH", math.Asinh)) + RegisterFunction("ATAN", makeMathWrapper("ATAN", math.Atan)) + RegisterFunction("ATANH", makeMathWrapper("ATANH", math.Atanh)) + RegisterFunction("ATAN2", Atan2) + RegisterFunction("_xlfn.BASE", Base) + // RegisterFunction("CEILING", ) // TODO: figure out how this acts, Libre doesn't use it + RegisterFunction("_xlfn.CEILING.MATH", CeilingMath) + RegisterFunction("_xlfn.CEILING.PRECISE", CeilingPrecise) + RegisterFunction("COMBIN", Combin) + RegisterFunction("_xlfn.COMBINA", Combina) + RegisterFunction("COS", makeMathWrapper("COS", math.Cos)) + RegisterFunction("COSH", makeMathWrapper("COSH", math.Cosh)) + //RegisterFunction("COT", + //RegisterFunction("COTH" + //RegisterFunction("CSC" + //RegisterFunction("CSCH" + RegisterFunction("_xlfn.DECIMAL", Decimal) + RegisterFunction("DEGREES", Degrees) + RegisterFunction("PI", Pi) +} + +// makeMathWrapper is used to wrap single argument math functions from the Go +// standard library and present them as a spreadsheet function. +func makeMathWrapper(name string, fn func(x float64) float64) Function { + return func(args []Result) Result { + if len(args) != 1 { + return MakeErrorResult(name + " requires one argument") + } + + arg := args[0].AsNumber() + switch arg.Type { + case ResultTypeNumber: + v := fn(arg.ValueNumber) + if v != v { + return MakeErrorResult(name + " returned NaN") + } + return MakeNumberResult(v) + case ResultTypeList, ResultTypeString: + return MakeErrorResult(name + " requires a numeric argument") + case ResultTypeError: + return arg + default: + return MakeErrorResult(fmt.Sprintf("unhandled %s() argument type %s", name, arg.Type)) + } + } +} + +// Atan2 implements the Excel ATAN2 function. It accepts two numeric arguments, +// and the arguments are (x,y), reversed from normal to match Excel's behaviour. +func Atan2(args []Result) Result { + if len(args) != 2 { + return MakeErrorResult("ATAN2 requires two arguments") + } + + arg1 := args[0].AsNumber() + arg2 := args[1].AsNumber() + if arg1.Type == ResultTypeNumber && arg2.Type == ResultTypeNumber { + // args are swapped here + v := math.Atan2(arg2.ValueNumber, arg1.ValueNumber) + if v != v { + return MakeErrorResult("ATAN2 returned NaN") + } + return MakeNumberResult(v) + } + + for _, t := range []ResultType{arg1.Type, arg2.Type} { + switch t { + case ResultTypeList, ResultTypeString: + return MakeErrorResult("ATAN2 requires a numeric argument") + case ResultTypeError: + return arg1 + default: + return MakeErrorResult(fmt.Sprintf("unhandled ATAN2() argument type %s", t)) + } + } + return MakeErrorResult("unhandled error for ATAN2()") +} + +// Arabic implements the Excel ARABIC function which parses roman numerals. It +// accepts one numeric argument. +func Arabic(args []Result) Result { + if len(args) != 1 { + return MakeErrorResult("ARABIC requires one argument") + } + arg := args[0] + switch arg.Type { + case ResultTypeNumber, ResultTypeList, ResultTypeEmpty: + return MakeErrorResult("ARABIC requires a string argument argument") + case ResultTypeString: + res := 0.0 + last := 0.0 + for _, c := range arg.ValueString { + digit := 0.0 + switch c { + case 'I': + digit = 1 + case 'V': + digit = 5 + case 'X': + digit = 10 + case 'L': + digit = 50 + case 'C': + digit = 100 + case 'D': + digit = 500 + case 'M': + digit = 1000 + } + res += digit + switch { + // repeated digits + case last == digit && + (last == 5 || last == 50 || last == 500): + return MakeErrorResult("invalid ARABIC format") + // simpler form + case 2*last == digit: + return MakeErrorResult("invalid ARABIC format") + } + if last < digit { + res -= 2 * last + } + last = digit + } + + return MakeNumberResult(res) + case ResultTypeError: + return arg + default: + return MakeErrorResult(fmt.Sprintf("unhandled ACOSH() argument type %s", arg.Type)) + } +} + +// CeilingMath implements _xlfn.CEILING.MATH which rounds numbers to the nearest +// multiple of the second argument, toward or away from zero as specified by the +// third argument. +func CeilingMath(args []Result) Result { + if len(args) == 0 { + return MakeErrorResult("CEILING.MATH() requires at least one argument") + } + if len(args) > 3 { + return MakeErrorResult("CEILING.MATH() allows at most three arguments") + } + // number to round + number := args[0].AsNumber() + if number.Type != ResultTypeNumber { + return MakeErrorResult("first arugment to CEILING.MATH() must be a number") + } + + // significance + significance := float64(1) + if number.ValueNumber < 0 { + significance = -1 + } + if len(args) > 1 { + sigArg := args[1].AsNumber() + if sigArg.Type != ResultTypeNumber { + return MakeErrorResult("second arugment to CEILING.MATH() must be a number") + } + significance = sigArg.ValueNumber + } + + // round direction + direction := float64(1) + if len(args) > 2 { + dirArg := args[2].AsNumber() + if dirArg.Type != ResultTypeNumber { + return MakeErrorResult("third arugment to CEILING.MATH() must be a number") + } + direction = dirArg.ValueNumber + } + + if len(args) == 1 { + return MakeNumberResult(math.Ceil(number.ValueNumber)) + } + + v := number.ValueNumber + v, res := math.Modf(v / significance) + if res != 0 { + if number.ValueNumber > 0 { + v++ + } else if direction < 0 { + v-- + } + } + return MakeNumberResult(v * significance) +} + +func CeilingPrecise(args []Result) Result { + if len(args) == 0 { + return MakeErrorResult("CEILING.PRECISE() requires at least one argument") + } + if len(args) > 2 { + return MakeErrorResult("CEILING.PRECISE() allows at most two arguments") + } + // number to round + number := args[0].AsNumber() + if number.Type != ResultTypeNumber { + return MakeErrorResult("first arugment to CEILING.PRECISE() must be a number") + } + + // significance + significance := float64(1) + if number.ValueNumber < 0 { + significance = -1 + } + if len(args) > 1 { + sigArg := args[1].AsNumber() + if sigArg.Type != ResultTypeNumber { + return MakeErrorResult("second arugment to CEILING.MATH() must be a number") + } + // don't care about sign of significance + significance = math.Abs(sigArg.ValueNumber) + } + + if len(args) == 1 { + return MakeNumberResult(math.Ceil(number.ValueNumber)) + } + + v := number.ValueNumber + v, res := math.Modf(v / significance) + if res != 0 { + if number.ValueNumber > 0 { + v++ + } + } + return MakeNumberResult(v * significance) +} + +// Base is an implementation of the Excel BASE function that returns a string +// form of an integer in a specified base and of a minimum length with padded +// zeros. +func Base(args []Result) Result { + if len(args) < 2 { + return MakeErrorResult("BASE() requires at least two arguments") + } + if len(args) > 3 { + return MakeErrorResult("BASE() allows at most three arguments") + } + // number to convert + number := args[0].AsNumber() + if number.Type != ResultTypeNumber { + return MakeErrorResult("first arugment to BASE() must be a number") + } + + radixArg := args[1].AsNumber() + if radixArg.Type != ResultTypeNumber { + return MakeErrorResult("second arugment to BASE() must be a number") + } + radix := int(radixArg.ValueNumber) + if radix < 0 || radix > 36 { + return MakeErrorResult("radix must be in the range [0,36]") + } + + // min length of result + minLength := 0 + if len(args) > 2 { + lenArg := args[2].AsNumber() + if lenArg.Type != ResultTypeNumber { + return MakeErrorResult("third arugment to BASE() must be a number") + } + minLength = int(lenArg.ValueNumber) + } + + s := strconv.FormatInt(int64(number.ValueNumber), radix) + if len(s) < minLength { + s = strings.Repeat("0", minLength-len(s)) + s + } + return MakeStringResult(s) +} + +// Combin is an implementation of the Excel COMBINA function whic returns the +// number of combinations. +func Combin(args []Result) Result { + if len(args) != 2 { + return MakeErrorResult("COMBIN() requires two argument") + } + nArg := args[0].AsNumber() + kArg := args[1].AsNumber() + if nArg.Type != ResultTypeNumber || kArg.Type != ResultTypeNumber { + return MakeErrorResult("COMBIN() requires numeric arguments") + } + n := math.Trunc(nArg.ValueNumber) + k := math.Trunc(kArg.ValueNumber) + if k > n { + return MakeErrorResult("COMBIN() requires k <= n") + } + if k == n || k == 0 { + return MakeNumberResult(1) + } + + res := float64(1) + for i := float64(1); i <= k; i++ { + res *= (n + 1 - i) / i + } + + return MakeNumberResult(res) +} + +// Combina is an implementation of the Excel COMBINA function whic returns the +// number of combinations with repetitions. +func Combina(args []Result) Result { + if len(args) != 2 { + return MakeErrorResult("COMBINA() requires two argument") + } + nArg := args[0].AsNumber() + kArg := args[1].AsNumber() + if nArg.Type != ResultTypeNumber || kArg.Type != ResultTypeNumber { + return MakeErrorResult("COMBINA() requires numeric arguments") + } + n := math.Trunc(nArg.ValueNumber) + k := math.Trunc(kArg.ValueNumber) + if n < k { + return MakeErrorResult("COMBINA() requires n > k") + } + if n == 0 { + return MakeNumberResult(0) + } + args[0] = MakeNumberResult(n + k - 1) + args[1] = MakeNumberResult(n - 1) + return Combin(args) +} + +func fact(f float64) float64 { + res := float64(1) + for i := float64(2); i <= f; i++ { + res *= i + } + return res +} + +// Decimal is an implementation of the Excel function DECIMAL() that parses a string +// in a given base and returns the numeric result. +func Decimal(args []Result) Result { + if len(args) != 2 { + return MakeErrorResult("DECIMAL() requires two arguments") + } + sArg := args[0].AsString() + if sArg.Type != ResultTypeString { + return MakeErrorResult("DECIMAL() requires string first argument") + } + baseArg := args[1].AsNumber() + if baseArg.Type != ResultTypeNumber { + return MakeErrorResult("DECIMAL() requires number second argument") + } + + sv := sArg.ValueString + if len(sv) > 2 && (strings.HasPrefix(sv, "0x") || strings.HasPrefix(sv, "0X")) { + sv = sv[2:] + } + i, err := strconv.ParseInt(sv, int(baseArg.ValueNumber), 64) + if err != nil { + return MakeErrorResult("DECIMAL() error in conversion") + } + return MakeNumberResult(float64(i)) +} + +// Degrees is an implementation of the Excel function DEGREES() that converts +// radians to degrees. +func Degrees(args []Result) Result { + if len(args) != 1 { + return MakeErrorResult("DEGREES() requires one argument") + } + vArg := args[0].AsNumber() + if vArg.Type != ResultTypeNumber { + return MakeErrorResult("DEGREES() requires number argument") + } + + return MakeNumberResult(180.0 / math.Pi * vArg.ValueNumber) +} + +// Pi is an implementation of the Excel Pi() function that just returns the Pi +// constant. +func Pi(args []Result) Result { + if len(args) != 0 { + return MakeErrorResult("PI() accepts no arguments") + } + return MakeNumberResult(math.Pi) +} diff --git a/spreadsheet/formula/fnsimple.go b/spreadsheet/formula/fnsimple.go index d2565378..9dd1e59d 100644 --- a/spreadsheet/formula/fnsimple.go +++ b/spreadsheet/formula/fnsimple.go @@ -34,6 +34,8 @@ func Sum(args []Result) Result { // treated as zero by Excel case ResultTypeError: return a + case ResultTypeEmpty: + // skip default: return MakeErrorResult(fmt.Sprintf("unhandled SUM() argument type %s", a.Type)) } @@ -64,6 +66,8 @@ func Min(args []Result) Result { if 0 < res.ValueNumber { res.ValueNumber = 0 } + case ResultTypeEmpty: + // skip case ResultTypeError: return a default: @@ -91,6 +95,8 @@ func Max(args []Result) Result { if subMax.ValueNumber > res.ValueNumber { res.ValueNumber = subMax.ValueNumber } + case ResultTypeEmpty: + // skip case ResultTypeString: // treated as zero by Excel if 0 > res.ValueNumber { diff --git a/spreadsheet/formula/gen.sh b/spreadsheet/formula/gen.sh index a7dea680..3f1dd329 100755 --- a/spreadsheet/formula/gen.sh +++ b/spreadsheet/formula/gen.sh @@ -1,6 +1,6 @@ #!/bin/bash echo "lexer" -ragel -G2 -Z lexer.rl +ragel -G0 -Z lexer.rl goimports -w lexer.go echo "parser" diff --git a/spreadsheet/formula/grammar.go b/spreadsheet/formula/grammar.go index 15c075e9..e745b39c 100644 --- a/spreadsheet/formula/grammar.go +++ b/spreadsheet/formula/grammar.go @@ -98,13 +98,13 @@ const yyLast = 145 var yyAct = [...]int{ - 42, 43, 3, 39, 59, 35, 34, 26, 36, 37, - 17, 24, 25, 26, 38, 26, 33, 60, 21, 41, - 19, 11, 33, 9, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 61, 56, 1, 22, - 23, 24, 25, 26, 31, 27, 28, 29, 30, 32, - 58, 18, 33, 10, 2, 8, 0, 0, 0, 0, - 0, 62, 57, 22, 23, 24, 25, 26, 31, 27, + 3, 24, 25, 26, 59, 34, 26, 36, 37, 17, + 26, 39, 33, 38, 35, 33, 21, 60, 41, 19, + 42, 43, 62, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 61, 56, 11, 22, 23, + 24, 25, 26, 31, 27, 28, 29, 30, 32, 58, + 9, 33, 1, 18, 10, 2, 8, 0, 0, 0, + 0, 63, 57, 22, 23, 24, 25, 26, 31, 27, 28, 29, 30, 32, 0, 0, 33, 22, 23, 24, 25, 26, 31, 27, 28, 29, 30, 32, 0, 0, 33, 22, 23, 24, 25, 26, 0, 0, 0, 13, @@ -116,42 +116,42 @@ var yyAct = [...]int{ } var yyPact = [...]int{ - 92, -1000, -1000, 58, 124, -19, 124, 124, -1000, -1000, - -1000, -1000, 124, -1000, -1000, -1000, -1000, -27, -1000, -1000, + 92, -1000, -1000, 58, 124, -10, 124, 124, -1000, -1000, + -1000, -1000, 124, -1000, -1000, -1000, -1000, -19, -1000, -1000, 110, -1000, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 58, 124, -10, -10, 44, 5, - -1000, -14, -1000, 58, -10, -10, -16, -16, -1000, 72, - 72, 72, 72, 72, 72, -8, 20, -1000, -1000, -1000, - 124, -1000, -1000, + 124, 124, 124, 124, 58, 124, -20, -20, 44, 3, + -1000, -14, -1000, 58, -20, -20, -17, -17, -1000, 72, + 72, 72, 72, 72, 72, -13, 19, -1000, -1000, -1000, + 124, -1000, -1000, 58, } var yyPgo = [...]int{ - 0, 1, 55, 54, 53, 10, 51, 38, 23, 21, - 0, 20, 19, + 0, 0, 56, 55, 54, 9, 53, 52, 50, 37, + 22, 20, 19, 18, } var yyR1 = [...]int{ 0, 7, 3, 3, 3, 8, 8, 8, 8, 1, 1, 1, 2, 2, 2, 2, 4, 4, 5, 6, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 9, 9, 9, 12, 12, 10, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 9, 9, 9, 13, 13, 11, 10, 10, } var yyR2 = [...]int{ 0, 1, 1, 2, 4, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 3, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 1, 2, 3, 1, 3, 1, + 3, 3, 1, 2, 3, 1, 3, 1, 1, 0, } var yyChk = [...]int{ -1000, -7, -3, -1, 24, 15, 19, 20, -2, -8, - -4, -9, 17, 7, 8, 9, 10, -5, -6, -11, + -4, -9, 17, 7, 8, 9, 10, -5, -6, -12, 14, 13, 19, 20, 21, 22, 23, 25, 26, 27, 28, 24, 29, 32, -1, 24, -1, -1, -1, 30, - 18, -12, -10, -1, -1, -1, -1, -1, -1, -1, + 18, -13, -11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 18, -5, 18, - 31, 16, -10, + 31, 16, -10, -1, } var yyDef = [...]int{ @@ -161,7 +161,7 @@ var yyDef = [...]int{ 0, 0, 0, 0, 3, 0, 9, 10, 0, 0, 33, 0, 35, 37, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 15, 19, 34, - 0, 4, 36, + 39, 4, 36, 38, } var yyTok1 = [...]int{ @@ -652,6 +652,11 @@ yydefault: { yyVAL.args = append(yyDollar[1].args, yyDollar[3].expr) } + case 39: + yyDollar = yyS[yypt-0 : yypt+1] + { + yyVAL.expr = NewEmptyExpr() + } } goto yystack /* stack new state and value */ } diff --git a/spreadsheet/formula/grammar.y b/spreadsheet/formula/grammar.y index 34255c68..33878335 100644 --- a/spreadsheet/formula/grammar.y +++ b/spreadsheet/formula/grammar.y @@ -17,7 +17,7 @@ package formula } %type formula formula1 initial reference referenceItem refFunctionCall -%type start constant functionCall argument +%type start constant functionCall argument argument1 %type binOp %type arguments @@ -96,10 +96,14 @@ functionCall: | tokenFunctionBultin arguments tokenRParen { $$ = NewFunction($1.val,$2)} ; arguments: - argument { $$ = append($$, $1) } + argument1{ $$ = append($$, $1) } | arguments tokenComma argument { $$ = append($1,$3) } ; -argument: formula; +argument1: formula ; + +argument: + formula + | /*empty*/ { $$ = NewEmptyExpr() } ; %% diff --git a/spreadsheet/formula/lexer.go b/spreadsheet/formula/lexer.go index e85ea6c6..774b110b 100644 --- a/spreadsheet/formula/lexer.go +++ b/spreadsheet/formula/lexer.go @@ -22,37 +22,37 @@ var _formula_actions []byte = []byte{ 1, 25, 1, 26, 1, 27, 1, 28, 1, 29, 1, 30, 1, 31, 1, 32, 1, 33, 1, 34, 1, 35, 1, 36, - 1, 37, 1, 38, 2, 0, 1, 2, - 3, 4, 2, 3, 5, 2, 3, 6, - 2, 3, 7, 2, 3, 8, 2, 3, - 9, + 1, 37, 1, 38, 1, 39, 2, 0, + 1, 2, 3, 4, 2, 3, 5, 2, + 3, 6, 2, 3, 7, 2, 3, 8, + 2, 3, 9, } var _formula_to_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 61, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, } var _formula_from_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, } -const formula_start int = 26 -const formula_first_final int = 26 +const formula_start int = 27 +const formula_first_final int = 27 const formula_error int = 0 -const formula_en_main int = 26 +const formula_en_main int = 27 // line 103 "lexer.rl" @@ -124,59 +124,59 @@ func (l *Lexer) lex(r io.Reader) { } switch cs { - case 26: + case 27: switch data[p] { case 34: goto tr1 case 35: - goto tr31 - case 36: goto tr32 + case 36: + goto tr33 case 37: goto tr4 case 38: - goto tr33 - case 39: goto tr34 - case 40: + case 39: goto tr35 - case 41: + case 40: goto tr36 - case 42: + case 41: goto tr37 - case 43: + case 42: goto tr38 - case 44: + case 43: goto tr39 - case 45: + case 44: goto tr40 - case 47: + case 45: goto tr41 + case 47: + goto tr42 case 58: - goto tr43 - case 60: goto tr44 - case 61: + case 60: goto tr45 - case 62: + case 61: goto tr46 + case 62: + goto tr47 case 70: - goto tr48 - case 84: goto tr49 - case 94: + case 84: goto tr50 - case 95: + case 94: goto tr51 - case 123: + case 95: goto tr52 - case 125: + case 123: goto tr53 + case 125: + goto tr54 } switch { case data[p] < 59: if 48 <= data[p] && data[p] <= 57 { - goto tr42 + goto tr43 } case data[p] > 63: switch { @@ -185,49 +185,49 @@ func (l *Lexer) lex(r io.Reader) { goto tr4 } case data[p] >= 65: - goto tr47 + goto tr48 } default: goto tr4 } - goto tr30 - case 27: + goto tr31 + case 28: switch data[p] { case 47: - goto tr54 + goto tr55 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 37: if 34 <= data[p] && data[p] <= 35 { - goto tr54 + goto tr55 } case data[p] > 45: switch { case data[p] > 63: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 58: - goto tr54 + goto tr55 } default: - goto tr54 + goto tr55 } - goto tr30 + goto tr31 case 1: if data[p] == 34 { goto tr2 } goto tr1 - case 28: + case 29: if data[p] == 34 { goto tr1 } - goto tr55 + goto tr56 case 2: switch data[p] { case 78: @@ -284,76 +284,76 @@ func (l *Lexer) lex(r io.Reader) { goto tr13 } goto tr4 - case 29: + case 30: switch data[p] { case 47: - goto tr54 + goto tr55 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 48: switch { case data[p] > 35: if 37 <= data[p] && data[p] <= 45 { - goto tr54 + goto tr55 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 57: switch { case data[p] < 65: if 58 <= data[p] && data[p] <= 63 { - goto tr54 + goto tr55 } case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } default: - goto tr57 + goto tr58 } default: - goto tr56 + goto tr57 } - goto tr30 - case 30: + goto tr31 + case 31: switch data[p] { case 47: - goto tr54 + goto tr55 case 58: - goto tr58 + goto tr59 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 48: switch { case data[p] > 35: if 37 <= data[p] && data[p] <= 45 { - goto tr54 + goto tr55 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 57: switch { case data[p] > 63: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 59: - goto tr54 + goto tr55 } default: - goto tr56 + goto tr57 } - goto tr30 + goto tr31 case 11: if data[p] == 36 { goto tr14 @@ -367,46 +367,46 @@ func (l *Lexer) lex(r io.Reader) { goto tr15 } goto tr0 - case 31: + case 32: if 48 <= data[p] && data[p] <= 57 { goto tr15 } - goto tr59 - case 32: + goto tr60 + case 33: switch data[p] { case 36: - goto tr60 + goto tr61 case 47: - goto tr54 + goto tr55 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 58: switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr62 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 63: switch { case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 65: - goto tr57 + goto tr58 } default: - goto tr54 + goto tr55 } - goto tr30 - case 33: + goto tr31 + case 34: switch data[p] { case 47: goto tr0 @@ -435,9 +435,9 @@ func (l *Lexer) lex(r io.Reader) { goto tr0 } default: - goto tr61 + goto tr62 } - goto tr30 + goto tr31 case 13: switch data[p] { case 39: @@ -477,77 +477,77 @@ func (l *Lexer) lex(r io.Reader) { goto tr18 } goto tr4 - case 34: + case 35: switch data[p] { case 46: - goto tr63 - case 58: - goto tr58 - case 101: goto tr64 + case 58: + goto tr59 + case 101: + goto tr65 case 123: - goto tr62 + goto tr63 case 125: - goto tr62 + goto tr63 } switch { case data[p] < 48: switch { case data[p] > 35: if 37 <= data[p] && data[p] <= 47 { - goto tr62 + goto tr63 } case data[p] >= 34: - goto tr62 + goto tr63 } case data[p] > 57: switch { case data[p] > 63: if 91 <= data[p] && data[p] <= 95 { - goto tr62 + goto tr63 } case data[p] >= 59: - goto tr62 + goto tr63 } default: - goto tr42 + goto tr43 } - goto tr30 - case 35: - switch data[p] { - case 47: - goto tr62 - case 101: - goto tr64 - case 123: - goto tr62 - case 125: - goto tr62 - } - switch { - case data[p] < 48: - switch { - case data[p] > 35: - if 37 <= data[p] && data[p] <= 45 { - goto tr62 - } - case data[p] >= 34: - goto tr62 - } - case data[p] > 57: - switch { - case data[p] > 63: - if 91 <= data[p] && data[p] <= 95 { - goto tr62 - } - case data[p] >= 58: - goto tr62 - } - default: - goto tr63 - } - goto tr30 + goto tr31 case 36: + switch data[p] { + case 47: + goto tr63 + case 101: + goto tr65 + case 123: + goto tr63 + case 125: + goto tr63 + } + switch { + case data[p] < 48: + switch { + case data[p] > 35: + if 37 <= data[p] && data[p] <= 45 { + goto tr63 + } + case data[p] >= 34: + goto tr63 + } + case data[p] > 57: + switch { + case data[p] > 63: + if 91 <= data[p] && data[p] <= 95 { + goto tr63 + } + case data[p] >= 58: + goto tr63 + } + default: + goto tr64 + } + goto tr31 + case 37: switch data[p] { case 47: goto tr0 @@ -576,28 +576,138 @@ func (l *Lexer) lex(r io.Reader) { goto tr0 } default: - goto tr65 + goto tr66 } - goto tr30 - case 37: + goto tr31 + case 38: switch data[p] { case 61: - goto tr67 - case 62: goto tr68 + case 62: + goto tr69 } - goto tr66 - case 38: - if data[p] == 61 { - goto tr70 - } - goto tr69 + goto tr67 case 39: + if data[p] == 61 { + goto tr71 + } + goto tr70 + case 40: switch data[p] { case 36: - goto tr60 + goto tr61 + case 47: + goto tr55 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 58: + switch { + case data[p] > 45: + if 48 <= data[p] && data[p] <= 57 { + goto tr72 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 63: + switch { + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + case data[p] >= 65: + goto tr73 + } + default: + goto tr55 + } + goto tr31 + case 41: + switch data[p] { case 40: - goto tr71 + goto tr75 + case 47: + goto tr74 + case 123: + goto tr74 + case 125: + goto tr74 + } + switch { + case data[p] < 48: + switch { + case data[p] > 35: + if 37 <= data[p] && data[p] <= 45 { + goto tr74 + } + case data[p] >= 34: + goto tr74 + } + case data[p] > 57: + switch { + case data[p] < 65: + if 58 <= data[p] && data[p] <= 63 { + goto tr74 + } + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr74 + } + default: + goto tr76 + } + default: + goto tr72 + } + goto tr31 + case 42: + switch data[p] { + case 40: + goto tr75 + case 47: + goto tr55 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 48: + switch { + case data[p] > 35: + if 37 <= data[p] && data[p] <= 45 { + goto tr55 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 57: + switch { + case data[p] < 65: + if 58 <= data[p] && data[p] <= 63 { + goto tr55 + } + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + default: + goto tr76 + } + default: + goto tr76 + } + goto tr31 + case 43: + switch data[p] { + case 36: + goto tr61 + case 40: + goto tr75 case 47: goto tr0 case 123: @@ -610,7 +720,7 @@ func (l *Lexer) lex(r io.Reader) { switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr72 } case data[p] >= 34: goto tr0 @@ -622,318 +732,314 @@ func (l *Lexer) lex(r io.Reader) { goto tr0 } case data[p] >= 65: - goto tr47 + goto tr73 } default: goto tr0 } - goto tr30 - case 40: - switch data[p] { - case 36: - goto tr60 - case 40: - goto tr71 - case 47: - goto tr54 - case 65: - goto tr72 - case 123: - goto tr54 - case 125: - goto tr54 - } - switch { - case data[p] < 58: - switch { - case data[p] > 45: - if 48 <= data[p] && data[p] <= 57 { - goto tr61 - } - case data[p] >= 34: - goto tr54 - } - case data[p] > 63: - switch { - case data[p] > 90: - if 91 <= data[p] && data[p] <= 95 { - goto tr54 - } - case data[p] >= 66: - goto tr47 - } - default: - goto tr54 - } - goto tr30 - case 41: - switch data[p] { - case 36: - goto tr60 - case 40: - goto tr71 - case 47: - goto tr54 - case 76: - goto tr73 - case 123: - goto tr54 - case 125: - goto tr54 - } - switch { - case data[p] < 58: - switch { - case data[p] > 45: - if 48 <= data[p] && data[p] <= 57 { - goto tr61 - } - case data[p] >= 34: - goto tr54 - } - case data[p] > 63: - switch { - case data[p] > 90: - if 91 <= data[p] && data[p] <= 95 { - goto tr54 - } - case data[p] >= 65: - goto tr47 - } - default: - goto tr54 - } - goto tr30 - case 42: - switch data[p] { - case 36: - goto tr60 - case 40: - goto tr71 - case 47: - goto tr54 - case 83: - goto tr74 - case 123: - goto tr54 - case 125: - goto tr54 - } - switch { - case data[p] < 58: - switch { - case data[p] > 45: - if 48 <= data[p] && data[p] <= 57 { - goto tr61 - } - case data[p] >= 34: - goto tr54 - } - case data[p] > 63: - switch { - case data[p] > 90: - if 91 <= data[p] && data[p] <= 95 { - goto tr54 - } - case data[p] >= 65: - goto tr47 - } - default: - goto tr54 - } - goto tr30 - case 43: - switch data[p] { - case 36: - goto tr60 - case 40: - goto tr71 - case 47: - goto tr54 - case 69: - goto tr75 - case 123: - goto tr54 - case 125: - goto tr54 - } - switch { - case data[p] < 58: - switch { - case data[p] > 45: - if 48 <= data[p] && data[p] <= 57 { - goto tr61 - } - case data[p] >= 34: - goto tr54 - } - case data[p] > 63: - switch { - case data[p] > 90: - if 91 <= data[p] && data[p] <= 95 { - goto tr54 - } - case data[p] >= 65: - goto tr47 - } - default: - goto tr54 - } - goto tr30 + goto tr31 case 44: switch data[p] { case 36: - goto tr60 - case 40: - goto tr71 + goto tr61 case 47: - goto tr54 - case 79: - goto tr76 - case 82: + goto tr55 + case 65: goto tr77 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 58: switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr72 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 63: switch { case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } - case data[p] >= 65: - goto tr47 + case data[p] >= 66: + goto tr73 } default: - goto tr54 + goto tr55 } - goto tr30 + goto tr31 case 45: switch data[p] { case 36: - goto tr60 + goto tr61 case 40: - goto tr71 + goto tr75 case 47: - goto tr54 - case 68: + goto tr55 + case 76: goto tr78 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 58: switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr72 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 63: switch { case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 65: - goto tr47 + goto tr73 } default: - goto tr54 + goto tr55 } - goto tr30 + goto tr31 case 46: switch data[p] { case 36: - goto tr60 + goto tr61 case 40: - goto tr71 + goto tr75 case 47: - goto tr54 - case 79: + goto tr55 + case 83: goto tr79 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 58: switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr72 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 63: switch { case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 65: - goto tr47 + goto tr73 } default: - goto tr54 + goto tr55 } - goto tr30 + goto tr31 case 47: switch data[p] { case 36: - goto tr60 + goto tr61 case 40: - goto tr71 + goto tr75 case 47: - goto tr54 - case 85: - goto tr74 + goto tr55 + case 69: + goto tr80 case 123: - goto tr54 + goto tr55 case 125: - goto tr54 + goto tr55 } switch { case data[p] < 58: switch { case data[p] > 45: if 48 <= data[p] && data[p] <= 57 { - goto tr61 + goto tr72 } case data[p] >= 34: - goto tr54 + goto tr55 } case data[p] > 63: switch { case data[p] > 90: if 91 <= data[p] && data[p] <= 95 { - goto tr54 + goto tr55 } case data[p] >= 65: - goto tr47 + goto tr73 } default: - goto tr54 + goto tr55 } - goto tr30 + goto tr31 + case 48: + switch data[p] { + case 36: + goto tr61 + case 47: + goto tr55 + case 79: + goto tr81 + case 82: + goto tr82 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 58: + switch { + case data[p] > 45: + if 48 <= data[p] && data[p] <= 57 { + goto tr72 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 63: + switch { + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + case data[p] >= 65: + goto tr73 + } + default: + goto tr55 + } + goto tr31 + case 49: + switch data[p] { + case 36: + goto tr61 + case 40: + goto tr75 + case 47: + goto tr55 + case 68: + goto tr83 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 58: + switch { + case data[p] > 45: + if 48 <= data[p] && data[p] <= 57 { + goto tr72 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 63: + switch { + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + case data[p] >= 65: + goto tr73 + } + default: + goto tr55 + } + goto tr31 + case 50: + switch data[p] { + case 36: + goto tr61 + case 40: + goto tr75 + case 47: + goto tr55 + case 79: + goto tr84 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 58: + switch { + case data[p] > 45: + if 48 <= data[p] && data[p] <= 57 { + goto tr72 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 63: + switch { + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + case data[p] >= 65: + goto tr73 + } + default: + goto tr55 + } + goto tr31 + case 51: + switch data[p] { + case 36: + goto tr61 + case 40: + goto tr75 + case 47: + goto tr55 + case 85: + goto tr79 + case 123: + goto tr55 + case 125: + goto tr55 + } + switch { + case data[p] < 58: + switch { + case data[p] > 45: + if 48 <= data[p] && data[p] <= 57 { + goto tr72 + } + case data[p] >= 34: + goto tr55 + } + case data[p] > 63: + switch { + case data[p] > 90: + if 91 <= data[p] && data[p] <= 95 { + goto tr55 + } + case data[p] >= 65: + goto tr73 + } + default: + goto tr55 + } + goto tr31 case 16: if data[p] == 120 { goto tr19 @@ -971,42 +1077,60 @@ func (l *Lexer) lex(r io.Reader) { } goto tr4 case 22: - switch data[p] { - case 40: + if data[p] == 46 { goto tr26 - case 95: - goto tr25 } - if 65 <= data[p] && data[p] <= 90 { - goto tr25 + switch { + case data[p] > 57: + if 65 <= data[p] && data[p] <= 90 { + goto tr26 + } + case data[p] >= 48: + goto tr26 } goto tr4 case 23: - if data[p] == 109 { + switch data[p] { + case 40: goto tr27 + case 46: + goto tr26 + } + switch { + case data[p] > 57: + if 65 <= data[p] && data[p] <= 90 { + goto tr26 + } + case data[p] >= 48: + goto tr26 } goto tr4 case 24: - if data[p] == 46 { + if data[p] == 109 { goto tr28 } goto tr4 case 25: - if data[p] == 95 { - goto tr29 - } - if 65 <= data[p] && data[p] <= 90 { + if data[p] == 46 { goto tr29 } goto tr4 - case 48: + case 26: if data[p] == 95 { - goto tr29 + goto tr30 } if 65 <= data[p] && data[p] <= 90 { - goto tr29 + goto tr30 } - goto tr80 + goto tr4 + case 52: + if data[p] == 95 { + goto tr30 + } + if 65 <= data[p] && data[p] <= 90 { + goto tr30 + } + goto tr85 } tr4: @@ -1015,7 +1139,7 @@ func (l *Lexer) lex(r io.Reader) { tr1: cs = 1 goto _again - tr31: + tr32: cs = 2 goto _again tr3: @@ -1042,13 +1166,13 @@ func (l *Lexer) lex(r io.Reader) { tr12: cs = 10 goto _again - tr58: + tr59: cs = 11 goto _again tr14: cs = 12 goto _again - tr34: + tr35: cs = 13 goto _again tr16: @@ -1057,7 +1181,7 @@ func (l *Lexer) lex(r io.Reader) { tr17: cs = 15 goto _again - tr51: + tr52: cs = 16 goto _again tr19: @@ -1078,180 +1202,195 @@ func (l *Lexer) lex(r io.Reader) { tr25: cs = 22 goto _again - tr22: + tr26: cs = 23 goto _again - tr27: + tr22: cs = 24 goto _again tr28: cs = 25 goto _again - tr0: + tr29: cs = 26 + goto _again + tr0: + cs = 27 goto f0 tr8: - cs = 26 + cs = 27 goto f2 tr13: - cs = 26 + cs = 27 goto f3 tr18: - cs = 26 - goto f4 - tr26: - cs = 26 - goto f5 - tr33: - cs = 26 - goto f8 - tr35: - cs = 26 - goto f9 - tr36: - cs = 26 - goto f10 - tr37: - cs = 26 - goto f11 - tr38: - cs = 26 - goto f12 - tr39: - cs = 26 - goto f13 - tr40: - cs = 26 - goto f14 - tr41: - cs = 26 - goto f15 - tr43: - cs = 26 - goto f17 - tr45: - cs = 26 - goto f18 - tr50: - cs = 26 - goto f20 - tr52: - cs = 26 - goto f21 - tr53: - cs = 26 - goto f22 - tr54: - cs = 26 - goto f23 - tr55: - cs = 26 - goto f24 - tr59: - cs = 26 - goto f25 - tr62: - cs = 26 - goto f27 - tr66: - cs = 26 - goto f28 - tr67: - cs = 26 - goto f29 - tr68: - cs = 26 - goto f30 - tr69: - cs = 26 - goto f31 - tr70: - cs = 26 - goto f32 - tr71: - cs = 26 - goto f33 - tr80: - cs = 26 - goto f36 - tr30: cs = 27 + goto f4 + tr27: + cs = 27 + goto f5 + tr34: + cs = 27 + goto f8 + tr36: + cs = 27 + goto f9 + tr37: + cs = 27 + goto f10 + tr38: + cs = 27 + goto f11 + tr39: + cs = 27 + goto f12 + tr40: + cs = 27 + goto f13 + tr41: + cs = 27 + goto f14 + tr42: + cs = 27 + goto f15 + tr44: + cs = 27 + goto f17 + tr46: + cs = 27 + goto f18 + tr51: + cs = 27 + goto f19 + tr53: + cs = 27 + goto f20 + tr54: + cs = 27 + goto f21 + tr55: + cs = 27 + goto f22 + tr56: + cs = 27 + goto f23 + tr60: + cs = 27 + goto f25 + tr63: + cs = 27 + goto f27 + tr67: + cs = 27 + goto f28 + tr68: + cs = 27 + goto f29 + tr69: + cs = 27 + goto f30 + tr70: + cs = 27 + goto f31 + tr71: + cs = 27 + goto f32 + tr74: + cs = 27 + goto f33 + tr75: + cs = 27 + goto f34 + tr85: + cs = 27 + goto f37 + tr31: + cs = 28 goto _again tr2: - cs = 28 - goto f1 - tr32: cs = 29 - goto _again - tr56: + goto f1 + tr33: cs = 30 - goto f19 - tr15: - cs = 31 goto _again tr57: + cs = 31 + goto f24 + tr15: cs = 32 goto _again - tr60: + tr58: cs = 33 - goto f19 - tr61: - cs = 33 - goto f26 - tr42: - cs = 34 - goto f16 - tr63: - cs = 35 goto _again - tr65: - cs = 36 + tr61: + cs = 34 + goto f24 + tr62: + cs = 34 + goto f26 + tr43: + cs = 35 goto f16 tr64: cs = 36 - goto f19 - tr44: - cs = 37 goto _again - tr46: + tr66: + cs = 37 + goto f16 + tr65: + cs = 37 + goto f24 + tr45: cs = 38 goto _again tr47: cs = 39 - goto f19 - tr75: - cs = 39 - goto f34 - tr79: - cs = 39 - goto f35 + goto _again tr48: cs = 40 goto _again tr72: cs = 41 goto _again - tr73: + tr76: cs = 42 goto _again - tr74: + tr73: cs = 43 - goto _again + goto f24 + tr80: + cs = 43 + goto f35 + tr84: + cs = 43 + goto f36 tr49: cs = 44 goto _again - tr76: + tr77: cs = 45 goto _again tr78: cs = 46 goto _again - tr77: + tr79: cs = 47 goto _again - tr29: + tr50: cs = 48 goto _again + tr81: + cs = 49 + goto _again + tr83: + cs = 50 + goto _again + tr82: + cs = 51 + goto _again + tr30: + cs = 52 + goto _again f2: _acts = 3 @@ -1262,7 +1401,7 @@ func (l *Lexer) lex(r io.Reader) { f4: _acts = 7 goto execFuncs - f33: + f34: _acts = 9 goto execFuncs f5: @@ -1271,10 +1410,10 @@ func (l *Lexer) lex(r io.Reader) { f8: _acts = 13 goto execFuncs - f21: + f20: _acts = 15 goto execFuncs - f22: + f21: _acts = 17 goto execFuncs f9: @@ -1295,7 +1434,7 @@ func (l *Lexer) lex(r io.Reader) { f15: _acts = 29 goto execFuncs - f20: + f19: _acts = 31 goto execFuncs f18: @@ -1319,44 +1458,47 @@ func (l *Lexer) lex(r io.Reader) { f27: _acts = 45 goto execFuncs - f25: + f33: _acts = 47 goto execFuncs - f23: + f25: _acts = 49 goto execFuncs - f36: + f22: _acts = 51 goto execFuncs - f24: + f37: _acts = 53 goto execFuncs - f28: + f23: _acts = 55 goto execFuncs - f31: + f28: _acts = 57 goto execFuncs - f0: + f31: _acts = 59 goto execFuncs - f34: - _acts = 64 - goto execFuncs - f16: - _acts = 67 - goto execFuncs - f26: - _acts = 70 + f0: + _acts = 61 goto execFuncs f35: - _acts = 73 + _acts = 66 goto execFuncs - f19: - _acts = 76 + f16: + _acts = 69 + goto execFuncs + f26: + _acts = 72 + goto execFuncs + f36: + _acts = 75 + goto execFuncs + f24: + _acts = 78 goto execFuncs f1: - _acts = 79 + _acts = 81 goto execFuncs execFuncs: @@ -1550,6 +1692,14 @@ func (l *Lexer) lex(r io.Reader) { l.emit(tokenNumber, data[ts:te]) } case 32: + // line 56 "lexer.rl" + + te = p + p-- + { + l.emit(tokenCell, data[ts:te]) + } + case 33: // line 60 "lexer.rl" te = p @@ -1557,7 +1707,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenHorizontalRange, data[ts:te]) } - case 33: + case 34: // line 61 "lexer.rl" te = p @@ -1565,7 +1715,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenSheet, data[ts:te]) } - case 34: + case 35: // line 62 "lexer.rl" te = p @@ -1573,7 +1723,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenReservedName, data[ts:te]) } - case 35: + case 36: // line 69 "lexer.rl" te = p @@ -1581,7 +1731,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenString, data[ts+1:te-1]) } - case 36: + case 37: // line 82 "lexer.rl" te = p @@ -1589,7 +1739,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenLT, data[ts:te]) } - case 37: + case 38: // line 83 "lexer.rl" te = p @@ -1597,7 +1747,7 @@ func (l *Lexer) lex(r io.Reader) { { l.emit(tokenGT, data[ts:te]) } - case 38: + case 39: // line 1 "NONE" switch act { @@ -1638,7 +1788,7 @@ func (l *Lexer) lex(r io.Reader) { } } - // line 1344 "lexer.go" + // line 1480 "lexer.go" } } goto _again @@ -1660,7 +1810,7 @@ func (l *Lexer) lex(r io.Reader) { act = 0 - // line 1365 "lexer.go" + // line 1501 "lexer.go" } } @@ -1675,56 +1825,62 @@ func (l *Lexer) lex(r io.Reader) { } if p == eof { switch cs { - case 27: - goto tr54 - case 1: - goto tr0 case 28: goto tr55 + case 1: + goto tr0 case 29: - goto tr54 + goto tr56 case 30: - goto tr54 + goto tr55 + case 31: + goto tr55 case 11: goto tr0 case 12: goto tr0 - case 31: - goto tr59 case 32: - goto tr54 + goto tr60 case 33: - goto tr0 + goto tr55 case 34: - goto tr62 + goto tr0 case 35: - goto tr62 + goto tr63 case 36: - goto tr0 + goto tr63 case 37: - goto tr66 - case 38: - goto tr69 - case 39: goto tr0 + case 38: + goto tr67 + case 39: + goto tr70 case 40: - goto tr54 + goto tr55 case 41: - goto tr54 + goto tr74 case 42: - goto tr54 + goto tr55 case 43: - goto tr54 + goto tr0 case 44: - goto tr54 + goto tr55 case 45: - goto tr54 + goto tr55 case 46: - goto tr54 + goto tr55 case 47: - goto tr54 + goto tr55 case 48: - goto tr80 + goto tr55 + case 49: + goto tr55 + case 50: + goto tr55 + case 51: + goto tr55 + case 52: + goto tr85 } } diff --git a/spreadsheet/formula/lexer.rl b/spreadsheet/formula/lexer.rl index febf518b..0fd02779 100644 --- a/spreadsheet/formula/lexer.rl +++ b/spreadsheet/formula/lexer.rl @@ -34,8 +34,8 @@ import ( horizontalRange = '$'? [0-9]+ ':' '$'? [0-9]+; # there is a function list at https://msdn.microsoft.com/en-us/library/dd906358(v=office.12).aspx - builtinFunction = [A-Z]+ '('; - excelFn = '_xlfn.' [A-Z_]+ '('; + builtinFunction = [A-Z] [A-Z0-9]+ '('; + excelFn = '_xlfn.' [A-Z_] [A-Z0-9.]+ '('; sheetChar = ^['%\[\]\\:/?();{}#"=<>&+\-*/^%,_]; enclosedSheetChar = ^['*\[\]\\:\/?]; diff --git a/spreadsheet/formula/parser.go b/spreadsheet/formula/parser.go index 5fd16b43..90ab91af 100644 --- a/spreadsheet/formula/parser.go +++ b/spreadsheet/formula/parser.go @@ -20,7 +20,7 @@ type plex struct { } func (f *plex) Lex(lval *yySymType) int { - // yyDebug = 3 + //yyDebug = 3 n := <-f.nodes if n != nil { lval.node = n @@ -40,5 +40,8 @@ func Parse(r io.Reader) Expression { } func ParseString(s string) Expression { + if s == "" { + return NewEmptyExpr() + } return Parse(strings.NewReader(s)) } diff --git a/spreadsheet/formula/result.go b/spreadsheet/formula/result.go index c6b00457..83613198 100644 --- a/spreadsheet/formula/result.go +++ b/spreadsheet/formula/result.go @@ -7,7 +7,10 @@ package formula -import "fmt" +import ( + "fmt" + "strconv" +) // ResultType is the type of the result //go:generate stringer -type=ResultType @@ -20,6 +23,7 @@ const ( ResultTypeString ResultTypeList ResultTypeError + ResultTypeEmpty ) // Result is the result of a formula or cell evaluation . @@ -45,6 +49,29 @@ func (r Result) Value() string { } } +// AsNumber attempts to intepret a string cell value as a number. Upon success, +// it returns a new number result, upon failure it returns the original result. +// This is used as functions return strings that can then act like number (e.g. +// LEFT(1.2345,3) + LEFT(1.2345,3) = 2.4) +func (r Result) AsNumber() Result { + if r.Type == ResultTypeString { + f, err := strconv.ParseFloat(r.ValueString, 64) + if err == nil { + return MakeNumberResult(f) + } + } + return r +} + +func (r Result) AsString() Result { + switch r.Type { + case ResultTypeNumber: + return MakeStringResult(r.Value()) + default: + return r + } +} + // MakeNumberResult constructs a number result. func MakeNumberResult(v float64) Result { return Result{Type: ResultTypeNumber, ValueNumber: v} @@ -106,3 +133,8 @@ func MakeErrorResultType(t ErrorType, msg string) Result { func MakeStringResult(s string) Result { return Result{Type: ResultTypeString, ValueString: s} } + +// MakeEmptyResult is ued when parsing an empty argument. +func MakeEmptyResult() Result { + return Result{Type: ResultTypeEmpty} +} diff --git a/spreadsheet/formula/resulttype_string.go b/spreadsheet/formula/resulttype_string.go index ea4453d9..0b232f6e 100644 --- a/spreadsheet/formula/resulttype_string.go +++ b/spreadsheet/formula/resulttype_string.go @@ -4,9 +4,9 @@ package formula import "fmt" -const _ResultType_name = "ResultTypeUnknownResultTypeNumberResultTypeStringResultTypeListResultTypeError" +const _ResultType_name = "ResultTypeUnknownResultTypeNumberResultTypeStringResultTypeListResultTypeErrorResultTypeEmpty" -var _ResultType_index = [...]uint8{0, 17, 33, 49, 63, 78} +var _ResultType_index = [...]uint8{0, 17, 33, 49, 63, 78, 93} func (i ResultType) String() string { if i >= ResultType(len(_ResultType_index)-1) { diff --git a/spreadsheet/formula/testdata/formulareference.xlsx b/spreadsheet/formula/testdata/formulareference.xlsx index 429e7f9c97fa16ffd656df2874e75e3fe3510abd..d9df37159da95e2d4ad1154da6b306ef1ad4bafe 100644 GIT binary patch delta 9837 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 delta 7407 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 P{ex_-usl(y|fiEMQt7Tz00l-0RS% zmiCMh@0=HYAOw?P9Me$y#X&c*Lh%XVPX_cT{dWSVp{4^8$782CFb7VA z`-d#FQpi9R3FR7x8gET>OL4nrGZcvApTJiOw(EVLPQ8R*Kx?HkCw|b4*>68=M;@*UQ6>55GO$dBjdEVMUpZiHyk}w?+0+1NFVj@3q}dxv&R({q zayWSS@x3r6r*{78_p89-(K;a_szoe}y*n1u1ks0Spc^aq8ofqZYVu>2wW|W>q1jW@ z!mCa`q;?zjmo11s7PZk&rjLgnST4sWgNeNO>>FgG@5HpUkGV$TxB!9K<^xwSE6)G} z`1QLX-&c-J2SQi^H^#Jm$4$%MHHIqfrPjRaa2((Mta^4z2V8#I|M4C}yKH9q?~Xnl z!hd@&?03%SPZXDjzNtA`d@n{PC^KoRaArlyfin&mmKZJb9?0eW%!)T4G%LZniSt&b zR<>-&ZUrGi`z796i-yQdCA%+oZ%1wl3P`+58Uomw#fMJb4II$kze$LRnsLV-dmS^| zMaC{7k3yhj)wLPG3X>lKU09;dM1OAnq(c6Vr1jC#P{b~)W0hv#FgmR}Y{*bC4K|Q# z238VXLu@WjbMCRLxcR_;Op#nMuKA9ELLhlDO~U6sJN4tel0b<%@-`*aGm{1IS>nVu z_8sjY#;2!$nqBg-bqKt_H+B{N&&I;Q;)4EPssqE(^CADy4gSblU~zgrv_BXBudRTC z>AA@N%=|4l5dE*j@PEfW@GL#!KbXHe%)cw>zs5^2DFfL*n7{jOXn!GozZh7DfrR4E zv;I?ugFpoT!Z@RVe=yMf1NuAE{|)+r4W?rJU(nx-!u{7uc0^!HMyh{S`g?b9{sodF W0Yex`(NRDsppf6a!Y