Преглед изворни кода

простой парсер в /demo-timestamp и некоторые багфиксы

kpmy пре 9 година
родитељ
комит
f5f0331e38
4 измењених фајлова са 160 додато и 23 уклоњено
  1. 94 0
      demo-timestamp/demo.go
  2. 16 3
      run.go
  3. 48 18
      sc.go
  4. 2 2
      tier_test.go

+ 94 - 0
demo-timestamp/demo.go

@@ -0,0 +1,94 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"github.com/kpmy/tier"
+	"io"
+	"log"
+)
+
+const (
+	Dash tier.SymCode = iota + tier.UserIota
+	Z
+	Colon
+	Plus
+
+	Minus = Dash
+)
+
+type Parser struct {
+	tier.Marker
+	r tier.Runner
+}
+
+func (p *Parser) FutureMark() tier.Marker { panic(126) }
+
+func (p *Parser) Mark(msg ...interface{}) {
+	panic(fmt.Sprint(msg...))
+}
+
+func (p *Parser) Log(msg ...interface{}) {
+	log.Println(msg...)
+}
+
+func (p *Parser) ConnectTo(rd io.Reader) {
+	sc := tier.NewScanner(bufio.NewReader(rd), opts)
+	p.r = tier.NewRunner(sc, p)
+	//tier.Debug(p.r)
+}
+
+var opts tier.Opts
+
+func parse(ts string) {
+	p := &Parser{}
+	p.ConnectTo(bytes.NewBufferString(ts))
+	log.Println(p.r.Assert(tier.Number, "number expected").Value)
+	p.r.Assert(Dash, "dash expected")
+	log.Println(p.r.Assert(tier.Number, "number expected").Value)
+	p.r.Assert(Dash, "dash expected")
+	log.Println(p.r.Assert(tier.Number, "number expected").Value)
+
+	offset := ""
+	getOffset := func() (offset string) {
+		offset = p.r.Assert(tier.Number, "number expected").Value
+		p.r.Assert(Colon, "colon expected")
+		offset = offset + ":" + p.r.Assert(tier.Number, "number expected").Value
+		return
+	}
+
+	if p.r.Await(Minus) {
+		p.r.Next()
+		offset = "-" + getOffset()
+	} else if p.r.Is(Plus) {
+		p.r.Next()
+		offset = "+" + getOffset()
+	} else if p.r.Is(Z) {
+		offset = "Z"
+	}
+
+	if offset != "" {
+		log.Println(offset)
+	}
+	log.Println()
+}
+
+func init() {
+	log.SetFlags(0)
+
+	opts = tier.DefaultOpts
+	opts.CombinedMap["-"] = Dash
+	opts.CombinedMap["+"] = Plus
+	opts.CombinedMap[":"] = Colon
+
+	opts.IdentMap["Z"] = Z
+}
+
+//xml datetime format
+func main() {
+	parse("2015-11-05")
+	parse("2015-11-05Z")
+	parse("2015-11-05+04:00")
+	parse("2015-11-05-01:00")
+}

+ 16 - 3
run.go

