package tcl import ( "log" "os" "github.com/davecgh/go-spew/spew" . "github.com/oec/goparsify" ) // Tcl parser, using combinators // Based on the grammer in: // https://wiki.tcl-lang.org/page/BNF+for+Tcl var ( word Parser words Parser command Parser script Parser ) var ( comment = NewParser("comment", Merge(Seq("#", NotChars("\r\n"), Chars("\r\n")))).Map(drop) wordExp = NewParser("{*}word", Seq("{*}", &word)).Map(resultchild(1)) wordSub = NewParser("[*]word", Seq("[*]", &word)).Map(resultchild(1)) wordQtd = NewParser(`"word"`, StringLit(`"`)).Map(token) wordSmp = NewParser("simple", NotChars("{} \t\r\n")).Map(token) group = NewParser("{group}", Seq("{", Maybe(Chars("\r\n")), Some(Any(&command, &words)), "}")).Map(func(r *Result) { collectchildren(&r.Child[2]); r.Result = r.Child[2].Result }) ) func init() { word = NewParser("word", Any(&wordQtd, &group, &wordSmp, &wordSub, &wordExp)) words = NewParser("words", Many(&word)).Map(collectchildren) command = NewParser("command", Seq(&words, Any(";", Chars("\r\n")))).Map(resultchild(0)) script = NewParser("script", Many(Any(&comment, &command))).Map(collectchildren) } func simpleWhitespace(s *State) { for s.Pos < len(s.Input) { switch s.Input[s.Pos] { case '\t', '\v', '\f', ' ': s.Pos++ default: return } } } func Parse(input string) (data interface{}, e error) { data, e = Run(&script, input, simpleWhitespace) return } func ParseDebug(input string) (data interface{}, e error) { EnableLogging(os.Stdout) data, e = Run(&script, input, simpleWhitespace) return } func found(typ string) func(*Result) { return func(r *Result) { log.Printf("found a %s: %q (r.R: %v)\n", typ, r.Token, r.Result) r.Result = r.Token } } func drop(r *Result) { r.Token = "" r.Result = nil } func token(r *Result) { r.Result = r.Token // log.Println("token:", r.Token) } func collectchildren(r *Result) { data := []string{} for _, ch := range r.Child { switch v := ch.Result.(type) { case string: data = append(data, v) case []string: data = append(data, v...) default: // log.Println("oops:", r) } } // log.Println("data: ", data) r.Result = data } func resultchild(c int) func(*Result) { return func(r *Result) { r.Result = r.Child[c].Result // log.Printf("child[%d]: %v\n", c, r.Result) } } func dump(r *Result) { spew.Dump(r) }