101 lines
3.2 KiB
Go
101 lines
3.2 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 examples
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"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},
|
|
} {
|
|
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)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Implementation of the parser
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ComputeSimple 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)
|
|
}
|
|
|
|
// A definition of an int64, which conveniently drops surrounding whitespace.
|
|
var dropWhitespace = parsekit.M.Drop(parsekit.C.Opt(parsekit.A.Whitespace))
|
|
var bareInteger = parsekit.C.Seq(dropWhitespace, parsekit.A.Integer, dropWhitespace)
|
|
var int64Token = parsekit.T.Int64(nil, bareInteger)
|
|
|
|
func (c *simpleCalculator) number(p *parsekit.ParseAPI) {
|
|
p.Expects("integer number")
|
|
if p.On(int64Token).Accept() {
|
|
c.Result += c.op * p.Result().Value(0).(int64)
|
|
p.Handle(c.operatorOrEndOfFile)
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|