├── token └── token.go ├── README.md ├── ast └── ast.go ├── parser ├── lexer.go ├── parser_test.go ├── parser.go.y └── parser.go ├── .gitignore ├── LICENSE ├── tq.go └── transformer └── transformer.go /token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | type Token struct { 4 | Token int 5 | Literal string 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tq 2 | 3 | tq is a command-line [TOML](https://github.com/toml-lang/toml) processor. Inspired by [jq](https://github.com/stedolan/jq). 4 | 5 | -------------------------------------------------------------------------------- /ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "github.com/zoncoen/tq/token" 5 | ) 6 | 7 | type Filter interface{} 8 | 9 | type EmptyFilter struct { 10 | } 11 | 12 | type KeyFilter struct { 13 | Key string 14 | } 15 | 16 | type IndexFilter struct { 17 | Index string 18 | } 19 | 20 | type RangeFilter struct { 21 | Low string 22 | High string 23 | } 24 | 25 | type BinaryOp struct { 26 | Left Filter 27 | Op token.Token 28 | Right Filter 29 | } 30 | 31 | type IgnoreErrorHandler struct { 32 | Filter Filter 33 | } 34 | -------------------------------------------------------------------------------- /parser/lexer.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "strconv" 5 | "text/scanner" 6 | 7 | "github.com/zoncoen/tq/ast" 8 | "github.com/zoncoen/tq/token" 9 | ) 10 | 11 | type Lexer struct { 12 | scanner.Scanner 13 | result ast.Filter 14 | } 15 | 16 | func (l *Lexer) Lex(lval *yySymType) int { 17 | tok := int(l.Scan()) 18 | lit := l.TokenText() 19 | if tok == scanner.Ident { 20 | tok = STRING 21 | } 22 | if tok == scanner.String { 23 | tok = STRING 24 | lit, _ = strconv.Unquote(lit) 25 | } 26 | if tok == scanner.Int { 27 | tok = INT 28 | } 29 | if tok == int('.') { 30 | tok = PERIOD 31 | } 32 | if tok == int('|') { 33 | tok = PIPE 34 | } 35 | if tok == int('[') { 36 | tok = LBRACK 37 | } 38 | if tok == int(']') { 39 | tok = RBRACK 40 | } 41 | if tok == int(':') { 42 | tok = COLON 43 | } 44 | if tok == int(',') { 45 | tok = COMMA 46 | } 47 | if tok == int('?') { 48 | tok = QUESTION 49 | } 50 | lval.token = token.Token{Token: tok, Literal: lit} 51 | return tok 52 | } 53 | 54 | func (l *Lexer) Error(e string) { 55 | panic(e) 56 | } 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/osx,vim,go 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | 31 | ### Vim ### 32 | [._]*.s[a-w][a-z] 33 | [._]s[a-w][a-z] 34 | *.un~ 35 | Session.vim 36 | .netrwhist 37 | *~ 38 | 39 | 40 | ### Go ### 41 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 42 | *.o 43 | *.a 44 | *.so 45 | 46 | # Folders 47 | _obj 48 | _test 49 | 50 | # Architecture specific extensions/prefixes 51 | *.[568vq] 52 | [568vq].out 53 | 54 | *.cgo1.go 55 | *.cgo2.c 56 | _cgo_defun.c 57 | _cgo_gotypes.go 58 | _cgo_export.* 59 | 60 | _testmain.go 61 | 62 | *.exe 63 | *.test 64 | *.prof 65 | 66 | # tool yacc 67 | *.output 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kenta Mori 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/zoncoen/tq/ast" 8 | "github.com/zoncoen/tq/token" 9 | ) 10 | 11 | var parseTests = []struct { 12 | text string 13 | ast ast.Filter 14 | }{ 15 | {".", ast.EmptyFilter{}}, 16 | {".key", ast.KeyFilter{Key: "key"}}, 17 | {".key?", ast.IgnoreErrorHandler{Filter: ast.KeyFilter{Key: "key"}}}, 18 | {".\"key\"", ast.KeyFilter{Key: "key"}}, 19 | {".[\"key\"]", ast.KeyFilter{Key: "key"}}, 20 | {".[0]", ast.IndexFilter{Index: "0"}}, 21 | {".[0]?", ast.IgnoreErrorHandler{Filter: ast.IndexFilter{Index: "0"}}}, 22 | {".[0:1]", ast.RangeFilter{Low: "0", High: "1"}}, 23 | {".[0:]", ast.RangeFilter{Low: "0", High: ""}}, 24 | {".[:1]", ast.RangeFilter{Low: "", High: "1"}}, 25 | {".[]", ast.RangeFilter{Low: "", High: ""}}, 26 | {". | .", ast.BinaryOp{ 27 | Left: ast.EmptyFilter{}, 28 | Op: token.Token{Token: PIPE, Literal: "|"}, 29 | Right: ast.EmptyFilter{}}}, 30 | {".first.second", ast.BinaryOp{ 31 | Left: ast.KeyFilter{Key: "first"}, 32 | Op: token.Token{Token: PIPE, Literal: "|"}, 33 | Right: ast.KeyFilter{Key: "second"}}}, 34 | {".first.second.third", ast.BinaryOp{ 35 | Left: ast.BinaryOp{ 36 | Left: ast.KeyFilter{Key: "first"}, 37 | Op: token.Token{Token: PIPE, Literal: "|"}, 38 | Right: ast.KeyFilter{Key: "second"}}, 39 | Op: token.Token{Token: PIPE, Literal: "|"}, 40 | Right: ast.KeyFilter{Key: "third"}}}, 41 | {".first, .second", ast.BinaryOp{ 42 | Left: ast.KeyFilter{Key: "first"}, 43 | Op: token.Token{Token: COMMA, Literal: ","}, 44 | Right: ast.KeyFilter{Key: "second"}}}, 45 | } 46 | 47 | func TestParse(t *testing.T) { 48 | for i, test := range parseTests { 49 | r := strings.NewReader(test.text) 50 | res := Parse(r) 51 | if res != test.ast { 52 | t.Errorf("case %d: got %#v; expected %#v", i, res, test.ast) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tq.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | "github.com/BurntSushi/toml" 12 | "github.com/zoncoen/tq/ast" 13 | "github.com/zoncoen/tq/parser" 14 | "github.com/zoncoen/tq/transformer" 15 | ) 16 | 17 | type option struct { 18 | help bool 19 | } 20 | 21 | var opt option 22 | 23 | func init() { 24 | flag.BoolVar(&opt.help, "h", false, "print help message") 25 | flag.BoolVar(&opt.help, "help", false, "print help message") 26 | } 27 | 28 | func write(w io.Writer, t interface{}) error { 29 | if t == nil { 30 | _, err := fmt.Fprint(w, "\n") 31 | if err != nil { 32 | return err 33 | } 34 | } else { 35 | switch t.(type) { 36 | case int64: 37 | _, err := fmt.Fprintf(w, "%d\n", t) 38 | if err != nil { 39 | return err 40 | } 41 | case string: 42 | _, err := fmt.Fprintf(w, "\"%s\"\n", t) 43 | if err != nil { 44 | return err 45 | } 46 | case []map[string]interface{}: 47 | e := toml.NewEncoder(w) 48 | s, _ := t.([]map[string]interface{}) 49 | for _, v := range s { 50 | err := e.Encode(v) 51 | if err != nil { 52 | return err 53 | } 54 | } 55 | case []interface{}: 56 | s, _ := t.([]interface{}) 57 | for _, v := range s { 58 | err := write(w, v) 59 | if err != nil { 60 | return err 61 | } 62 | } 63 | default: 64 | e := toml.NewEncoder(os.Stdout) 65 | err := e.Encode(t) 66 | if err != nil { 67 | return err 68 | } 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | func main() { 75 | flag.Parse() 76 | 77 | if opt.help { 78 | flag.Usage() 79 | os.Exit(0) 80 | } 81 | 82 | var t interface{} 83 | _, err := toml.DecodeReader(os.Stdin, &t) 84 | if err != nil { 85 | log.Fatalf("parse error: %s", err) 86 | } 87 | 88 | var filter ast.Filter 89 | if flag.NArg() > 0 { 90 | str := flag.Arg(0) 91 | r := strings.NewReader(str) 92 | filter = parser.Parse(r) 93 | t, err = transformer.Transform(t, filter) 94 | if err != nil { 95 | log.Fatalf("%s", err) 96 | } 97 | } 98 | 99 | err = write(os.Stdout, t) 100 | if err != nil { 101 | log.Fatalf("%s", err) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /parser/parser.go.y: -------------------------------------------------------------------------------- 1 | %{ 2 | package parser 3 | 4 | import ( 5 | "io" 6 | 7 | "github.com/zoncoen/tq/ast" 8 | "github.com/zoncoen/tq/token" 9 | ) 10 | %} 11 | 12 | %union{ 13 | token token.Token 14 | expr ast.Filter 15 | } 16 | 17 | %type program 18 | %type filter 19 | %type key_filter 20 | %type index_filter 21 | %type range_filter 22 | %type binary_op 23 | 24 | %token PERIOD STRING INT PIPE LBRACK RBRACK COLON COMMA QUESTION 25 | 26 | %left PIPE 27 | 28 | %% 29 | 30 | program 31 | : filter 32 | { 33 | $$ = $1 34 | yylex.(*Lexer).result = $$ 35 | } 36 | 37 | filter 38 | : PERIOD 39 | { 40 | $$ = ast.EmptyFilter{} 41 | } 42 | | key_filter 43 | { 44 | $$ = $1 45 | } 46 | | index_filter 47 | { 48 | $$ = $1 49 | } 50 | | range_filter 51 | { 52 | $$ = $1 53 | } 54 | | binary_op 55 | { 56 | $$ = $1 57 | } 58 | | filter QUESTION 59 | { 60 | $$ = ast.IgnoreErrorHandler{Filter: $1} 61 | } 62 | 63 | key_filter 64 | : PERIOD STRING 65 | { 66 | $$ = ast.KeyFilter{Key: $2.Literal} 67 | } 68 | | PERIOD LBRACK STRING RBRACK 69 | { 70 | $$ = ast.KeyFilter{Key: $3.Literal} 71 | } 72 | 73 | index_filter 74 | : PERIOD LBRACK INT RBRACK 75 | { 76 | $$ = ast.IndexFilter{Index: $3.Literal} 77 | } 78 | 79 | range_filter 80 | : PERIOD LBRACK INT COLON INT RBRACK 81 | { 82 | $$ = ast.RangeFilter{Low: $3.Literal, High: $5.Literal} 83 | } 84 | | PERIOD LBRACK INT COLON RBRACK 85 | { 86 | $$ = ast.RangeFilter{Low: $3.Literal, High: ""} 87 | } 88 | | PERIOD LBRACK COLON INT RBRACK 89 | { 90 | $$ = ast.RangeFilter{Low: "", High: $4.Literal} 91 | } 92 | | PERIOD LBRACK RBRACK 93 | { 94 | $$ = ast.RangeFilter{Low: "", High: ""} 95 | } 96 | 97 | binary_op 98 | : filter PIPE filter 99 | { 100 | $$ = ast.BinaryOp{Left: $1, Op: $2, Right: $3} 101 | } 102 | | filter PERIOD STRING 103 | { 104 | $$ = ast.BinaryOp{Left: $1, Op: token.Token{Token: PIPE, Literal: "|"}, Right: ast.KeyFilter{Key: $3.Literal}} 105 | } 106 | | filter COMMA filter 107 | { 108 | $$ = ast.BinaryOp{Left: $1, Op: $2, Right: $3} 109 | } 110 | %% 111 | 112 | func Parse(r io.Reader) ast.Filter { 113 | l := new(Lexer) 114 | l.Init(r) 115 | yyParse(l) 116 | return l.result 117 | } 118 | -------------------------------------------------------------------------------- /transformer/transformer.go: -------------------------------------------------------------------------------- 1 | package transformer 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | 7 | "github.com/zoncoen/tq/ast" 8 | ) 9 | 10 | func Transform(i interface{}, f ast.Filter) (interface{}, error) { 11 | return Filter(i, f) 12 | } 13 | 14 | func Filter(i interface{}, f ast.Filter) (res interface{}, err error) { 15 | if i == nil { 16 | return nil, nil 17 | } 18 | switch f.(type) { 19 | case ast.EmptyFilter: 20 | res = i 21 | case ast.KeyFilter: 22 | kf, _ := f.(ast.KeyFilter) 23 | res, err = FilterByKey(i, kf) 24 | case ast.IndexFilter: 25 | inf, _ := f.(ast.IndexFilter) 26 | res, err = FilterByIndex(i, inf) 27 | case ast.RangeFilter: 28 | rf, _ := f.(ast.RangeFilter) 29 | res, err = FilterByRange(i, rf) 30 | case ast.BinaryOp: 31 | bo, _ := f.(ast.BinaryOp) 32 | res, err = ExecuteBinaryOp(i, bo) 33 | case ast.IgnoreErrorHandler: 34 | h, _ := f.(ast.IgnoreErrorHandler) 35 | res, err = Filter(i, h.Filter) 36 | if err != nil { 37 | return []map[string]interface{}{}, nil 38 | } 39 | } 40 | return res, err 41 | } 42 | 43 | func FilterByKey(i interface{}, f ast.KeyFilter) (interface{}, error) { 44 | m, ok := i.(map[string]interface{}) 45 | if !ok { 46 | return nil, errors.New("transform error: Object must consist of key:value pairs") 47 | } 48 | v, ok := m[f.Key] 49 | if !ok { 50 | return nil, nil 51 | } 52 | return v, nil 53 | } 54 | 55 | func FilterByIndex(i interface{}, f ast.IndexFilter) (interface{}, error) { 56 | a, ok := i.([]map[string]interface{}) 57 | if !ok { 58 | return nil, errors.New("transform error: Object must consist of array") 59 | } 60 | index, err := strconv.Atoi(f.Index) 61 | if err != nil { 62 | return nil, err 63 | } 64 | if index >= len(a) { 65 | return nil, nil 66 | } 67 | return a[index], nil 68 | } 69 | 70 | func FilterByRange(i interface{}, f ast.RangeFilter) (interface{}, error) { 71 | a, ok := i.([]map[string]interface{}) 72 | if !ok { 73 | return nil, errors.New("transform error: Object must consist of array") 74 | } 75 | 76 | l := 0 77 | h := len(a) + 1 78 | if f.Low != "" { 79 | n, err := strconv.Atoi(f.Low) 80 | if err != nil { 81 | return nil, err 82 | } 83 | if n > l { 84 | l = n 85 | } 86 | } 87 | if f.High != "" { 88 | n, err := strconv.Atoi(f.High) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if n < h { 93 | h = n 94 | } 95 | } 96 | 97 | if l > h { 98 | return []map[string]interface{}{}, nil 99 | } 100 | 101 | return a[l:h], nil 102 | } 103 | 104 | func ExecuteBinaryOp(i interface{}, bo ast.BinaryOp) (res interface{}, err error) { 105 | switch bo.Op.Literal { 106 | case "|": 107 | res, err = Filter(i, bo.Left) 108 | if err != nil { 109 | return res, err 110 | } 111 | res, err = Filter(res, bo.Right) 112 | case ",": 113 | lRes, err := Filter(i, bo.Left) 114 | if err != nil { 115 | return res, err 116 | } 117 | rRes, err := Filter(i, bo.Right) 118 | if err != nil { 119 | return res, err 120 | } 121 | res = []interface{}{lRes, rRes} 122 | } 123 | return res, err 124 | } 125 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | //line parser/parser.go.y:2 2 | package parser 3 | 4 | import __yyfmt__ "fmt" 5 | 6 | //line parser/parser.go.y:2 7 | import ( 8 | "io" 9 | 10 | "github.com/zoncoen/tq/ast" 11 | "github.com/zoncoen/tq/token" 12 | ) 13 | 14 | //line parser/parser.go.y:12 15 | type yySymType struct { 16 | yys int 17 | token token.Token 18 | expr ast.Filter 19 | } 20 | 21 | const PERIOD = 57346 22 | const STRING = 57347 23 | const INT = 57348 24 | const PIPE = 57349 25 | const LBRACK = 57350 26 | const RBRACK = 57351 27 | const COLON = 57352 28 | const COMMA = 57353 29 | const QUESTION = 57354 30 | 31 | var yyToknames = [...]string{ 32 | "$end", 33 | "error", 34 | "$unk", 35 | "PERIOD", 36 | "STRING", 37 | "INT", 38 | "PIPE", 39 | "LBRACK", 40 | "RBRACK", 41 | "COLON", 42 | "COMMA", 43 | "QUESTION", 44 | } 45 | var yyStatenames = [...]string{} 46 | 47 | const yyEofCode = 1 48 | const yyErrCode = 2 49 | const yyMaxDepth = 200 50 | 51 | //line parser/parser.go.y:110 52 | 53 | func Parse(r io.Reader) ast.Filter { 54 | l := new(Lexer) 55 | l.Init(r) 56 | yyParse(l) 57 | return l.result 58 | } 59 | 60 | //line yacctab:1 61 | var yyExca = [...]int{ 62 | -1, 1, 63 | 1, -1, 64 | -2, 0, 65 | } 66 | 67 | const yyNprod = 18 68 | const yyPrivate = 57344 69 | 70 | var yyTokenNames []string 71 | var yyStates []string 72 | 73 | const yyLast = 31 74 | 75 | var yyAct = [...]int{ 76 | 77 | 10, 28, 10, 9, 22, 23, 27, 11, 8, 11, 78 | 8, 17, 18, 2, 25, 20, 19, 26, 21, 12, 79 | 3, 24, 13, 14, 15, 16, 7, 6, 5, 4, 80 | 1, 81 | } 82 | var yyPact = [...]int{ 83 | 84 | 16, -1000, -4, 14, -1000, -1000, -1000, -1000, -1000, 16, 85 | 19, 16, -1000, 6, -2, -1000, -4, 9, -5, 15, 86 | -1000, -1000, -1000, 8, -3, -8, -1000, -1000, -1000, 87 | } 88 | var yyPgo = [...]int{ 89 | 90 | 0, 30, 13, 29, 28, 27, 26, 91 | } 92 | var yyR1 = [...]int{ 93 | 94 | 0, 1, 2, 2, 2, 2, 2, 2, 3, 3, 95 | 4, 5, 5, 5, 5, 6, 6, 6, 96 | } 97 | var yyR2 = [...]int{ 98 | 99 | 0, 1, 1, 1, 1, 1, 1, 2, 2, 4, 100 | 4, 6, 5, 5, 3, 3, 3, 3, 101 | } 102 | var yyChk = [...]int{ 103 | 104 | -1000, -1, -2, 4, -3, -4, -5, -6, 12, 7, 105 | 4, 11, 5, 8, -2, 5, -2, 5, 6, 10, 106 | 9, 9, 9, 10, 6, 6, 9, 9, 9, 107 | } 108 | var yyDef = [...]int{ 109 | 110 | 0, -2, 1, 2, 3, 4, 5, 6, 7, 0, 111 | 0, 0, 8, 0, 15, 16, 17, 0, 0, 0, 112 | 14, 9, 10, 0, 0, 0, 12, 13, 11, 113 | } 114 | var yyTok1 = [...]int{ 115 | 116 | 1, 117 | } 118 | var yyTok2 = [...]int{ 119 | 120 | 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 121 | 12, 122 | } 123 | var yyTok3 = [...]int{ 124 | 0, 125 | } 126 | 127 | var yyErrorMessages = [...]struct { 128 | state int 129 | token int 130 | msg string 131 | }{} 132 | 133 | //line yaccpar:1 134 | 135 | /* parser for yacc output */ 136 | 137 | var ( 138 | yyDebug = 0 139 | yyErrorVerbose = false 140 | ) 141 | 142 | type yyLexer interface { 143 | Lex(lval *yySymType) int 144 | Error(s string) 145 | } 146 | 147 | type yyParser interface { 148 | Parse(yyLexer) int 149 | Lookahead() int 150 | } 151 | 152 | type yyParserImpl struct { 153 | lookahead func() int 154 | } 155 | 156 | func (p *yyParserImpl) Lookahead() int { 157 | return p.lookahead() 158 | } 159 | 160 | func yyNewParser() yyParser { 161 | p := &yyParserImpl{ 162 | lookahead: func() int { return -1 }, 163 | } 164 | return p 165 | } 166 | 167 | const yyFlag = -1000 168 | 169 | func yyTokname(c int) string { 170 | if c >= 1 && c-1 < len(yyToknames) { 171 | if yyToknames[c-1] != "" { 172 | return yyToknames[c-1] 173 | } 174 | } 175 | return __yyfmt__.Sprintf("tok-%v", c) 176 | } 177 | 178 | func yyStatname(s int) string { 179 | if s >= 0 && s < len(yyStatenames) { 180 | if yyStatenames[s] != "" { 181 | return yyStatenames[s] 182 | } 183 | } 184 | return __yyfmt__.Sprintf("state-%v", s) 185 | } 186 | 187 | func yyErrorMessage(state, lookAhead int) string { 188 | const TOKSTART = 4 189 | 190 | if !yyErrorVerbose { 191 | return "syntax error" 192 | } 193 | 194 | for _, e := range yyErrorMessages { 195 | if e.state == state && e.token == lookAhead { 196 | return "syntax error: " + e.msg 197 | } 198 | } 199 | 200 | res := "syntax error: unexpected " + yyTokname(lookAhead) 201 | 202 | // To match Bison, suggest at most four expected tokens. 203 | expected := make([]int, 0, 4) 204 | 205 | // Look for shiftable tokens. 206 | base := yyPact[state] 207 | for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { 208 | if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { 209 | if len(expected) == cap(expected) { 210 | return res 211 | } 212 | expected = append(expected, tok) 213 | } 214 | } 215 | 216 | if yyDef[state] == -2 { 217 | i := 0 218 | for yyExca[i] != -1 || yyExca[i+1] != state { 219 | i += 2 220 | } 221 | 222 | // Look for tokens that we accept or reduce. 223 | for i += 2; yyExca[i] >= 0; i += 2 { 224 | tok := yyExca[i] 225 | if tok < TOKSTART || yyExca[i+1] == 0 { 226 | continue 227 | } 228 | if len(expected) == cap(expected) { 229 | return res 230 | } 231 | expected = append(expected, tok) 232 | } 233 | 234 | // If the default action is to accept or reduce, give up. 235 | if yyExca[i+1] != 0 { 236 | return res 237 | } 238 | } 239 | 240 | for i, tok := range expected { 241 | if i == 0 { 242 | res += ", expecting " 243 | } else { 244 | res += " or " 245 | } 246 | res += yyTokname(tok) 247 | } 248 | return res 249 | } 250 | 251 | func yylex1(lex yyLexer, lval *yySymType) (char, token int) { 252 | token = 0 253 | char = lex.Lex(lval) 254 | if char <= 0 { 255 | token = yyTok1[0] 256 | goto out 257 | } 258 | if char < len(yyTok1) { 259 | token = yyTok1[char] 260 | goto out 261 | } 262 | if char >= yyPrivate { 263 | if char < yyPrivate+len(yyTok2) { 264 | token = yyTok2[char-yyPrivate] 265 | goto out 266 | } 267 | } 268 | for i := 0; i < len(yyTok3); i += 2 { 269 | token = yyTok3[i+0] 270 | if token == char { 271 | token = yyTok3[i+1] 272 | goto out 273 | } 274 | } 275 | 276 | out: 277 | if token == 0 { 278 | token = yyTok2[1] /* unknown char */ 279 | } 280 | if yyDebug >= 3 { 281 | __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) 282 | } 283 | return char, token 284 | } 285 | 286 | func yyParse(yylex yyLexer) int { 287 | return yyNewParser().Parse(yylex) 288 | } 289 | 290 | func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { 291 | var yyn int 292 | var yylval yySymType 293 | var yyVAL yySymType 294 | var yyDollar []yySymType 295 | _ = yyDollar // silence set and not used 296 | yyS := make([]yySymType, yyMaxDepth) 297 | 298 | Nerrs := 0 /* number of errors */ 299 | Errflag := 0 /* error recovery flag */ 300 | yystate := 0 301 | yychar := -1 302 | yytoken := -1 // yychar translated into internal numbering 303 | yyrcvr.lookahead = func() int { return yychar } 304 | defer func() { 305 | // Make sure we report no lookahead when not parsing. 306 | yystate = -1 307 | yychar = -1 308 | yytoken = -1 309 | }() 310 | yyp := -1 311 | goto yystack 312 | 313 | ret0: 314 | return 0 315 | 316 | ret1: 317 | return 1 318 | 319 | yystack: 320 | /* put a state and value onto the stack */ 321 | if yyDebug >= 4 { 322 | __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) 323 | } 324 | 325 | yyp++ 326 | if yyp >= len(yyS) { 327 | nyys := make([]yySymType, len(yyS)*2) 328 | copy(nyys, yyS) 329 | yyS = nyys 330 | } 331 | yyS[yyp] = yyVAL 332 | yyS[yyp].yys = yystate 333 | 334 | yynewstate: 335 | yyn = yyPact[yystate] 336 | if yyn <= yyFlag { 337 | goto yydefault /* simple state */ 338 | } 339 | if yychar < 0 { 340 | yychar, yytoken = yylex1(yylex, &yylval) 341 | } 342 | yyn += yytoken 343 | if yyn < 0 || yyn >= yyLast { 344 | goto yydefault 345 | } 346 | yyn = yyAct[yyn] 347 | if yyChk[yyn] == yytoken { /* valid shift */ 348 | yychar = -1 349 | yytoken = -1 350 | yyVAL = yylval 351 | yystate = yyn 352 | if Errflag > 0 { 353 | Errflag-- 354 | } 355 | goto yystack 356 | } 357 | 358 | yydefault: 359 | /* default state action */ 360 | yyn = yyDef[yystate] 361 | if yyn == -2 { 362 | if yychar < 0 { 363 | yychar, yytoken = yylex1(yylex, &yylval) 364 | } 365 | 366 | /* look through exception table */ 367 | xi := 0 368 | for { 369 | if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { 370 | break 371 | } 372 | xi += 2 373 | } 374 | for xi += 2; ; xi += 2 { 375 | yyn = yyExca[xi+0] 376 | if yyn < 0 || yyn == yytoken { 377 | break 378 | } 379 | } 380 | yyn = yyExca[xi+1] 381 | if yyn < 0 { 382 | goto ret0 383 | } 384 | } 385 | if yyn == 0 { 386 | /* error ... attempt to resume parsing */ 387 | switch Errflag { 388 | case 0: /* brand new error */ 389 | yylex.Error(yyErrorMessage(yystate, yytoken)) 390 | Nerrs++ 391 | if yyDebug >= 1 { 392 | __yyfmt__.Printf("%s", yyStatname(yystate)) 393 | __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) 394 | } 395 | fallthrough 396 | 397 | case 1, 2: /* incompletely recovered error ... try again */ 398 | Errflag = 3 399 | 400 | /* find a state where "error" is a legal shift action */ 401 | for yyp >= 0 { 402 | yyn = yyPact[yyS[yyp].yys] + yyErrCode 403 | if yyn >= 0 && yyn < yyLast { 404 | yystate = yyAct[yyn] /* simulate a shift of "error" */ 405 | if yyChk[yystate] == yyErrCode { 406 | goto yystack 407 | } 408 | } 409 | 410 | /* the current p has no shift on "error", pop stack */ 411 | if yyDebug >= 2 { 412 | __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) 413 | } 414 | yyp-- 415 | } 416 | /* there is no state on the stack with an error shift ... abort */ 417 | goto ret1 418 | 419 | case 3: /* no shift yet; clobber input char */ 420 | if yyDebug >= 2 { 421 | __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) 422 | } 423 | if yytoken == yyEofCode { 424 | goto ret1 425 | } 426 | yychar = -1 427 | yytoken = -1 428 | goto yynewstate /* try again in the same state */ 429 | } 430 | } 431 | 432 | /* reduction by production yyn */ 433 | if yyDebug >= 2 { 434 | __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) 435 | } 436 | 437 | yynt := yyn 438 | yypt := yyp 439 | _ = yypt // guard against "declared and not used" 440 | 441 | yyp -= yyR2[yyn] 442 | // yyp is now the index of $0. Perform the default action. Iff the 443 | // reduced production is ε, $1 is possibly out of range. 444 | if yyp+1 >= len(yyS) { 445 | nyys := make([]yySymType, len(yyS)*2) 446 | copy(nyys, yyS) 447 | yyS = nyys 448 | } 449 | yyVAL = yyS[yyp+1] 450 | 451 | /* consult goto table to find next state */ 452 | yyn = yyR1[yyn] 453 | yyg := yyPgo[yyn] 454 | yyj := yyg + yyS[yyp].yys + 1 455 | 456 | if yyj >= yyLast { 457 | yystate = yyAct[yyg] 458 | } else { 459 | yystate = yyAct[yyj] 460 | if yyChk[yystate] != -yyn { 461 | yystate = yyAct[yyg] 462 | } 463 | } 464 | // dummy call; replaced with literal code 465 | switch yynt { 466 | 467 | case 1: 468 | yyDollar = yyS[yypt-1 : yypt+1] 469 | //line parser/parser.go.y:32 470 | { 471 | yyVAL.expr = yyDollar[1].expr 472 | yylex.(*Lexer).result = yyVAL.expr 473 | } 474 | case 2: 475 | yyDollar = yyS[yypt-1 : yypt+1] 476 | //line parser/parser.go.y:39 477 | { 478 | yyVAL.expr = ast.EmptyFilter{} 479 | } 480 | case 3: 481 | yyDollar = yyS[yypt-1 : yypt+1] 482 | //line parser/parser.go.y:43 483 | { 484 | yyVAL.expr = yyDollar[1].expr 485 | } 486 | case 4: 487 | yyDollar = yyS[yypt-1 : yypt+1] 488 | //line parser/parser.go.y:47 489 | { 490 | yyVAL.expr = yyDollar[1].expr 491 | } 492 | case 5: 493 | yyDollar = yyS[yypt-1 : yypt+1] 494 | //line parser/parser.go.y:51 495 | { 496 | yyVAL.expr = yyDollar[1].expr 497 | } 498 | case 6: 499 | yyDollar = yyS[yypt-1 : yypt+1] 500 | //line parser/parser.go.y:55 501 | { 502 | yyVAL.expr = yyDollar[1].expr 503 | } 504 | case 7: 505 | yyDollar = yyS[yypt-2 : yypt+1] 506 | //line parser/parser.go.y:59 507 | { 508 | yyVAL.expr = ast.IgnoreErrorHandler{Filter: yyDollar[1].expr} 509 | } 510 | case 8: 511 | yyDollar = yyS[yypt-2 : yypt+1] 512 | //line parser/parser.go.y:65 513 | { 514 | yyVAL.expr = ast.KeyFilter{Key: yyDollar[2].token.Literal} 515 | } 516 | case 9: 517 | yyDollar = yyS[yypt-4 : yypt+1] 518 | //line parser/parser.go.y:69 519 | { 520 | yyVAL.expr = ast.KeyFilter{Key: yyDollar[3].token.Literal} 521 | } 522 | case 10: 523 | yyDollar = yyS[yypt-4 : yypt+1] 524 | //line parser/parser.go.y:75 525 | { 526 | yyVAL.expr = ast.IndexFilter{Index: yyDollar[3].token.Literal} 527 | } 528 | case 11: 529 | yyDollar = yyS[yypt-6 : yypt+1] 530 | //line parser/parser.go.y:81 531 | { 532 | yyVAL.expr = ast.RangeFilter{Low: yyDollar[3].token.Literal, High: yyDollar[5].token.Literal} 533 | } 534 | case 12: 535 | yyDollar = yyS[yypt-5 : yypt+1] 536 | //line parser/parser.go.y:85 537 | { 538 | yyVAL.expr = ast.RangeFilter{Low: yyDollar[3].token.Literal, High: ""} 539 | } 540 | case 13: 541 | yyDollar = yyS[yypt-5 : yypt+1] 542 | //line parser/parser.go.y:89 543 | { 544 | yyVAL.expr = ast.RangeFilter{Low: "", High: yyDollar[4].token.Literal} 545 | } 546 | case 14: 547 | yyDollar = yyS[yypt-3 : yypt+1] 548 | //line parser/parser.go.y:93 549 | { 550 | yyVAL.expr = ast.RangeFilter{Low: "", High: ""} 551 | } 552 | case 15: 553 | yyDollar = yyS[yypt-3 : yypt+1] 554 | //line parser/parser.go.y:99 555 | { 556 | yyVAL.expr = ast.BinaryOp{Left: yyDollar[1].expr, Op: yyDollar[2].token, Right: yyDollar[3].expr} 557 | } 558 | case 16: 559 | yyDollar = yyS[yypt-3 : yypt+1] 560 | //line parser/parser.go.y:103 561 | { 562 | yyVAL.expr = ast.BinaryOp{Left: yyDollar[1].expr, Op: token.Token{Token: PIPE, Literal: "|"}, Right: ast.KeyFilter{Key: yyDollar[3].token.Literal}} 563 | } 564 | case 17: 565 | yyDollar = yyS[yypt-3 : yypt+1] 566 | //line parser/parser.go.y:107 567 | { 568 | yyVAL.expr = ast.BinaryOp{Left: yyDollar[1].expr, Op: yyDollar[2].token, Right: yyDollar[3].expr} 569 | } 570 | } 571 | goto yystack /* stack new state and value */ 572 | } 573 | --------------------------------------------------------------------------------