initial commit, parser is a go, but the data structures generating aren't actually interesting in any way
This commit is contained in:
commit
06bdf35e10
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.gng
|
19
ginger.go
Normal file
19
ginger.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
file, err := os.Open("test.gng")
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
rbuf := bufio.NewReader(file)
|
||||||
|
|
||||||
|
for {
|
||||||
|
el,err := PullElement(rbuf)
|
||||||
|
if err != nil { break }
|
||||||
|
fmt.Printf("%s\n",el)
|
||||||
|
}
|
||||||
|
}
|
319
parse.go
Normal file
319
parse.go
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CharComp interface {
|
||||||
|
includes(byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//A single character
|
||||||
|
type Char byte
|
||||||
|
func (c Char) includes(comp byte) bool { return byte(c) == comp }
|
||||||
|
|
||||||
|
//The starting and ending chars (inclusive) in a range of chars
|
||||||
|
type CharRange struct {
|
||||||
|
start byte
|
||||||
|
end byte
|
||||||
|
}
|
||||||
|
func (cr CharRange) includes (comp byte) bool {
|
||||||
|
return byte(cr.start) <= comp && byte(cr.end) >= comp
|
||||||
|
}
|
||||||
|
|
||||||
|
//A set of char comparables. Could contain other CharSets
|
||||||
|
type CharSet []CharComp
|
||||||
|
func (cs CharSet) includes (comp byte) bool {
|
||||||
|
for _,c := range cs {
|
||||||
|
if c.includes(comp) { return true }
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var ASCII = CharRange{0,127}
|
||||||
|
|
||||||
|
var Whitespace = CharSet{
|
||||||
|
CharRange{9,13}, //tab -> carriage return
|
||||||
|
Char(' '),
|
||||||
|
}
|
||||||
|
|
||||||
|
var Letters = CharSet{
|
||||||
|
CharRange{65,90}, //upper
|
||||||
|
CharRange{97,122}, //lower
|
||||||
|
}
|
||||||
|
|
||||||
|
var Numbers = CharRange{48,57}
|
||||||
|
|
||||||
|
//For the simple string syntax (no d-quotes)
|
||||||
|
var SimpleChars = CharSet{
|
||||||
|
Letters,
|
||||||
|
Numbers,
|
||||||
|
Char('-'),
|
||||||
|
Char('_'),
|
||||||
|
Char('!'),
|
||||||
|
Char('?'),
|
||||||
|
}
|
||||||
|
|
||||||
|
//For translating the special characters
|
||||||
|
var SpecialCharsMap = map[byte]byte{
|
||||||
|
'\\': '\\',
|
||||||
|
'"' : '"',
|
||||||
|
'\'': '\'',
|
||||||
|
'n' : '\n',
|
||||||
|
'r' : '\r',
|
||||||
|
't' : '\t',
|
||||||
|
'0' : 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
//For knowing what the other end of the seq should be
|
||||||
|
var SeqMap = map[byte]byte{
|
||||||
|
'(': ')',
|
||||||
|
'[': ']',
|
||||||
|
'{': '}',
|
||||||
|
}
|
||||||
|
|
||||||
|
//For simple unsigned integers
|
||||||
|
var UintChars = CharSet{
|
||||||
|
Numbers,
|
||||||
|
Char(','),
|
||||||
|
}
|
||||||
|
|
||||||
|
//Looks at (but doesn't consume) the first byte in the buffer
|
||||||
|
func FirstByte(rbuf *bufio.Reader) (byte,error) {
|
||||||
|
b,err := rbuf.Peek(1)
|
||||||
|
if err != nil { return 0,err }
|
||||||
|
return b[0],err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrettyChar(c byte) string {
|
||||||
|
switch c {
|
||||||
|
case '\n': return "newline"
|
||||||
|
case '\t': return "tab"
|
||||||
|
case '\r': return "carriage return"
|
||||||
|
case 0: return "null byte"
|
||||||
|
default: return string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExpectedPanic(rbuf *bufio.Reader,givenErr error,expected string) {
|
||||||
|
if givenErr != nil {
|
||||||
|
panic("Expected "+expected+", but found:"+givenErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
panic("Expected "+expected+", but found:"+err.Error())
|
||||||
|
} else {
|
||||||
|
panic("Expected "+expected+", but found:"+PrettyChar(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConcatByteSlices(a,b []byte) []byte {
|
||||||
|
c := make([]byte,len(a)+len(b))
|
||||||
|
copy(c,a)
|
||||||
|
copy(c[len(a):],b)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullWhitespace(rbuf *bufio.Reader) (int,error) {
|
||||||
|
var i int
|
||||||
|
for i=0;;i++{
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return i,err }
|
||||||
|
if Whitespace.includes(b) != true { return i,nil }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
}
|
||||||
|
return i,nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullUint(rbuf *bufio.Reader) ([]byte,error) {
|
||||||
|
r := make([]byte,0,16)
|
||||||
|
for {
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return r,err }
|
||||||
|
if UintChars.includes(b) != true { return r,nil }
|
||||||
|
if b != ',' { r = append(r,b) }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullInteger(rbuf *bufio.Reader) ([]byte,error) {
|
||||||
|
neg := false
|
||||||
|
nb,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
if nb == '-' {
|
||||||
|
neg = true
|
||||||
|
rbuf.ReadByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
ui,err := PullUint(rbuf)
|
||||||
|
if len(ui) == 0 || err != nil { return ui,err }
|
||||||
|
|
||||||
|
var r []byte
|
||||||
|
if neg {
|
||||||
|
r = ConcatByteSlices([]byte{'-'},ui)
|
||||||
|
} else {
|
||||||
|
r = ui
|
||||||
|
}
|
||||||
|
|
||||||
|
return r,nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullFullString(rbuf *bufio.Reader) ([]byte,error) {
|
||||||
|
r := make([]byte,0,256)
|
||||||
|
|
||||||
|
//Make sure string starts with dquote
|
||||||
|
dq,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
if dq != '"' { return r,nil }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
|
||||||
|
for {
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return r,err }
|
||||||
|
if b == '"' {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
return r,nil
|
||||||
|
} else if b == '\\' {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
ec,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return r,err }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
|
||||||
|
if c,ok := SpecialCharsMap[ec]; ok {
|
||||||
|
r = append(r,c)
|
||||||
|
} else {
|
||||||
|
r = append(r,ec)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
r = append(r,b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullSimpleString(rbuf *bufio.Reader) ([]byte,error) {
|
||||||
|
r := make([]byte,0,16)
|
||||||
|
|
||||||
|
for {
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return r,err }
|
||||||
|
if SimpleChars.includes(b) {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
r = append(r,b)
|
||||||
|
} else {
|
||||||
|
return r,nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullByte(rbuf *bufio.Reader) ([]byte,error) {
|
||||||
|
sq,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
if sq != '\'' { return []byte{},nil }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
|
||||||
|
var rb byte
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
if b == '\\' {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
ec,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
|
||||||
|
if c,ok := SpecialCharsMap[ec]; ok {
|
||||||
|
rb = c
|
||||||
|
} else {
|
||||||
|
return []byte{},nil
|
||||||
|
}
|
||||||
|
} else if ASCII.includes(b) {
|
||||||
|
rb = b
|
||||||
|
} else {
|
||||||
|
return []byte{},nil
|
||||||
|
}
|
||||||
|
rbuf.ReadByte()
|
||||||
|
rstr := strconv.Itoa(int(rb))
|
||||||
|
return []byte(rstr),nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullSeq(rbuf *bufio.Reader) ([]GngType,error) {
|
||||||
|
d,err := FirstByte(rbuf)
|
||||||
|
if err != nil { return nil,err }
|
||||||
|
od,ok := SeqMap[d]
|
||||||
|
if !ok { return nil,fmt.Errorf("Unknown seq type") }
|
||||||
|
rbuf.ReadByte()
|
||||||
|
|
||||||
|
r := make([]GngType,0,16)
|
||||||
|
for {
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
return r,err
|
||||||
|
} else if (b == od) {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
return r,nil
|
||||||
|
} else {
|
||||||
|
el,err := PullElement(rbuf)
|
||||||
|
if err != nil { return r,err }
|
||||||
|
r = append(r,el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func PullElement(rbuf *bufio.Reader) (GngType,error) {
|
||||||
|
for {
|
||||||
|
b,err := FirstByte(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
return nil,err
|
||||||
|
} else if Whitespace.includes(b) {
|
||||||
|
rbuf.ReadByte() //ignore
|
||||||
|
} else if Numbers.includes(b) || b == '-' {
|
||||||
|
n,err := PullInteger(rbuf)
|
||||||
|
if len(n) == 0 || err != nil { ExpectedPanic(rbuf,err,"number") }
|
||||||
|
|
||||||
|
fb,err := FirstByte(rbuf)
|
||||||
|
if err != nil { break }
|
||||||
|
if fb == '.' {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
ui,err := PullUint(rbuf)
|
||||||
|
if len(ui) == 0 || err != nil { ExpectedPanic(rbuf,err,"number") }
|
||||||
|
ui = ConcatByteSlices([]byte{'.'},ui)
|
||||||
|
n = ConcatByteSlices(n,ui)
|
||||||
|
return NewGngFloat(n),nil
|
||||||
|
} else if fb == 'b' {
|
||||||
|
rbuf.ReadByte()
|
||||||
|
return NewGngByte(n),nil
|
||||||
|
} else {
|
||||||
|
return NewGngInteger(n),nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if b == '"' {
|
||||||
|
s,err := PullFullString(rbuf)
|
||||||
|
if len(s) == 0 || err != nil { ExpectedPanic(rbuf,err,"end of string") }
|
||||||
|
return NewGngString(s),nil
|
||||||
|
} else if b == '\'' {
|
||||||
|
b,err := PullByte(rbuf)
|
||||||
|
if len(b) == 0 || err != nil { ExpectedPanic(rbuf,err,"character") }
|
||||||
|
return NewGngByte(b),nil
|
||||||
|
} else if Letters.includes(b) {
|
||||||
|
s,err := PullSimpleString(rbuf)
|
||||||
|
if len(s) == 0 || err != nil { ExpectedPanic(rbuf,err,"end of string") }
|
||||||
|
return NewGngString(s),nil
|
||||||
|
} else if _,ok := SeqMap[b]; ok {
|
||||||
|
s,err := PullSeq(rbuf)
|
||||||
|
if err != nil { ExpectedPanic(rbuf,err,"end of seq") }
|
||||||
|
switch(b) {
|
||||||
|
case '(': return NewGngList(s)
|
||||||
|
case '[': return NewGngVector(s)
|
||||||
|
case '{': return NewGngMap(s)
|
||||||
|
default: panic("Unknown sequence type:"+string(b))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ExpectedPanic(rbuf,nil,"start of valid data structure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil,nil
|
||||||
|
}
|
47
types.go
Normal file
47
types.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GngType interface {}
|
||||||
|
|
||||||
|
type GngString string
|
||||||
|
func NewGngString(b []byte) GngString { return GngString(b) }
|
||||||
|
|
||||||
|
type GngByte byte
|
||||||
|
func NewGngByte(b []byte) GngByte {
|
||||||
|
i,err := strconv.Atoi(string(b))
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
return GngByte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GngInteger int64
|
||||||
|
func NewGngInteger(b []byte) GngInteger {
|
||||||
|
i,err := strconv.Atoi(string(b))
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
return GngInteger(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GngFloat float64
|
||||||
|
func NewGngFloat(b []byte) GngFloat {
|
||||||
|
f,err := strconv.ParseFloat(string(b),64)
|
||||||
|
if err != nil { panic(err) }
|
||||||
|
return GngFloat(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GngVector []GngType
|
||||||
|
func NewGngVector(e []GngType) (GngVector,error) { return GngVector(e),nil }
|
||||||
|
|
||||||
|
type GngList []GngType
|
||||||
|
func NewGngList(e []GngType) (GngList,error) { return GngList(e),nil }
|
||||||
|
|
||||||
|
type GngMap []GngType
|
||||||
|
func NewGngMap(e []GngType) (GngMap,error) {
|
||||||
|
if len(e)%2 != 0 {
|
||||||
|
return nil,fmt.Errorf("uneven number of elements in map literal")
|
||||||
|
} else {
|
||||||
|
return GngMap(e),nil
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user