Skip to content

Commit

Permalink
Add BNF support.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed Oct 11, 2024
1 parent 139663f commit 323775c
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 46 deletions.
5 changes: 3 additions & 2 deletions abnf/abnf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package abnf_test
import (
_ "embed"
"fmt"
"testing"

"github.com/0x51-dev/upeg/abnf"
"github.com/0x51-dev/upeg/abnf/ir"
"github.com/0x51-dev/upeg/ir"
"github.com/0x51-dev/upeg/parser"
"github.com/0x51-dev/upeg/parser/op"
"testing"
)

var (
Expand Down
5 changes: 3 additions & 2 deletions abnf/gen/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"bytes"
"embed"
"fmt"
"github.com/0x51-dev/upeg/abnf"
"github.com/0x51-dev/upeg/abnf/ir"
"io"
"io/fs"
"log"
"sort"
"strings"
"text/template"
"unicode"

"github.com/0x51-dev/upeg/abnf"
"github.com/0x51-dev/upeg/ir"
)

const (
Expand Down
1 change: 1 addition & 0 deletions abnf/gen/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gen_test

import (
"fmt"

"github.com/0x51-dev/upeg/abnf/gen"
)

Expand Down
3 changes: 3 additions & 0 deletions bnf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package upeg

//go:generate go run github.com/0x51-dev/upeg/cmd/abnf --in=bnf/definition.abnf --out=bnf/definition.go --ignore=defined-as,elements,c-wsp,c-nl,element,group,rulename-br,literal-double,literal-single --importCore --package=bnf
20 changes: 20 additions & 0 deletions bnf/definition.abnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; BNF
rulelist = 1*( rule / (*WSP c-nl) )
rule = rulename-br defined-as elements c-nl
rulename-br = "<" rulename ">"
rulename = ALPHA *(ALPHA / DIGIT / "-")
defined-as = *c-wsp ("::=") *c-wsp
elements = alternation *WSP
c-wsp = WSP / (c-nl WSP)
c-nl = comment / CRLF
comment = ";" *(WSP / VCHAR) CRLF
alternation = concatenation *(*c-wsp "|" *c-wsp concatenation)
concatenation = repetition *(1*c-wsp repetition)
repetition = element [repeat]
repeat = "*" / "+"
element = rulename-br / group / option / char-val
group = "(" *c-wsp alternation *c-wsp ")"
option = "[" *c-wsp alternation *c-wsp "]"
char-val = literal-double / literal-single
literal-double = %x22 *(%x20-21 / %x23-7E) %x22
literal-single = %x27 *(%x20-26 / %x28-7E) %x27
39 changes: 39 additions & 0 deletions bnf/definition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package bnf is autogenerated by https://github.com/0x51-dev/upeg. DO NOT EDIT.
package bnf

import (
. "github.com/0x51-dev/upeg/abnf/core"
"github.com/0x51-dev/upeg/parser"
"github.com/0x51-dev/upeg/parser/op"
)

var (
Rulelist = op.Capture{Name: "Rulelist", Value: op.OneOrMore{Value: op.Or{Rule, op.And{op.ZeroOrMore{Value: WSP}, CNl}}}}
Rule = op.Capture{Name: "Rule", Value: op.And{RulenameBr, DefinedAs, Elements, CNl}}
RulenameBr = op.And{'<', Rulename, '>'}
Rulename = op.Capture{Name: "Rulename", Value: op.And{ALPHA, op.ZeroOrMore{Value: op.Or{ALPHA, DIGIT, '-'}}}}
DefinedAs = op.And{op.ZeroOrMore{Value: CWsp}, "::=", op.ZeroOrMore{Value: CWsp}}
Elements = op.And{Alternation, op.ZeroOrMore{Value: WSP}}
CWsp = op.Or{WSP, op.And{CNl, WSP}}
CNl = op.Or{Comment, CRLF}
Comment = op.Capture{Name: "Comment", Value: op.And{';', op.ZeroOrMore{Value: op.Or{WSP, VCHAR}}, CRLF}}
Alternation = op.Capture{Name: "Alternation", Value: op.And{Concatenation, op.ZeroOrMore{Value: op.And{op.ZeroOrMore{Value: CWsp}, '|', op.ZeroOrMore{Value: CWsp}, Concatenation}}}}
Concatenation = op.Capture{Name: "Concatenation", Value: op.And{Repetition, op.ZeroOrMore{Value: op.And{op.OneOrMore{Value: CWsp}, Repetition}}}}
Repetition = op.Capture{Name: "Repetition", Value: op.And{Element, op.Optional{Value: Repeat}}}
Repeat = op.Capture{Name: "Repeat", Value: op.Or{'*', '+'}}
Element = op.Or{RulenameBr, Group, Option, CharVal}
Group = op.And{'(', op.ZeroOrMore{Value: CWsp}, op.Reference{Name: "Alternation"}, op.ZeroOrMore{Value: CWsp}, ')'}
Option = op.Capture{Name: "Option", Value: op.And{'[', op.ZeroOrMore{Value: CWsp}, op.Reference{Name: "Alternation"}, op.ZeroOrMore{Value: CWsp}, ']'}}
CharVal = op.Capture{Name: "CharVal", Value: op.Or{LiteralDouble, LiteralSingle}}
LiteralDouble = op.And{rune(0x22), op.ZeroOrMore{Value: op.Or{op.RuneRange{Min: 0x20, Max: 0x21}, op.RuneRange{Min: 0x23, Max: 0x7E}}}, rune(0x22)}
LiteralSingle = op.And{rune(0x27), op.ZeroOrMore{Value: op.Or{op.RuneRange{Min: 0x20, Max: 0x26}, op.RuneRange{Min: 0x28, Max: 0x7E}}}, rune(0x27)}
)

func NewParser(input []rune) (*parser.Parser, error) {
p, err := parser.New(input)
if err != nil {
return nil, err
}
p.Rules["Alternation"] = Alternation
return p, nil
}
62 changes: 28 additions & 34 deletions abnf/ir/ir.go → ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package ir

import (
"fmt"
"github.com/0x51-dev/upeg/parser"
"strings"

"github.com/0x51-dev/upeg/parser"
)

func ParseRulename(n *parser.Node) (string, error) {
Expand Down Expand Up @@ -244,6 +245,16 @@ func ParseRepeat(n *parser.Node) (*Repeat, error) {
return nil, err
}
v := n.Value()
switch v {
case "+":
one := "1"
return &Repeat{
Min: &one,
}, nil
case "*":
return &Repeat{}, nil
}

if !strings.ContainsRune(v, '*') {
return &Repeat{
Min: &v,
Expand Down Expand Up @@ -298,65 +309,48 @@ func ParseRepetition(n *parser.Node) (*Repetition, error) {
if err := checkParent(n, "Repetition"); err != nil {
return nil, err
}
var v Element
var r *Repeat
for i, n := range n.Children() {
for _, n := range n.Children() {
switch n.Name {
case "Repeat":
if i != 0 {
return nil, NewInvalidNodeError("Repeat", n.Name)
}
repeat, err := ParseRepeat(n)
if err != nil {
return nil, err
}
r = repeat
case "Rulename":
v := Rulename(n.Value())
return &Repetition{
Repeat: r,
Value: &v,
}, nil
rn := Rulename(n.Value())
v = &rn
case "Alternation":
a, err := ParseAlternation(n)
if err != nil {
return nil, err
}
return &Repetition{
Repeat: r,
Value: a,
}, nil
v = a
case "Option":
o, err := ParseOption(n)
if err != nil {
return nil, err
}
return &Repetition{
Repeat: r,
Value: o,
}, nil
v = o
case "CharVal":
v := CharVal(n.Value())
return &Repetition{
Repeat: r,
Value: &v,
}, nil
cv := CharVal(n.Value())
v = &cv
case "NumVal":
v := NumVal(n.Children()[0].Value())
return &Repetition{
Repeat: r,
Value: &v,
}, nil
nv := NumVal(n.Children()[0].Value())
v = &nv
case "ProseVal":
v := ProseVal(n.Value())
return &Repetition{
Repeat: r,
Value: &v,
}, nil
pv := ProseVal(n.Value())
v = &pv
default:
return nil, NewInvalidNodeError("Element / Repeat", n.Name)
}
}
return nil, NewInvalidNodeError("Element / Repeat", "")
return &Repetition{
Repeat: r,
Value: v,
}, nil
}

func (r *Repetition) String() string {
Expand Down
50 changes: 48 additions & 2 deletions abnf/ir/ir_test.go → ir/ir_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,58 @@
package ir_test

import (
"testing"

"github.com/0x51-dev/upeg/abnf"
"github.com/0x51-dev/upeg/abnf/ir"
"github.com/0x51-dev/upeg/bnf"
"github.com/0x51-dev/upeg/ir"
"github.com/0x51-dev/upeg/parser"
"testing"
)

func TestBNF_Rulelist(t *testing.T) {
for _, test := range []struct {
raw string
expected string
}{
{
raw: "<X> ::= <Y>",
expected: "X = Y",
},
{
raw: "<X> ::= <Y> <Z>",
expected: "X = ( Y Z )",
},
{
raw: "<X> ::= <Y> | <Z>",
expected: "X = ( Y / Z )",
},
{
raw: "<X> ::= <Y>+",
expected: "X = 1*Y",
},
{
raw: "<X> ::= <Y>*",
expected: "X = *Y",
},
} {
p, err := parser.New([]rune(test.raw + "\n"))
if err != nil {
t.Fatal(err)
}
n, err := p.ParseEOF(bnf.Rulelist)
if err != nil {
t.Fatal(err)
}
l, err := ir.ParseRulelist(n)
if err != nil {
t.Fatal(err)
}
if l.String() != test.expected {
t.Errorf("expected %s, got %s", test.expected, l.String())
}
}
}

func TestParseAlternation(t *testing.T) {
for _, test := range []struct {
raw string
Expand Down
5 changes: 2 additions & 3 deletions parser/op/and_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package op_test

import (
"errors"
"fmt"
"testing"

"github.com/0x51-dev/upeg/parser"
"github.com/0x51-dev/upeg/parser/op"
"testing"
)

var AndTestCases = []AndTestCase{
Expand Down Expand Up @@ -84,7 +84,6 @@ func TestAnd_error(t *testing.T) {
errors.As(err, &stack)
var match *parser.NoMatchError
errors.As(stack.Errors[1], &match)
fmt.Println(stack)
if match.End.Character() != 'b' {
t.Fatalf("expected cursor to be at 'b', got %c", match.End.Character())
}
Expand Down
5 changes: 2 additions & 3 deletions parser/reader_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package parser_test

import (
"fmt"
. "github.com/0x51-dev/upeg/parser"
"testing"

. "github.com/0x51-dev/upeg/parser"
)

func TestReader(t *testing.T) {
Expand Down Expand Up @@ -104,7 +104,6 @@ func TestReader_Cursor(t *testing.T) {
lastNl: 4,
},
} {
fmt.Println(test.input)
r, err := NewReader([]rune(test.input))
if err != nil {
t.Fatal(err)
Expand Down

0 comments on commit 323775c

Please sign in to comment.