Skip to content

Vovan-VE/go-lr0-parser

Repository files navigation

LR(0) Parser

GitHub go.mod Go version GitHub release License

This package contains LR(0) parser to parse text according to defined LR(0) grammar.

It's based on my previous PHP library, but with some package API enhancements.

Example

package main

import (
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/vovan-ve/go-lr0-parser"
)

const (
	tInt lr0.Id = iota + 1
	tPlus
	tMinus

	nVal
	nSum
	nGoal
)

var parser = lr0.New(
	[]lr0.Terminal{
		lr0.NewTerm(tInt, "int").FuncByte(isDigit, bytesToInt),
		lr0.NewTerm(tPlus, `"+"`).Hide().Str("+"),
		lr0.NewTerm(tMinus, `"-"`).Hide().Str("-"),
	},
	[]lr0.NonTerminalDefinition{
		lr0.NewNT(nGoal, "Goal").Main().Is(nSum),
		lr0.NewNT(nSum, "Sum").
			Is(nSum, tPlus, nVal).Do(func(a, b int) int { return a + b }).
			Is(nSum, tMinus, nVal).Do(func(a, b int) int { return a - b }).
			Is(nVal),
		lr0.NewNT(nVal, "Val").Is(tInt),
	},
)

func main() {
	if len(os.Args) <= 1 {
		log.Println("no args to calc")
		return
	}
	for i, input := range os.Args[1:] {
		fmt.Printf("%d> %s\t=> ", i, input)
		result, err := parser.Parse(lr0.NewState([]byte(input)))
		if err != nil {
			fmt.Println("Error:", err)
		} else {
			fmt.Println(result)
		}
	}
}


func isDigit(b byte) bool              { return b >= '0' && b <= '9' }
func bytesToInt(b []byte) (int, error) { return strconv.Atoi(string(b)) }
$ go build -o calc examples/01-calc-tiny/main.go
...
$ ./calc "3+8-5" "3+ 8-5" "3+8*5"
0> 3+8-5        => 6
1> 3+ 8-5       => Error: unexpected input: expected int: parse error near ⟪3+⟫⏵⟪␠8-5⟫
2> 3+8*5        => Error: unexpected input: expected "+" or "-": parse error near ⟪3+8⟫⏵⟪*5⟫

See examples in examples/ and tests.

Theory

LR parser.

License

MIT