github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/flosch/pongo2.v3/parser.go (about) 1 package pongo2 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 ) 8 9 type INode interface { 10 Execute(*ExecutionContext, *bytes.Buffer) *Error 11 } 12 13 type IEvaluator interface { 14 INode 15 GetPositionToken() *Token 16 Evaluate(*ExecutionContext) (*Value, *Error) 17 FilterApplied(name string) bool 18 } 19 20 // The parser provides you a comprehensive and easy tool to 21 // work with the template document and arguments provided by 22 // the user for your custom tag. 23 // 24 // The parser works on a token list which will be provided by pongo2. 25 // A token is a unit you can work with. Tokens are either of type identifier, 26 // string, number, keyword, HTML or symbol. 27 // 28 // (See Token's documentation for more about tokens) 29 type Parser struct { 30 name string 31 idx int 32 tokens []*Token 33 last_token *Token 34 35 // if the parser parses a template document, here will be 36 // a reference to it (needed to access the template through Tags) 37 template *Template 38 } 39 40 // Creates a new parser to parse tokens. 41 // Used inside pongo2 to parse documents and to provide an easy-to-use 42 // parser for tag authors 43 func newParser(name string, tokens []*Token, template *Template) *Parser { 44 p := &Parser{ 45 name: name, 46 tokens: tokens, 47 template: template, 48 } 49 if len(tokens) > 0 { 50 p.last_token = tokens[len(tokens)-1] 51 } 52 return p 53 } 54 55 // Consume one token. It will be gone forever. 56 func (p *Parser) Consume() { 57 p.ConsumeN(1) 58 } 59 60 // Consume N tokens. They will be gone forever. 61 func (p *Parser) ConsumeN(count int) { 62 p.idx += count 63 } 64 65 // Returns the current token. 66 func (p *Parser) Current() *Token { 67 return p.Get(p.idx) 68 } 69 70 // Returns the CURRENT token if the given type matches. 71 // Consumes this token on success. 72 func (p *Parser) MatchType(typ TokenType) *Token { 73 if t := p.PeekType(typ); t != nil { 74 p.Consume() 75 return t 76 } 77 return nil 78 } 79 80 // Returns the CURRENT token if the given type AND value matches. 81 // Consumes this token on success. 82 func (p *Parser) Match(typ TokenType, val string) *Token { 83 if t := p.Peek(typ, val); t != nil { 84 p.Consume() 85 return t 86 } 87 return nil 88 } 89 90 // Returns the CURRENT token if the given type AND *one* of 91 // the given values matches. 92 // Consumes this token on success. 93 func (p *Parser) MatchOne(typ TokenType, vals ...string) *Token { 94 for _, val := range vals { 95 if t := p.Peek(typ, val); t != nil { 96 p.Consume() 97 return t 98 } 99 } 100 return nil 101 } 102 103 // Returns the CURRENT token if the given type matches. 104 // It DOES NOT consume the token. 105 func (p *Parser) PeekType(typ TokenType) *Token { 106 return p.PeekTypeN(0, typ) 107 } 108 109 // Returns the CURRENT token if the given type AND value matches. 110 // It DOES NOT consume the token. 111 func (p *Parser) Peek(typ TokenType, val string) *Token { 112 return p.PeekN(0, typ, val) 113 } 114 115 // Returns the CURRENT token if the given type AND *one* of 116 // the given values matches. 117 // It DOES NOT consume the token. 118 func (p *Parser) PeekOne(typ TokenType, vals ...string) *Token { 119 for _, v := range vals { 120 t := p.PeekN(0, typ, v) 121 if t != nil { 122 return t 123 } 124 } 125 return nil 126 } 127 128 // Returns the tokens[current position + shift] token if the 129 // given type AND value matches for that token. 130 // DOES NOT consume the token. 131 func (p *Parser) PeekN(shift int, typ TokenType, val string) *Token { 132 t := p.Get(p.idx + shift) 133 if t != nil { 134 if t.Typ == typ && t.Val == val { 135 return t 136 } 137 } 138 return nil 139 } 140 141 // Returns the tokens[current position + shift] token if the given type matches. 142 // DOES NOT consume the token for that token. 143 func (p *Parser) PeekTypeN(shift int, typ TokenType) *Token { 144 t := p.Get(p.idx + shift) 145 if t != nil { 146 if t.Typ == typ { 147 return t 148 } 149 } 150 return nil 151 } 152 153 // Returns the UNCONSUMED token count. 154 func (p *Parser) Remaining() int { 155 return len(p.tokens) - p.idx 156 } 157 158 // Returns the total token count. 159 func (p *Parser) Count() int { 160 return len(p.tokens) 161 } 162 163 // Returns tokens[i] or NIL (if i >= len(tokens)) 164 func (p *Parser) Get(i int) *Token { 165 if i < len(p.tokens) { 166 return p.tokens[i] 167 } 168 return nil 169 } 170 171 // Returns tokens[current-position + shift] or NIL 172 // (if (current-position + i) >= len(tokens)) 173 func (p *Parser) GetR(shift int) *Token { 174 i := p.idx + shift 175 return p.Get(i) 176 } 177 178 // Produces a nice error message and returns an error-object. 179 // The 'token'-argument is optional. If provided, it will take 180 // the token's position information. If not provided, it will 181 // automatically use the CURRENT token's position information. 182 func (p *Parser) Error(msg string, token *Token) *Error { 183 if token == nil { 184 // Set current token 185 token = p.Current() 186 if token == nil { 187 // Set to last token 188 if len(p.tokens) > 0 { 189 token = p.tokens[len(p.tokens)-1] 190 } 191 } 192 } 193 var line, col int 194 if token != nil { 195 line = token.Line 196 col = token.Col 197 } 198 return &Error{ 199 Template: p.template, 200 Filename: p.name, 201 Sender: "parser", 202 Line: line, 203 Column: col, 204 Token: token, 205 ErrorMsg: msg, 206 } 207 } 208 209 // Wraps all nodes between starting tag and "{% endtag %}" and provides 210 // one simple interface to execute the wrapped nodes. 211 // It returns a parser to process provided arguments to the tag. 212 func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) { 213 wrapper := &NodeWrapper{} 214 215 tagArgs := make([]*Token, 0) 216 217 for p.Remaining() > 0 { 218 // New tag, check whether we have to stop wrapping here 219 if p.Peek(TokenSymbol, "{%") != nil { 220 tag_ident := p.PeekTypeN(1, TokenIdentifier) 221 222 if tag_ident != nil { 223 // We've found a (!) end-tag 224 225 found := false 226 for _, n := range names { 227 if tag_ident.Val == n { 228 found = true 229 break 230 } 231 } 232 233 // We only process the tag if we've found an end tag 234 if found { 235 // Okay, endtag found. 236 p.ConsumeN(2) // '{%' tagname 237 238 for { 239 if p.Match(TokenSymbol, "%}") != nil { 240 // Okay, end the wrapping here 241 wrapper.Endtag = tag_ident.Val 242 return wrapper, newParser(p.template.name, tagArgs, p.template), nil 243 } else { 244 t := p.Current() 245 p.Consume() 246 if t == nil { 247 return nil, nil, p.Error("Unexpected EOF.", p.last_token) 248 } 249 tagArgs = append(tagArgs, t) 250 } 251 } 252 } 253 } 254 255 } 256 257 // Otherwise process next element to be wrapped 258 node, err := p.parseDocElement() 259 if err != nil { 260 return nil, nil, err 261 } 262 wrapper.nodes = append(wrapper.nodes, node) 263 } 264 265 return nil, nil, p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")), 266 p.last_token) 267 }