| /* | 
 |   Based on the "jsonpath" spec/concept. | 
 |  | 
 |   http://goessner.net/articles/JsonPath/ | 
 |   https://code.google.com/p/json-path/ | 
 | */ | 
 |  | 
 | package toml | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | ) | 
 |  | 
 | const MaxInt = int(^uint(0) >> 1) | 
 |  | 
 | type queryParser struct { | 
 | 	flow         chan token | 
 | 	tokensBuffer []token | 
 | 	query        *Query | 
 | 	union        []pathFn | 
 | 	err          error | 
 | } | 
 |  | 
 | type queryParserStateFn func() queryParserStateFn | 
 |  | 
 | // Formats and panics an error message based on a token | 
 | func (p *queryParser) parseError(tok *token, msg string, args ...interface{}) queryParserStateFn { | 
 | 	p.err = fmt.Errorf(tok.Position.String()+": "+msg, args...) | 
 | 	return nil // trigger parse to end | 
 | } | 
 |  | 
 | func (p *queryParser) run() { | 
 | 	for state := p.parseStart; state != nil; { | 
 | 		state = state() | 
 | 	} | 
 | } | 
 |  | 
 | func (p *queryParser) backup(tok *token) { | 
 | 	p.tokensBuffer = append(p.tokensBuffer, *tok) | 
 | } | 
 |  | 
 | func (p *queryParser) peek() *token { | 
 | 	if len(p.tokensBuffer) != 0 { | 
 | 		return &(p.tokensBuffer[0]) | 
 | 	} | 
 |  | 
 | 	tok, ok := <-p.flow | 
 | 	if !ok { | 
 | 		return nil | 
 | 	} | 
 | 	p.backup(&tok) | 
 | 	return &tok | 
 | } | 
 |  | 
 | func (p *queryParser) lookahead(types ...tokenType) bool { | 
 | 	result := true | 
 | 	buffer := []token{} | 
 |  | 
 | 	for _, typ := range types { | 
 | 		tok := p.getToken() | 
 | 		if tok == nil { | 
 | 			result = false | 
 | 			break | 
 | 		} | 
 | 		buffer = append(buffer, *tok) | 
 | 		if tok.typ != typ { | 
 | 			result = false | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	// add the tokens back to the buffer, and return | 
 | 	p.tokensBuffer = append(p.tokensBuffer, buffer...) | 
 | 	return result | 
 | } | 
 |  | 
 | func (p *queryParser) getToken() *token { | 
 | 	if len(p.tokensBuffer) != 0 { | 
 | 		tok := p.tokensBuffer[0] | 
 | 		p.tokensBuffer = p.tokensBuffer[1:] | 
 | 		return &tok | 
 | 	} | 
 | 	tok, ok := <-p.flow | 
 | 	if !ok { | 
 | 		return nil | 
 | 	} | 
 | 	return &tok | 
 | } | 
 |  | 
 | func (p *queryParser) parseStart() queryParserStateFn { | 
 | 	tok := p.getToken() | 
 |  | 
 | 	if tok == nil || tok.typ == tokenEOF { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	if tok.typ != tokenDollar { | 
 | 		return p.parseError(tok, "Expected '$' at start of expression") | 
 | 	} | 
 |  | 
 | 	return p.parseMatchExpr | 
 | } | 
 |  | 
 | // handle '.' prefix, '[]', and '..' | 
 | func (p *queryParser) parseMatchExpr() queryParserStateFn { | 
 | 	tok := p.getToken() | 
 | 	switch tok.typ { | 
 | 	case tokenDotDot: | 
 | 		p.query.appendPath(&matchRecursiveFn{}) | 
 | 		// nested parse for '..' | 
 | 		tok := p.getToken() | 
 | 		switch tok.typ { | 
 | 		case tokenKey: | 
 | 			p.query.appendPath(newMatchKeyFn(tok.val)) | 
 | 			return p.parseMatchExpr | 
 | 		case tokenLeftBracket: | 
 | 			return p.parseBracketExpr | 
 | 		case tokenStar: | 
 | 			// do nothing - the recursive predicate is enough | 
 | 			return p.parseMatchExpr | 
 | 		} | 
 |  | 
 | 	case tokenDot: | 
 | 		// nested parse for '.' | 
 | 		tok := p.getToken() | 
 | 		switch tok.typ { | 
 | 		case tokenKey: | 
 | 			p.query.appendPath(newMatchKeyFn(tok.val)) | 
 | 			return p.parseMatchExpr | 
 | 		case tokenStar: | 
 | 			p.query.appendPath(&matchAnyFn{}) | 
 | 			return p.parseMatchExpr | 
 | 		} | 
 |  | 
 | 	case tokenLeftBracket: | 
 | 		return p.parseBracketExpr | 
 |  | 
 | 	case tokenEOF: | 
 | 		return nil // allow EOF at this stage | 
 | 	} | 
 | 	return p.parseError(tok, "expected match expression") | 
 | 	return nil | 
 | } | 
 |  | 
 | func (p *queryParser) parseBracketExpr() queryParserStateFn { | 
 | 	if p.lookahead(tokenInteger, tokenColon) { | 
 | 		return p.parseSliceExpr | 
 | 	} | 
 | 	if p.peek().typ == tokenColon { | 
 | 		return p.parseSliceExpr | 
 | 	} | 
 | 	return p.parseUnionExpr | 
 | } | 
 |  | 
 | func (p *queryParser) parseUnionExpr() queryParserStateFn { | 
 | 	var tok *token | 
 |  | 
 | 	// this state can be traversed after some sub-expressions | 
 | 	// so be careful when setting up state in the parser | 
 | 	if p.union == nil { | 
 | 		p.union = []pathFn{} | 
 | 	} | 
 |  | 
 | loop: // labeled loop for easy breaking | 
 | 	for { | 
 | 		if len(p.union) > 0 { | 
 | 			// parse delimiter or terminator | 
 | 			tok = p.getToken() | 
 | 			switch tok.typ { | 
 | 			case tokenComma: | 
 | 				// do nothing | 
 | 			case tokenRightBracket: | 
 | 				break loop | 
 | 			default: | 
 | 				return p.parseError(tok, "expected ',' or ']', not '%s'", tok.val) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		// parse sub expression | 
 | 		tok = p.getToken() | 
 | 		switch tok.typ { | 
 | 		case tokenInteger: | 
 | 			p.union = append(p.union, newMatchIndexFn(tok.Int())) | 
 | 		case tokenKey: | 
 | 			p.union = append(p.union, newMatchKeyFn(tok.val)) | 
 | 		case tokenString: | 
 | 			p.union = append(p.union, newMatchKeyFn(tok.val)) | 
 | 		case tokenQuestion: | 
 | 			return p.parseFilterExpr | 
 | 		default: | 
 | 			return p.parseError(tok, "expected union sub expression, not '%s', %d", tok.val, len(p.union)) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// if there is only one sub-expression, use that instead | 
 | 	if len(p.union) == 1 { | 
 | 		p.query.appendPath(p.union[0]) | 
 | 	} else { | 
 | 		p.query.appendPath(&matchUnionFn{p.union}) | 
 | 	} | 
 |  | 
 | 	p.union = nil // clear out state | 
 | 	return p.parseMatchExpr | 
 | } | 
 |  | 
 | func (p *queryParser) parseSliceExpr() queryParserStateFn { | 
 | 	// init slice to grab all elements | 
 | 	start, end, step := 0, MaxInt, 1 | 
 |  | 
 | 	// parse optional start | 
 | 	tok := p.getToken() | 
 | 	if tok.typ == tokenInteger { | 
 | 		start = tok.Int() | 
 | 		tok = p.getToken() | 
 | 	} | 
 | 	if tok.typ != tokenColon { | 
 | 		return p.parseError(tok, "expected ':'") | 
 | 	} | 
 |  | 
 | 	// parse optional end | 
 | 	tok = p.getToken() | 
 | 	if tok.typ == tokenInteger { | 
 | 		end = tok.Int() | 
 | 		tok = p.getToken() | 
 | 	} | 
 | 	if tok.typ == tokenRightBracket { | 
 | 		p.query.appendPath(newMatchSliceFn(start, end, step)) | 
 | 		return p.parseMatchExpr | 
 | 	} | 
 | 	if tok.typ != tokenColon { | 
 | 		return p.parseError(tok, "expected ']' or ':'") | 
 | 	} | 
 |  | 
 | 	// parse optional step | 
 | 	tok = p.getToken() | 
 | 	if tok.typ == tokenInteger { | 
 | 		step = tok.Int() | 
 | 		if step < 0 { | 
 | 			return p.parseError(tok, "step must be a positive value") | 
 | 		} | 
 | 		tok = p.getToken() | 
 | 	} | 
 | 	if tok.typ != tokenRightBracket { | 
 | 		return p.parseError(tok, "expected ']'") | 
 | 	} | 
 |  | 
 | 	p.query.appendPath(newMatchSliceFn(start, end, step)) | 
 | 	return p.parseMatchExpr | 
 | } | 
 |  | 
 | func (p *queryParser) parseFilterExpr() queryParserStateFn { | 
 | 	tok := p.getToken() | 
 | 	if tok.typ != tokenLeftParen { | 
 | 		return p.parseError(tok, "expected left-parenthesis for filter expression") | 
 | 	} | 
 | 	tok = p.getToken() | 
 | 	if tok.typ != tokenKey && tok.typ != tokenString { | 
 | 		return p.parseError(tok, "expected key or string for filter funciton name") | 
 | 	} | 
 | 	name := tok.val | 
 | 	tok = p.getToken() | 
 | 	if tok.typ != tokenRightParen { | 
 | 		return p.parseError(tok, "expected right-parenthesis for filter expression") | 
 | 	} | 
 | 	p.union = append(p.union, newMatchFilterFn(name, tok.Position)) | 
 | 	return p.parseUnionExpr | 
 | } | 
 |  | 
 | func parseQuery(flow chan token) (*Query, error) { | 
 | 	parser := &queryParser{ | 
 | 		flow:         flow, | 
 | 		tokensBuffer: []token{}, | 
 | 		query:        newQuery(), | 
 | 	} | 
 | 	parser.run() | 
 | 	return parser.query, parser.err | 
 | } |