Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-lyman committed Nov 2, 2016
1 parent 06952f8 commit 693ccd4
Show file tree
Hide file tree
Showing 6 changed files with 502 additions and 0 deletions.
95 changes: 95 additions & 0 deletions amotoen.go
@@ -0,0 +1,95 @@
package amotoen

import (
"strconv"
)

type Writer interface {
Write([]byte) (int, error)
}

var Debug = false
var DebugOut Writer

func Manual(root State, input Input, mustConsumeAll bool) Tree {
result, err := root.Handle(input)
if err != nil {
panic(err)
}
if mustConsumeAll && !input.AtEOI() {
panic("Failed to consume all input. Ended at: " + input.LinedCursor() + ", Tree: " + result.String())
}
return result
}

type State interface {
Handle(Input) (Tree, error)
Stringer
}

type Input interface {
Peek() (interface{}, error)
Cursor() int64
LinedCursor() string
Consume(n int64)
Revert(n int64)
AtEOI() bool
Debug(State)
}

type Tree interface {
Append(Tree) Tree
Stringer
}

type DeferredState struct{ inner State }

func (s *DeferredState) String() string { return s.inner.String() }
func (s *DeferredState) Set(newState State) { s.inner = newState }
func (s *DeferredState) Handle(input Input) (Tree, error) {
t, err := s.inner.Handle(input)
return t, err
}

func c(args ...State) {
for _, a := range args {
if a == nil {
panic("You can't have a nil state - consider using a DeferredState wrapper.")
}
}
}

func N(arg State) State { c(arg); return &nState{"", arg} }
func E(args ...State) State { c(args...); return &eState{"", args} }
func O(arg State) State { c(arg); return &oState{"", arg} }
func S(args ...State) State { c(args...); return &sState{"", args} }
func Z(arg State) State { c(arg); return &zState{"", arg} }

func NL(label string, arg State) State { c(arg); return &nState{label, arg} }
func EL(label string, args ...State) State { c(args...); return &eState{label, args} }
func OL(label string, arg State) State { c(arg); return &oState{label, arg} }
func SL(label string, args ...State) State { c(args...); return &sState{label, args} }
func ZL(label string, arg State) State { c(arg); return &zState{label, arg} }

type Stringer interface {
String() string
}

type internalError struct {
state State
cursor int64
message string
err error
}

func (e internalError) Error() string {
if e.err != nil {
return e.state.String() + "@" + strconv.FormatInt(e.cursor, 10) + " " + e.message + ": " + e.err.Error()
} else {
return e.state.String() + "@" + strconv.FormatInt(e.cursor, 10) + " " + e.message + ": <nil error>"
}
}