@@ -7,8 +7,10 @@ import (
 
 type Runner interface {
 	Next() Symbol
-	//expect is the most powerful step forward runner, breaks the compilation if unexpected sym found
+	//expect is the most powerful step forward runner, breaks the compilation if unexpected sym found, .Next should be called after handle .Sym()
 	Expect(SymCode, interface{}, ...SymCode)
+	//assert expects symbol and return it if success
+	Assert(SymCode, interface{}, ...SymCode) Symbol
 	//await runs for the sym through skip list, but may not find the sym
 	Await(SymCode, ...SymCode) bool
 	//pass runs through skip list
@@ -17,6 +19,7 @@ type Runner interface {
 	Run(SymCode)
 	//Is current symbol?
 	Is(SymCode) bool
+	Sym() Symbol
 }
 
 type rn struct {
@@ -39,16 +42,24 @@ func (r *rn) Next() Symbol {
 
 //expect is the most powerful step forward runner, breaks the compilation if unexpected sym found
 func (r *rn) Expect(sym SymCode, msg interface{}, skip ...SymCode) {
-	assert.For(r.done, 20)
+	assert.For(r.done, 20, "previous symbol unhandled")
 	if !r.Await(sym, skip...) {
 		r.marker.Mark(msg)
 	}
 	r.done = false
 }
 
+func (r *rn) Assert(sym SymCode, msg interface{}, skip ...SymCode) (ret Symbol) {
+	assert.For(r.done, 20, "previous symbol unhandled")
+	r.Expect(sym, msg, skip...)
+	ret = r.Sym()
+	r.Next()
+	return
+}
+
 //await runs for the sym through skip list, but may not find the sym
 func (r *rn) Await(sym SymCode, skip ...SymCode) bool {
-	assert.For(r.done, 20)
+	assert.For(r.done, 20, "previous symbol unhandled")
 	skipped := func() (ret bool) {
 		for _, v := range skip {
 			if v == r.sym.Code {
@@ -96,6 +107,8 @@ func (r *rn) Is(sym SymCode) bool {
 	return r.sym.Code == sym
 }
 
+func (r *rn) Sym() Symbol { return r.sym }
+
 func NewRunner(s Scanner, m Marker) Runner {
 	ret := &rn{}
 	ret.sc = s

+ 48 - 18
sc.go

@@ -16,6 +16,8 @@ const (
 	Ident
 	Number
 	String
+
+	UserIota = 100
 )
 
 type Symbol struct {
@@ -34,11 +36,11 @@ type Symbol struct {
 
 type Opts struct {
 	IdentMap      map[string]SymCode
-	IdentStarts   string
-	IdentContains string
+	IdentStarts   func() string
+	IdentContains func() string
 
-	NumContains  string
-	NumModifiers string
+	NumContains  func() string
+	NumModifiers func() string
 
 	SpaceMap map[string]SymCode
 
@@ -126,12 +128,40 @@ func (s *sc) mark(msg ...interface{}) {
 	panic(Err("scanner", s.Count(), l, c, msg...))
 }
 
+func (o Opts) safeIdentContains() string {
+	if o.IdentContains != nil {
+		return o.IdentContains()
+	}
+	return ""
+}
+
+func (o Opts) safeIdentStarts() string {
+	if o.IdentStarts != nil {
+		return o.IdentStarts()
+	}
+	return ""
+}
+
+func (o Opts) safeNumContains() string {
+	if o.NumContains != nil {
+		return o.NumContains()
+	}
+	return ""
+}
+
+func (o Opts) safeNumModifiers() string {
+	if o.NumContains != nil {
+		return o.NumModifiers()
+	}
+	return ""
+}
+
 func (o Opts) isIdentLetter(r rune) bool {
-	return o.isIdentFirstLetter(r) || unicode.IsDigit(r) || strings.ContainsRune(o.IdentContains, r)
+	return o.isIdentFirstLetter(r) || unicode.IsDigit(r) || strings.ContainsRune(o.safeIdentContains(), r)
 }
 
 func (o Opts) isIdentFirstLetter(r rune) bool {
-	return unicode.IsLetter(r) || strings.ContainsRune(o.IdentStarts, r)
+	return unicode.IsLetter(r) || strings.ContainsRune(o.safeIdentStarts(), r)
 }
 
 func (o Opts) validate() {
@@ -178,18 +208,18 @@ func (s *sc) num() (sym Symbol) {
 				s.mark("dot unexpected")
 			}
 		}
-		if s.err != nil || !(s.ch == '.' || strings.ContainsRune(dec, s.ch) || strings.ContainsRune(s.opts.NumContains, s.ch)) {
+		if s.err != nil || !(s.ch == '.' || strings.ContainsRune(dec, s.ch) || strings.ContainsRune(s.opts.safeNumContains(), s.ch)) {
 			break
 		}
 	}
-	if strings.ContainsRune(s.opts.NumModifiers, s.ch) {
+	if strings.ContainsRune(s.opts.safeNumModifiers(), s.ch) {
 		mbuf = append(mbuf, s.ch)
 		s.next()
 	}
-	if strings.ContainsAny(string(buf), s.opts.NumContains) && len(mbuf) == 0 {
+	if strings.ContainsAny(string(buf), s.opts.safeNumContains()) && len(mbuf) == 0 {
 		s.mark("modifier expected")
 	}
-	if s.err == nil {
+	if s.err == nil || s.err == io.EOF {
 		sym.Code = Number
 		sym.Value = string(buf)
 		sym.NumberOpts.Modifier = string(mbuf)
@@ -343,24 +373,24 @@ func NewScanner(rd io.RuneReader, opts ...Opts) Scanner {
 	if len(opts) > 0 {
 		ret.opts = opts[0]
 	} else {
-		ret.opts = defaultOpts
+		ret.opts = DefaultOpts
 	}
 	ret.opts.validate()
 	ret.next()
 	return ret
 }
 
-var defaultOpts Opts
+var DefaultOpts Opts
 
 func init() {
-	defaultOpts.IdentMap = make(map[string]SymCode)
+	DefaultOpts.IdentMap = make(map[string]SymCode)
 
-	defaultOpts.SpaceMap = make(map[string]SymCode)
+	DefaultOpts.SpaceMap = make(map[string]SymCode)
 
-	defaultOpts.NumContains = "ABCDEF"
-	defaultOpts.NumModifiers = "U"
+	DefaultOpts.NumContains = func() string { return "ABCDEF" }
+	DefaultOpts.NumModifiers = func() string { return "U" }
 
-	defaultOpts.CombinedMap = make(map[string]SymCode)
+	DefaultOpts.CombinedMap = make(map[string]SymCode)
 
-	defaultOpts.CommentTriplet = [3]rune{'(', '*', ')'}
+	DefaultOpts.CommentTriplet = [3]rune{'(', '*', ')'}
 }

+ 2 - 2
tier_test.go

@@ -15,8 +15,8 @@ func defTestOpts() Opts {
 	defaultOpts.SpaceMap[" "] = 101
 	defaultOpts.SpaceMap["\n"] = 102
 
-	defaultOpts.NumContains = "ABCDEF"
-	defaultOpts.NumModifiers = "UH"
+	defaultOpts.NumContains = func() string { return "ABCDEF" }
+	defaultOpts.NumModifiers = func() string { return "UH" }
 
 	defaultOpts.CombinedMap = make(map[string]SymCode)
 	defaultOpts.CombinedMap[":"] = 200