Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Commit

Permalink
Introduce pattern "Repeated"
Browse files Browse the repository at this point in the history
Introduce pattern "Repeated" and remove the patterns "ZeroOrMore" and
"OneOrMore".
  • Loading branch information
romshark committed Sep 16, 2019
1 parent 92bc9a2 commit 3c207fa
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 157 deletions.
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,22 @@ Pattern: llparser.Sequence{
},
```

#### Pattern: ZeroOrMore
`ZeroOrMore` tries to match one or many instances of a specific pattern but doesn't expect it to be matched:
#### Pattern: Repeated

`Repeated` tries to match a number repititions of a single pattern:

```go
Pattern: llparser.ZeroOrMore{
Pattern: somePattern,
Pattern: llparser.Repeated{
Pattern: somePattern,
},
```

#### Pattern: OneOrMore
`OneOrMore` expects at least one or many instances of a specific pattern:
By default, `Min` and `Max` are `0` which is equivalent to "unlimited".

```go
Pattern: llparser.OneOrMore{
Pattern: somePattern,
},
```
- Setting `Min` to a greater number than `Max` when `Max` is greater `0` is illegal and will cause a panic.
- Setting `Min` to `0` and `Max` to `1` is equivalent to declaring an optional.
- Setting both `Min` and `Max` to `0` is equivalent to declaring an unlimited number of repetitions.
- Setting `Min` to a positive number will require at least `Min` number of repetitions.

#### Pattern: Either
`Either` expects either of the given patterns selecting the first match:
Expand Down
2 changes: 1 addition & 1 deletion examples/dicklang/dicks.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
B===> 8==> B::>
B===> 8=> B::>
<====8 <::::::3
8xxxx> 8xxx=xxx>
B:x:=:x>
Expand Down
14 changes: 1 addition & 13 deletions examples/dicklang/model.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package main

import (
"fmt"

parser "github.com/romshark/llparser"
)

Expand All @@ -19,20 +17,10 @@ type ModelDick struct {
}

func (mod *ModelDicks) onDickDetected(frag parser.Fragment) error {
shaftLength := uint(len(frag.Elements()[1].Elements()))

// Check dick length
if shaftLength < 2 {
return fmt.Errorf(
"sorry, but that dick's too small (%d/2)",
shaftLength,
)
}

// Register the newly parsed dick
mod.Dicks = append(mod.Dicks, ModelDick{
Frag: frag,
ShaftLength: shaftLength,
ShaftLength: uint(len(frag.Elements()[1].Elements())),
})

return nil
Expand Down
44 changes: 36 additions & 8 deletions examples/dicklang/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,16 @@ func Parse(fileName string, source []rune) (*ModelDicks, error) {
Kind: FrSpace,
}

shaftElement := parser.Either{
parser.Exact{Expectation: []rune("=")},
parser.Exact{Expectation: []rune(":")},
parser.Exact{Expectation: []rune("x")},
}

ruleShaft := &parser.Rule{
Designation: "shaft",
Kind: FrShaft,
Pattern: parser.OneOrMore{
Pattern: parser.Either{
parser.Exact{Expectation: []rune("=")},
parser.Exact{Expectation: []rune(":")},
parser.Exact{Expectation: []rune("x")},
},
},
Pattern: parser.Repeated{Pattern: shaftElement, Min: 2},
}

ruleDickRight := &parser.Rule{
Expand Down Expand Up @@ -106,7 +106,7 @@ func Parse(fileName string, source []rune) (*ModelDicks, error) {
Designation: "file",
Pattern: parser.Sequence{
parser.Optional{Pattern: termSpace},
parser.ZeroOrMore{
parser.Repeated{
Pattern: parser.Sequence{
parser.Either{
ruleDickLeft,
Expand Down Expand Up @@ -167,6 +167,34 @@ func Parse(fileName string, source []rune) (*ModelDicks, error) {
return errors.New("that dick is missing its balls")
},
},
// Dick (right) too small
&parser.Rule{
Pattern: parser.Sequence{
parser.Either{
termBalls1,
termBallsRight1,
},
shaftElement,
termHeadRight,
},
Action: func(parser.Fragment) error {
return errors.New("that dick is too small")
},
},
// Dick (left) too small
&parser.Rule{
Pattern: parser.Sequence{
termHeadLeft,
shaftElement,
parser.Either{
termBalls1,
termBallsLeft1,
},
},
Action: func(parser.Fragment) error {
return errors.New("that dick is too small")
},
},
},
}

Expand Down
14 changes: 14 additions & 0 deletions examples/dicklang/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,18 @@ func TestParserErr(t *testing.T) {
"that dick is missing its balls at sample.dicklang:1:7",
)
})
t.Run("TooSmallLeft", func(t *testing.T) {
checkErr(
t,
`<===3 <=3 <===3`,
"that dick is too small at sample.dicklang:1:7",
)
})
t.Run("TooSmallRight", func(t *testing.T) {
checkErr(
t,
`B===> B=> B===>`,
"that dick is too small at sample.dicklang:1:7",
)
})
}
61 changes: 26 additions & 35 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,9 @@ func (pr Parser) handlePattern(
return pr.parseTermExact(scan, pt)
case Lexed:
return pr.parseLexed(scan, pt)
case ZeroOrMore:
// ZeroOrMore
return pr.parseZeroOrMore(scan, pt.Pattern)
case OneOrMore:
// OneOrMore
return pr.parseOneOrMore(scan, pt.Pattern)
case Repeated:
err := pr.parseRepeated(scan, pt.Min, pt.Max, pt.Pattern)
return nil, err
case Optional:
// Optional
return pr.parseOptional(scan, pt.Pattern)
Expand Down Expand Up @@ -97,48 +94,40 @@ func (pr Parser) parseOptional(
return frag, nil
}

func (pr Parser) parseZeroOrMore(
func (pr Parser) parseRepeated(
scanner *scanner,
min uint,
max uint,
pattern Pattern,
) (Fragment, error) {
lastPosition := scanner.Lexer.cr
for {
frag, err := pr.handlePattern(scanner, pattern)
if err != nil {
if _, ok := err.(*ErrUnexpectedToken); ok {
// Reset scanner to the last match
scanner.Set(lastPosition)
return nil, nil
}
return nil, err
}
lastPosition = scanner.Lexer.cr
// Append rule patterns, other patterns are appended automatically
if !pattern.Container() {
scanner.Append(pattern, frag)
}
) error {
if max != 0 && min > max {
panic(fmt.Errorf(
"min (%d) > max (%d) while parsing pattern Repeated(%s)",
min,
max,
pattern.Desig(),
))
}
}

func (pr Parser) parseOneOrMore(
scanner *scanner,
pattern Pattern,
) (Fragment, error) {
num := 0
num := uint(0)
lastPosition := scanner.Lexer.cr
for {
if max != 0 && num >= max {
break
}

frag, err := pr.handlePattern(scanner, pattern)
if err != nil {
if _, ok := err.(*ErrUnexpectedToken); ok {
if num < 1 {
// No matches so far but already a mismatch
return nil, err
if min != 0 && num < min {
// Mismatch before the minimum is read
return err
}
// Reset scanner to the last match
scanner.Set(lastPosition)
return nil, nil
return nil
}
return nil, err
return err
}
num++
lastPosition = scanner.Lexer.cr
Expand All @@ -147,6 +136,8 @@ func (pr Parser) parseOneOrMore(
scanner.Append(pattern, frag)
}
}

return nil
}

func (pr Parser) parseSequence(
Expand Down

0 comments on commit 3c207fa

Please sign in to comment.