func newError(s State, input Input, message string, err error) error {
return internalError{s, input.Cursor(), message, err}
}
25 changes: 25 additions & 0 deletions amotoen.grammar
@@ -0,0 +1,25 @@
newline := \u10
space := \u20
tab := \u09
zws := *{space, tab}
zwsn := *{space, tab, newline}
grammar := +[zwsn, rule, zws, newline]
rule := [key, zws, \:, \=, zws, ruleBody]
key := [lower, *{lower, upper, number}]
lower := >`abcdefghijklmnopqrstuvwxyz`
upper := >`ABCDEFGHIJKLMNOPQRSTUVWXYZ`
number := >`0123456789`
ruleBody := {escapedUnicode, escapedRune, seq, either, eitherString, zeroOrMore, oneOrMore, anyNot, stringValue, key}
ruleBodyList := [ruleBody, *[zws, \,, zws, ruleBody]]
escapedUnicode := [\\, {shortUnicode, longUnicode}]
shortUnicode := [\u, hexRune, hexRune]
longUnicode := [\U, hexRune, hexRune, hexRune, hexRune]
escapedRune := [\\, !\u00]
seq := [\[, ruleBodyList, \]]
either := [\{, ruleBodyList, \}]
eitherString := [\>, \`, +!\`, \`]
zeroOrMore := [\*, ruleBody]
oneOrMore := [\+, ruleBody]
anyNot := [\!, ruleBody]
stringValue := [\`, !\`, *!\`, \`]
hexRune := >`0123456789abcdefABCDEF`
31 changes: 31 additions & 0 deletions grammar.go
@@ -0,0 +1,31 @@
package amotoen

var zws = Z(ES(" \t"))
var Grammar = O(S(Z(ES(" \t\n")), rule, zws, R('\n')))
var rule = S(key, zws, R(':'), R('='), zws, ruleBody)
var key = S(lower, Z(E(lower, upper, number)))
var lower = ES("abcdefghijklmnopqrstuvwxyz")
var upper = ES("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
var number = ES("0123456789")
var ruleBody = &DeferredState{}
var ruleBodyList = S(ruleBody, Z(S(zws, R(','), zws, ruleBody)))
var escapedUnicode = S(R('\\'), E(shortUnicode, longUnicode))
var shortUnicode = S(R('u'), hexRune, hexRune)
var longUnicode = S(R('U'), hexRune, hexRune, hexRune, hexRune)
var escapedRune = S(R('\\'), N(R('\x00')))
var seq = S(R('['), ruleBodyList, R(']'))
var either = S(R('{'), ruleBodyList, R('}'))
var eitherString = S(R('>'), R('`'), O(N(R('`'))), R('`'))
var zeroOrMore = S(R('*'), ruleBody)
var oneOrMore = S(R('+'), ruleBody)
var anyNot = S(R('!'), ruleBody)
var stringValue = S(R('`'), O(N(R('`'))), R('`'))
var hexRune = ES("0123456789abcdefABCDEF")

func init() {
ruleBody.Set(E(
escapedUnicode, escapedRune,
seq, either, eitherString, zeroOrMore, oneOrMore, anyNot,
stringValue, key,
))
}
43 changes: 43 additions & 0 deletions node.go
@@ -0,0 +1,43 @@
package amotoen

import (
"bytes"
)

type node struct {
content Stringer
children []Tree
}

func (n *node) Append(t Tree) Tree {
n.children = append(n.children, t)
return t
}

func (n *node) String() string {
var result bytes.Buffer
if n.content != nil {
result.WriteString(n.content.String())
}
if len(n.children) > 0 {
result.WriteString("[")
}
for i, c := range n.children {
result.WriteString(c.String())
if i < len(n.children)-1 {
result.WriteString(",")
}
}
if len(n.children) > 0 {
result.WriteString("]")
}
return result.String()
}

type stringContent string

func (s stringContent) String() string { return string(s) }

type runeContent rune

func (r runeContent) String() string { return "'" + string(r) + "'" }
154 changes: 154 additions & 0 deletions state.go
@@ -0,0 +1,154 @@
package amotoen

type oState struct {
label string
inner State
}

func (s *oState) String() string {
if len(s.label) == 0 {
return "One or more <no label>"
} else {
return "One or more of: " + s.label
}
}

func (s *oState) Handle(input Input) (result Tree, err error) {
input.Debug(s)
result = &node{stringContent(s.label), nil}
previous := input.Cursor()
tmp, err := s.inner.Handle(input)
if err != nil {
input.Revert(previous)
return nil, err
}
result.Append(tmp)
for {
previous := input.Cursor()
tmp, err := s.inner.Handle(input)
if err != nil {
input.Revert(previous)
break
}
result.Append(tmp)
}
return result, nil
}

type zState struct {
label string
inner State
}

func (s *zState) String() string {
if len(s.label) == 0 {
return "Zero or more <no label>"
} else {
return "Zero or more of: " + s.label
}
}

func (s *zState) Handle(input Input) (result Tree, err error) {
input.Debug(s)
result = &node{stringContent(s.label), nil}
for {
previous := input.Cursor()
tmp, err := s.inner.Handle(input)
if err != nil {
input.Revert(previous)
break
}
result.Append(tmp)
}
return result, nil
}

type sState struct {
label string
inner []State
}

func (s *sState) String() string {
if len(s.label) == 0 {
return "Sequence <no label>"
} else {
return "Sequence of: " + s.label
}
}

func (s *sState) Handle(input Input) (Tree, error) {
input.Debug(s)
previous := input.Cursor()
result := &node{stringContent(s.label), nil}
for _, child := range s.inner {
tmp, err := child.Handle(input)
if err != nil {
input.Revert(previous)
return nil, err
}
result.Append(tmp)
}
return result, nil
}

type nState struct {
label string
inner State
}

func (s *nState) String() string {
if len(s.label) == 0 {
return "Not <no label>"
} else {
return "Not of: " + s.label
}
}

func (s *nState) Handle(input Input) (Tree, error) {
input.Debug(s)
previous := input.Cursor()
_, err := s.inner.Handle(input)
if err != nil {
tmp, err := input.Peek()
if err != nil {
input.Revert(previous)
return nil, newError(s, input, "nState failed to get rune", err)
}
r := tmp.(rune)
input.Consume(1)
return &node{runeContent(r), nil}, nil
}
input.Revert(previous)
return nil, newError(s, input, "nState failed", nil)
}

type eState struct {
label string
inner []State
}

func (s *eState) String() string {
if len(s.label) == 0 {
return "Either <no label>"
} else {
return "Either of: " + s.label
}
}

func (s *eState) Handle(input Input) (Tree, error) {
input.Debug(s)
previous := input.Cursor()
for _, child := range s.inner {
tmp, err := child.Handle(input)
if err != nil {
input.Revert(previous)
if Debug {
DebugOut.Write([]byte(s.String() + " failed on: " + child.String() + " with: " + err.Error() + "\n"))
}
continue
}
return tmp, nil
}
input.Revert(previous)
return nil, newError(s, input, "eState failed", nil)
}

0 comments on commit 693ccd4

Please sign in to comment.