package main import ( "fmt" "main/subex" "strings" "unicode/utf8" ) type parser struct { tokenStream chan Token rewinds []Token labels map[rune]int } 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.SubexAST { delim := p.next() if delim.typ != TokenSubstituteDelimiter { panic("Missing substitute delimiter") } subexProgramToken := p.next() if subexProgramToken.typ != TokenSubex { panic("Missing subex from substitution") } reader := subex.NewStringRuneReader(subexProgramToken.val) subexAST := subex.Parse(reader) delim = p.next() if delim.typ != TokenSubstituteDelimiter { panic("Missing end substitute delimiter") } return subexAST } func (p *parser) parseBasicCommand(commands []Command, commandChar rune) []Command { switch commandChar { case 'p': return append(commands, PrintValueCommand{}) case 'd': return append(commands, DeleteValueCommand{}) case 'n': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, NextCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteNextCommand {subex}, JumpCommand {len(commands) + 3}) case 'N': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, AppendNextCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteAppendNextCommand {subex}, JumpCommand {len(commands) + 3}) case 'm': return append(commands, MergeCommand{}) case 's': ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteValueCommand {subex}, JumpCommand {len(commands) + 3}) case 'o': return append(commands, NoopCommand{}) case 'x': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, SwapXRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteToXRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'X': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, AppendXRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteAppendXRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, SwapYRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteToYRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'Y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, AppendYRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteAppendYRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, SwapZRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteToZRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'Z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return append(commands, AppendZRegCommand{}) } ast := p.parseSubex() subex := subex.CompileTransducer(ast) return append(commands, SubstituteAppendZRegCommand {subex}, JumpCommand {len(commands) + 3}) case 'a': return append(commands, IsStartCommand{}, JumpCommand {len(commands) + 3}) case 'A': return append(commands, IsPrevStartCommand{}, JumpCommand {len(commands) + 3}) case 'e': return append(commands, IsEndCommand{}, JumpCommand {len(commands) + 3}) case 'E': return append(commands, IsNextEndCommand{}, JumpCommand {len(commands) + 3}) case ':': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) p.labels[label] = len(commands) return commands case 'b': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) return append(commands, BranchPlaceholderCommand {label}) default: panic("Invalid command") } } func (p *parser) parseCommand(commands []Command) []Command { token := p.next() switch token.typ { case TokenLBrace: jumpToBlockCommand := &JumpCommand{0} commands = append(commands, JumpCommand {len(commands) + 2}, jumpToBlockCommand) commands = p.parseCommands(commands) if p.next().typ != TokenRBrace { panic("Missing matching }") } jumpToBlockCommand.destination = len(commands) return commands case TokenCommand: commandChar, _, err := strings.NewReader(token.val).ReadRune() if err != nil { panic("Error reading a command character!?") } return p.parseBasicCommand(commands, commandChar) default: panic("Invalid token, expected command") } } func (p *parser) parseCommands(commands []Command) []Command { for { nextToken := p.peek() if nextToken.typ == TokenEOF || nextToken.typ == TokenRBrace { return commands } commands = p.parseCommand(commands) } } func Parse(tokens chan Token) []Command { p := parser { tokenStream: tokens, rewinds: nil, labels: make(map[rune]int), } program := p.parseCommands(nil) for i, command := range program { switch branch := command.(type) { case BranchPlaceholderCommand: destination, exists := p.labels[branch.label] if !exists { panic("Tried to branch to a label that doesn't exist") } program[i] = JumpCommand {destination} } } return program }