ginger/parse.go
2013-05-27 01:04:56 -04:00

327 lines
8.3 KiB
Go

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',
's' : ' ',
'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
} else if b[0] == ';' {
rbuf.ReadLine()
return FirstByte(rbuf)
}
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
}