go-parsekit/example_basiccalculator1_te...

112 lines
3.5 KiB
Go

// Let's write a parser for a really basic calculator.
// The calculator understands input that looks like:
//
// 10 + 20 - 8+4
//
// So positive numbers that can be either added or substracted, and whitespace
// is ignored.
package parsekit_test
import (
"fmt"
"strconv"
"git.makaay.nl/mauricem/go-parsekit"
)
func Example_basicCalculator1() {
for _, c := range []struct {
input string
expected int64
}{
{"0", 0},
{"1", 1},
{"1+2+3", 6},
{" 10 + \t20 - 3 + 7 -10 ", 24},
{"", 0},
{" \t ", 0},
{"+", 0},
{"10.8 + 12", 0},
{"42+ ", 0},
{"9999999999999999999 + 8888888", 0},
} {
output, err := ComputeSimple(c.input)
if err != nil {
fmt.Printf("Input: %q, got error: %s\n", c.input, err)
} else {
fmt.Printf("Input: %q, got outcome: %d, correct = %t\n", c.input, output, output == c.expected)
}
}
// Output:
// Input: "0", got outcome: 0, correct = true
// Input: "1", got outcome: 1, correct = true
// Input: "1+2+3", got outcome: 6, correct = true
// Input: " 10 + \t20 - 3 + 7 -10 ", got outcome: 24, correct = true
// Input: "", got error: unexpected end of file (expected integer number)
// Input: " \t ", got error: unexpected character ' ' (expected integer number)
// Input: "+", got error: unexpected character '+' (expected integer number)
// Input: "10.8 + 12", got error: unexpected character '.' (expected operator, '+' or '-')
// Input: "42+ ", got error: unexpected character ' ' (expected integer number)
// Input: "9999999999999999999 + 8888888", got error: invalid value: strconv.ParseInt: parsing "9999999999999999999": value out of range
}
// ---------------------------------------------------------------------------
// Implementation of the parser
// ---------------------------------------------------------------------------
// CalculateSimple interprets a simple calculation, consisting of only integers
// and add or subtract operators. It returns the result of the calculation.
// An error is returned in case the calculation failed.
func ComputeSimple(calculation string) (int64, *parsekit.Error) {
calculator := &simpleCalculator{op: +1}
parser := parsekit.NewParser(calculator.number)
err := parser.Execute(calculation)
return calculator.Result, err
}
// simpleCalculator defines the parsing state machine. We do this using methods
// on a struct, so the parser can make use of state data inside that struct
// during the parsing.
type simpleCalculator struct {
Result int64 // holds the resulting outcome of the computation
op int64 // represents operation for next term (+1 = add, -1 = subtract)
}
func (c *simpleCalculator) number(p *parsekit.ParseAPI) {
// A definition of integer, which conveniently drops surrounding whitespace.
pc, a, m := parsekit.C, parsekit.A, parsekit.M
whitespace := m.Drop(pc.Opt(a.Whitespace))
integer := pc.Seq(whitespace, a.Integer, whitespace)
if p.On(integer).Accept() {
value, err := strconv.ParseInt(p.BufLiteral(), 10, 64)
p.BufClear()
if err != nil {
p.EmitError("invalid value: %s", err)
} else {
c.Result += c.op * value
p.Handle(c.operatorOrEndOfFile)
}
} else {
p.Expects("integer number")
p.UnexpectedInput()
}
}
func (c *simpleCalculator) operatorOrEndOfFile(p *parsekit.ParseAPI) {
var a = parsekit.A
switch {
case p.On(a.Add).Skip():
c.op = +1
p.Handle(c.number)
case p.On(a.Subtract).Skip():
c.op = -1
p.Handle(c.number)
case !p.On(a.EndOfFile).Stay():
p.Expects("operator, '+' or '-'")
p.UnexpectedInput()
default:
p.ExpectEndOfFile()
}
}