package main import ( "strings" "strconv" "fmt" ) type parser struct { tokenStream chan Token rewinds []Token } func (p *parser) next() Token { var token Token if len(p.rewinds) == 0 { token = <- p.tokenStream } else { token = p.rewinds[len(p.rewinds)-1] p.rewinds = p.rewinds[:len(p.rewinds)-1] } if token.typ == TokenErr { fmt.Println(token) panic("Lexing error") } return token } func (p *parser) rewind(token Token) { p.rewinds = append(p.rewinds, token) } func (p *parser) peek() Token { token := p.next() p.rewind(token) return token } var segmentTokens map[TokenType]bool = map[TokenType]bool { TokenHash: true, TokenAt: true, TokenDot: true, TokenLParen: true, } func (p *parser) parsePathPatternFilter(minPower int) PathFilterAST { var lhs PathFilterAST token := p.next() switch token.typ { case TokenHash: stringIndex := p.next() if stringIndex.typ != TokenPatternStringIndex { panic("Expected string index after # in pattern") } lhs = StringSegmentPathFilterAST{stringIndex.val} case TokenAt: intIndex := p.next() if intIndex.typ != TokenPatternIntegerIndex { panic("Expected integer index after @ in pattern") } index, err := strconv.Atoi(intIndex.val) if err != nil { panic("Expected integer index after @ in pattern") } lhs = IntegerSegmentPathFilterAST{index} case TokenDot: lhs = AnySegmentPathFilterAST{} case TokenLParen: lhs = p.parsePathPatternFilter(0) if p.next().typ != TokenRParen { panic("Expected )") } default: panic("Expected path pattern filter segment") } loop: for { token = p.next() switch { case token.typ == TokenAst && 10 >= minPower: lhs = RepeatPathFilterAST {lhs} case token.typ == TokenQuestion && 10 >= minPower: lhs = OrPathFilterAST{lhs, NonePathFilterAST{}} case token.typ == TokenBar && 0 >= minPower: lhs = OrPathFilterAST{lhs, p.parsePathPatternFilter(1)} case segmentTokens[token.typ] && 2 >= minPower: p.rewind(token) lhs = SequencePathFilterAST {lhs, p.parsePathPatternFilter(3)} default: p.rewind(token) break loop } } return lhs } func (p *parser) parseFilter(minPower int) Filter { var lhs Filter token := p.next() switch token.typ { case TokenHash, TokenAt, TokenDot: p.rewind(token) filterAst := p.parsePathPatternFilter(0) lhs = compilePathFilterAST(filterAst) case TokenHat: lhs = BeginTerminalFilter{} case TokenDollar: lhs = EndTerminalFilter{} case TokenHatDollar: lhs = TerminalFilter{} case TokenTilde: lhs = RootFilter{} default: panic("Expected filter") } loop: for { token = p.next() switch { case token.typ == TokenAnd && 2 >= minPower: lhs = AndFilter {lhs, p.parseFilter(3)} default: p.rewind(token) break loop } } return lhs } func (p *parser) parseBasicCommand(commandChar rune) Command { switch commandChar { case 'p': return PrintValueCommand{} case 'd': return DeleteAllCommand{} default: panic("Invalid command") } } func (p *parser) parseCommand() Command { token := p.next() switch token.typ { case TokenHash, TokenAt, TokenDot, TokenLParen, TokenHat, TokenDollar, TokenHatDollar, TokenTilde: p.rewind(token) filter := p.parseFilter(0) notToken := p.next() if notToken.typ == TokenExclamation { filter = NotFilter {filter} } else { p.rewind(notToken) } command := p.parseCommand() command = FilteredCommand { filter: filter, command: command, } return command case TokenCommand: commandChar, _, err := strings.NewReader(token.val).ReadRune() if err != nil { panic("Error reading a command character!?") } return p.parseBasicCommand(commandChar) default: panic("Invalid token, expected command") } } func (p *parser) parseCommands() []Command { var commands []Command for { nextToken := p.peek() if nextToken.typ == TokenEOF || nextToken.typ == TokenRBrace { return commands } commands = append(commands, p.parseCommand()) semicolon := p.next() if semicolon.typ == TokenEOF || semicolon.typ == TokenRBrace { return commands } if semicolon.typ != TokenSemicolon { panic("Expected ; after command") } } } func Parse(tokens chan Token) []Command { p := parser { tokenStream: tokens, } return p.parseCommands() }