2019-06-16 20:45:30 +02:00
|
|
|
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 (
|
2019-06-17 06:27:17 +02:00
|
|
|
word Parser
|
|
|
|
words Parser
|
|
|
|
command Parser
|
|
|
|
script Parser
|
2019-06-16 20:45:30 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-06-17 08:47:04 +02:00
|
|
|
comment = NewParser("comment", Merge(Seq("#", NotChars("\r\n"), Chars("\r\n")))).Map(drop)
|
2019-06-17 06:27:17 +02:00
|
|
|
wordExp = NewParser("{*}word", Seq("{*}", &word)).Map(resultchild(1))
|
|
|
|
wordSub = NewParser("[*]word", Seq("[*]", &word)).Map(resultchild(1))
|
|
|
|
wordQtd = NewParser(`"word"`, StringLit(`"`)).Map(token)
|
2019-06-17 08:47:04 +02:00
|
|
|
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 })
|
2019-06-16 20:45:30 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2019-06-17 08:47:04 +02:00
|
|
|
word = NewParser("word", Any(&wordQtd, &group, &wordSmp, &wordSub, &wordExp))
|
2019-06-17 06:27:17 +02:00
|
|
|
words = NewParser("words", Many(&word)).Map(collectchildren)
|
2019-06-17 08:47:04 +02:00
|
|
|
command = NewParser("command", Seq(&words, Any(";", Chars("\r\n")))).Map(resultchild(0))
|
2019-06-17 06:27:17 +02:00
|
|
|
script = NewParser("script", Many(Any(&comment, &command))).Map(collectchildren)
|
2019-06-16 20:45:30 +02:00
|
|
|
}
|
|
|
|
|
2019-06-17 08:47:04 +02:00
|
|
|
func simpleWhitespace(s *State) {
|
|
|
|
for s.Pos < len(s.Input) {
|
|
|
|
switch s.Input[s.Pos] {
|
|
|
|
case '\t', '\v', '\f', ' ':
|
|
|
|
s.Pos++
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-16 20:45:30 +02:00
|
|
|
func Parse(input string) (data interface{}, e error) {
|
2019-06-17 08:47:04 +02:00
|
|
|
data, e = Run(&script, input, simpleWhitespace)
|
2019-06-16 20:45:30 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseDebug(input string) (data interface{}, e error) {
|
|
|
|
EnableLogging(os.Stdout)
|
2019-06-17 08:47:04 +02:00
|
|
|
data, e = Run(&script, input, simpleWhitespace)
|
2019-06-16 20:45:30 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func found(typ string) func(*Result) {
|
|
|
|
return func(r *Result) {
|
2019-06-17 06:27:17 +02:00
|
|
|
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)
|
|
|
|
}
|
2019-06-16 20:45:30 +02:00
|
|
|
|
2019-06-17 06:27:17 +02:00
|
|
|
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)
|
|
|
|
}
|
2019-06-16 20:45:30 +02:00
|
|
|
}
|
2019-06-17 06:27:17 +02:00
|
|
|
// log.Println("data: ", data)
|
|
|
|
r.Result = data
|
2019-06-16 20:45:30 +02:00
|
|
|
}
|
2019-06-17 06:27:17 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-16 20:45:30 +02:00
|
|
|
func dump(r *Result) {
|
|
|
|
spew.Dump(r)
|
|
|
|
}
|