bash - ast_parser.go
1 package bash
2
3 import (
4 "fmt"
5
6 "vimagination.zapto.org/parser"
7 )
8
9 // Token represents a parser.Token combined with positioning information.
10 type Token struct {
11 parser.Token
12 Pos, Line, LinePos uint64
13 }
14
15 // Tokens represents a list ok tokens that have been parsed.
16 type Tokens []Token
17
18 type bashParser struct {
19 Tokens
20 StopAt *parser.Token
21 }
22
23 // Tokeniser represents the methods required by the bash tokeniser.
24 type Tokeniser interface {
25 Iter(func(parser.Token) bool)
26 TokeniserState(parser.TokenFunc)
27 GetError() error
28 }
29
30 func newBashParser(t Tokeniser) (*bashParser, error) {
31 b := new(bashTokeniser)
32
33 t.TokeniserState(b.main)
34
35 var (
36 tokens Tokens
37 err error
38 pos, line, linePos uint64
39 )
40
41 for tk := range t.Iter {
42 tokens = append(tokens, Token{Token: tk, Pos: pos, Line: line, LinePos: linePos})
43
44 switch tk.Type {
45 case parser.TokenDone:
46 case parser.TokenError:
47 err = Error{Err: t.GetError(), Parsing: "Tokens", Token: tokens[len(tokens)-1]}
48 case TokenLineTerminator:
49 line += uint64(len(tk.Data))
50 linePos = 0
51 default:
52 for _, c := range tk.Data {
53 if c == '\n' {
54 line++
55 linePos = 0
56 } else {
57 linePos++
58 }
59 }
60
61 linePos += uint64(len(tk.Data))
62 }
63
64 pos += uint64(len(tk.Data))
65 }
66
67 return &bashParser{Tokens: tokens[0:0:len(tokens)]}, err
68 }
69
70 func (b bashParser) NewGoal() *bashParser {
71 return &bashParser{
72 Tokens: b.Tokens[len(b.Tokens):],
73 StopAt: b.StopAt,
74 }
75 }
76
77 func (b *bashParser) Score(k *bashParser) {
78 b.Tokens = b.Tokens[:len(b.Tokens)+len(k.Tokens)]
79 }
80
81 func (b *bashParser) next() Token {
82 l := len(b.Tokens)
83 b.Tokens = b.Tokens[:l+1]
84 tk := b.Tokens[l]
85
86 if b.StopAt != nil && *b.StopAt == tk.Token {
87 return Token{Token: parser.Token{Type: parser.TokenDone}}
88 }
89
90 return tk
91 }
92
93 func (b *bashParser) backup() {
94 b.Tokens = b.Tokens[:len(b.Tokens)-1]
95 }
96
97 func (b *bashParser) Peek() parser.Token {
98 tk := b.next().Token
99
100 b.backup()
101
102 return tk
103 }
104
105 func (b *bashParser) Accept(ts ...parser.TokenType) bool {
106 tt := b.next().Type
107
108 for _, pt := range ts {
109 if pt == tt {
110 return true
111 }
112 }
113
114 b.backup()
115
116 return false
117 }
118
119 func (b *bashParser) AcceptRun(ts ...parser.TokenType) parser.TokenType {
120 Loop:
121 for {
122 tt := b.next().Type
123
124 for _, pt := range ts {
125 if pt == tt {
126 continue Loop
127 }
128 }
129
130 b.backup()
131
132 return tt
133 }
134 }
135
136 func (b *bashParser) Next() Token {
137 return b.next()
138 }
139
140 func (b *bashParser) AcceptToken(tk parser.Token) bool {
141 if b.next().Token == tk {
142 return true
143 }
144
145 b.backup()
146
147 return false
148 }
149
150 func (b *bashParser) ToTokens() Tokens {
151 return b.Tokens[:len(b.Tokens):len(b.Tokens)]
152 }
153
154 func (b *bashParser) GetLastToken() *Token {
155 return &b.Tokens[len(b.Tokens)-1]
156 }
157
158 func (b *bashParser) AcceptRunWhitespace() parser.TokenType {
159 return b.AcceptRun(TokenWhitespace)
160 }
161
162 func (p *bashParser) AcceptRunAllWhitespace() parser.TokenType {
163 return p.AcceptRun(TokenWhitespace, TokenComment, TokenLineTerminator)
164 }
165
166 // Error represents a Bash parsing error.
167 type Error struct {
168 Err error
169 Parsing string
170 Token Token
171 }
172
173 // Error implements the error interface.
174 func (e Error) Error() string {
175 return fmt.Sprintf("%s: error at position %d (%d:%d):\n%s", e.Parsing, e.Token.Pos+1, e.Token.Line+1, e.Token.LinePos+1, e.Err)
176 }
177
178 // Unwrap returns the underlying error.
179 func (e Error) Unwrap() error {
180 return e.Err
181 }
182
183 func (b *bashParser) Error(parsingFunc string, err error) error {
184 tk := b.next()
185
186 b.backup()
187
188 return Error{
189 Err: err,
190 Parsing: parsingFunc,
191 Token: tk,
192 }
193 }
194