Simplified calculator 2 example.

This commit is contained in:
Maurice Makaay 2019-05-28 23:51:19 +00:00
parent 11883b06ac
commit e1534f678e
3 changed files with 22 additions and 38 deletions

View File

@ -98,11 +98,11 @@ func (c *calculator) expr(p *parsekit.ParseAPI) {
var pc, a = parsekit.C, parsekit.A var pc, a = parsekit.C, parsekit.A
if p.Handle(c.term) { if p.Handle(c.term) {
for p.On(pc.Any(a.Add, a.Subtract)).Skip() { for p.On(pc.Any(a.Add, a.Subtract)).Skip() {
c.interpreter.pushOperator(p.LastMatch) op := p.LastMatch
if !p.Handle(c.term) { if !p.Handle(c.term) {
return return
} }
c.interpreter.eval() c.interpreter.eval(op)
} }
} }
@ -116,11 +116,11 @@ func (c *calculator) term(p *parsekit.ParseAPI) {
var pc, a = parsekit.C, parsekit.A var pc, a = parsekit.C, parsekit.A
if p.Handle(c.factor) { if p.Handle(c.factor) {
for p.On(pc.Any(a.Multiply, a.Divide)).Skip() { for p.On(pc.Any(a.Multiply, a.Divide)).Skip() {
c.interpreter.pushOperator(p.LastMatch) op := p.LastMatch
if !p.Handle(c.factor) { if !p.Handle(c.factor) {
return return
} }
c.interpreter.eval() c.interpreter.eval(op)
} }
} }
@ -166,7 +166,6 @@ func (c *calculator) factor(p *parsekit.ParseAPI) {
type stackFrame struct { type stackFrame struct {
a float64 a float64
b float64 b float64
op func(a, b float64) float64
} }
type interpreter struct { type interpreter struct {
@ -175,51 +174,38 @@ type interpreter struct {
result float64 result float64
} }
func (i *interpreter) push() *stackFrame { func (i *interpreter) push() {
f := &stackFrame{} f := &stackFrame{}
i.stack = append(i.stack, f) i.top, i.stack = f, append(i.stack, f)
i.top = f
i.pushOperator("VAL")
return f
} }
func (i *interpreter) pop() float64 { func (i *interpreter) pop() {
value := i.eval() popped := i.top
i.stack = i.stack[0 : len(i.stack)-1] i.stack = i.stack[0 : len(i.stack)-1]
if len(i.stack) > 0 { if len(i.stack) > 0 {
i.top = i.stack[len(i.stack)-1] i.top = i.stack[len(i.stack)-1]
i.pushValue(value) i.pushValue(popped.b)
} else { } else {
i.result = i.top.b i.result = popped.b
i.top = nil
} }
return value
} }
func (i *interpreter) pushValue(value float64) { func (i *interpreter) pushValue(value float64) {
i.top.a, i.top.b = i.top.b, value i.top.a, i.top.b = i.top.b, value
} }
func (i *interpreter) pushOperator(op string) { func (i *interpreter) eval(op string) float64 {
value := i.top.a
switch op { switch op {
case "VAL":
i.top.op = func(a, b float64) float64 { return b }
case "+": case "+":
i.top.op = func(a, b float64) float64 { return a + b } value += i.top.b
case "-": case "-":
i.top.op = func(a, b float64) float64 { return a - b } value -= i.top.b
case "*": case "*":
i.top.op = func(a, b float64) float64 { return a * b } value *= i.top.b
case "/": case "/":
i.top.op = func(a, b float64) float64 { return a / b } value /= i.top.b
default:
panic(fmt.Sprintf("Unhandled op name: %s", op))
} }
} i.top.b = value
func (i *interpreter) eval() float64 {
value := i.top.op(i.top.a, i.top.b)
i.pushValue(value)
i.pushOperator("VAL")
return value return value
} }

View File

@ -68,7 +68,7 @@ func (p *ParseAPI) checkForLoops() {
id := fmt.Sprintf("%s:%d", file, line) id := fmt.Sprintf("%s:%d", file, line)
if _, ok := p.loopCheck[id]; ok { if _, ok := p.loopCheck[id]; ok {
caller := runtime.FuncForPC(pc) caller := runtime.FuncForPC(pc)
panic(fmt.Sprintf("Loop detected in parser in %s at %s, line %d", caller.Name(), file, line)) panic(fmt.Sprintf("Loop detected in parser in %s at %s", caller.Name(), id))
} }
p.loopCheck[id] = true p.loopCheck[id] = true
} }

View File

@ -1,7 +1,6 @@
package parsekit_test package parsekit_test
import ( import (
"fmt"
"testing" "testing"
"git.makaay.nl/mauricem/go-parsekit" "git.makaay.nl/mauricem/go-parsekit"
@ -105,7 +104,7 @@ func TestGivenLoopingParserDefinition_ParserPanics(t *testing.T) {
func() { parser.Execute("Het houdt niet op, niet vanzelf") }, func() { parser.Execute("Het houdt niet op, niet vanzelf") },
"Loop detected in parser in git.makaay.nl/mauricem/go-parsekit_test." + "Loop detected in parser in git.makaay.nl/mauricem/go-parsekit_test." +
"(*parserWithLoop).second at /home/ubuntu/Projects/Parsekit/go-parsekit" + "(*parserWithLoop).second at /home/ubuntu/Projects/Parsekit/go-parsekit" +
"/parsehandler_test.go, line 87"}) "/parsehandler_test.go:87"})
} }
// This test incorporates an actual loop bug that I dropped on myself and // This test incorporates an actual loop bug that I dropped on myself and
@ -124,7 +123,6 @@ func TestGivenLoopingParserDefinition_ParserPanics(t *testing.T) {
func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) { func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) {
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) { parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
for p.On(c.Max(5, a.AnyRune)).Accept() { for p.On(c.Max(5, a.AnyRune)).Accept() {
fmt.Printf("Cycle: %s\n", p.BufLiteral())
p.BufClear() p.BufClear()
} }
p.Stop() p.Stop()
@ -133,5 +131,5 @@ func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) {
func() { parser.Execute("This will end soon") }, func() { parser.Execute("This will end soon") },
"Loop detected in parser in git.makaay.nl/mauricem/go-parsekit_test." + "Loop detected in parser in git.makaay.nl/mauricem/go-parsekit_test." +
"TestGivenLoopingParserDefinition2_ParserPanics.func1 at " + "TestGivenLoopingParserDefinition2_ParserPanics.func1 at " +
"/home/ubuntu/Projects/Parsekit/go-parsekit/parsehandler_test.go, line 126"}) "/home/ubuntu/Projects/Parsekit/go-parsekit/parsehandler_test.go:125"})
} }