package main import ( "strings" "fmt" "main/subex" ) 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 } func (p *parser) parseSubex() subex.SubexState { delim := p.next() if delim.typ != TokenSubstituteDelimiter { panic("Missing substitute delimiter") } subexProgramToken := p.next() if subexProgramToken.typ != TokenSubex { panic("Missing subex from substitution") } var subexProgram string if delim.val == "=" || delim.val == "~" || delim.val == "\"" || delim.val == "`" { subexProgram = delim.val + subexProgramToken.val + delim.val } else { subexProgram = subexProgramToken.val } reader := subex.NewStringRuneReader(subexProgram) subexAST := subex.Parse(reader) subex := subex.CompileTransducer(subexAST) delim = p.next() if delim.typ != TokenSubstituteDelimiter { panic("Missing end substitute delimiter") } return subex } func (p *parser) parseBasicCommand(commandChar rune) Command { switch commandChar { case 'p': return PrintValueCommand{} case 'd': return DeleteValueCommand{} case 'D': return DeletePathCommand{} case 'n': return NextCommand{} case 'N': return AppendNextCommand{} case 's', 'S': subex := p.parseSubex() var next Command token := p.peek() switch token.typ { case TokenEOF, TokenRBrace: next = NoopCommand{} default: next = p.parseCommand() } if (commandChar == 's') { return SubstituteValueCommand { subex: subex, next: next, } } else { return SubstitutePathCommand { subex: subex, next: next, } } case 'o': return NoopCommand{} case 'x': return SwapXRegCommand{} case 'X': return AppendXRegCommand{} case 'k': return SwapPathCommand{} case 'K': return AppendPathCommand{} default: panic("Invalid command") } } func (p *parser) parseCommand() Command { token := p.next() switch token.typ { case TokenLBrace: commands := p.parseCommands() if p.next().typ != TokenRBrace { panic("Missing matching }") } return SequenceCommand {commands} 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()) endToken := p.peek() if endToken.typ == TokenEOF || endToken.typ == TokenRBrace { return commands } } } func Parse(tokens chan Token) []Command { p := parser { tokenStream: tokens, } return p.parseCommands() }