Skip to content

Instantly share code, notes, and snippets.

@matthewmueller
Created August 1, 2020 12:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewmueller/98ec8465bde19c463cdbe17ca5f84851 to your computer and use it in GitHub Desktop.
Save matthewmueller/98ec8465bde19c463cdbe17ca5f84851 to your computer and use it in GitHub Desktop.
Route parser, similar to https://github.com/pillarjs/path-to-regexp in Express.js
package route
//go:generate peg -switch -inline grammar.peg
import (
"regexp"
"github.com/kr/pretty"
)
// Parse fn
func Parse(input string) (*regexp.Regexp, error) {
parser := &parser{Buffer: input}
parser.Init()
err := parser.Parse()
if err != nil {
return nil, err
}
parser.Execute()
pretty.Println(parser.route)
return nil, nil
}
// Route struct
type Route struct {
Segments []Segment
}
// Segment interface
type Segment interface {
segment()
}
// Slash struct
type Slash struct {
}
// OptionalKey struct
type OptionalKey struct {
Prefix *Text
Key Key
}
func (*Slash) segment() {}
func (*OptionalKey) segment() {}
func (*RegexpKey) segment() {}
func (*BasicKey) segment() {}
func (*Text) segment() {}
// Key interface
type Key interface {
Segment
key()
}
// RegexpKey struct
type RegexpKey struct {
Name *Identifier
Regexp *Regexp
}
// BasicKey struct
type BasicKey struct {
Name *Identifier
}
func (*RegexpKey) key() {}
func (*BasicKey) key() {}
// Identifier struct
type Identifier struct {
Value string
}
// Regexp struct
type Regexp struct {
Value string
}
// Text struct
type Text struct {
Value string
}
package route
type parser Peg {
route *Route
segments []Segment
key Key
rkey *RegexpKey
bkey *BasicKey
optional *OptionalKey
slash *Slash
regexp *Regexp
identifier *Identifier
text *Text
}
Route <- Segment+ End {
p.route = &Route{
Segments: p.segments,
}
}
Segment <- Slash { p.segments = append(p.segments, p.slash) }
/ OptionalKey { p.segments = append(p.segments, p.optional) }
/ Key { p.segments = append(p.segments, p.key) }
/ Text { p.segments = append(p.segments, p.text) }
OptionalKey <- Text? Key '?' {
p.optional = &OptionalKey{
Prefix: p.text,
Key: p.key,
}
}
Key <- RegexpKey { p.key = p.rkey }
/ BasicKey { p.key = p.bkey }
RegexpKey <- ':' Identifier Regexp {
p.rkey = &RegexpKey{
Name: p.identifier,
Regexp: p.regexp,
}
fmt.Println("regexp", text)
}
BasicKey <- ':' Identifier {
p.bkey = &BasicKey{Name: p.identifier}
}
Regexp <- '(' < [^)]+ > ')' {
p.regexp = &Regexp{Value: text}
}
Identifier <- < [A-Za-z] [A-Za-z0-9]* > {
p.identifier = &Identifier{Value: text}
}
Text <- < [^:/]+ > {
p.text = &Text{Value: text}
}
Slash <- '/' {
p.slash = &Slash{}
}
End
<- !.
// First prototype in peg.js
Route = Segment+
Segment = Slash / OptionalKey / Key / Text
Key = RegexpKey / BasicKey
OptionalKey = prefix:Text? key:Key '?' {
return {
type: "optional",
prefix: prefix,
key: key
}
}
RegexpKey = ':' id:Identifier regex:Regexp {
return {
type: "rekey",
name: id,
pattern: regex,
}
}
BasicKey = ':' id:Identifier {
return {
type: "key",
name: id,
}
}
Slash = '/' {
return {
type:"slash",
value: text()
}
}
Text = [^:/]+ {
return {
type:"text",
value: text()
}
}
Regexp = '(' regex:[^\)]+ ')' {
return {
type: "regexp",
value: regex.join('')
}
}
Identifier = head:[A-Za-z] tail:[A-Za-z0-9]* {
return {
type: "identifier",
value: [head].concat(tail).join("")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment