Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
06952f8
commit 693ccd4
Showing
6 changed files
with
502 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) + "'" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.