package main import ( "fmt" "main/subex" "strings" "unicode/utf8" ) 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.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(commandChar rune) []Command { switch commandChar { case 'p': return []Command {PrintValueCommand{}} case 'd': return []Command {DeleteValueCommand{}} case 'n': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {NextCommand{}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteNextCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'N': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {AppendNextCommand{}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteAppendNextCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'm': return []Command {MergeCommand {}} case 'M': ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { FullMergeCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 's': ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteValueCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'r': ast := p.parseSubex() subex := subex.CompileTransducer(ast) return []Command { SubstituteValueCommand { subex: subex, elseJump: 2, }, RelativeJumpCommand { destination: -1, }, } case 'o': return []Command {NoopCommand {}} case 'x': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {SwapXRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteToXRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'X': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {AppendXRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteAppendXRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {SwapYRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteToYRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'Y': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {AppendYRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteAppendYRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {SwapZRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteToZRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'Z': delim := p.peek() if delim.typ != TokenSubstituteDelimiter { return []Command {AppendZRegCommand {}} } ast := p.parseSubex() subex := subex.CompileTransducer(ast) elseBranch := p.parseCommand() return append( []Command { SubstituteAppendZRegCommand { subex: subex, elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'a': elseBranch := p.parseCommand() return append( []Command { IsStartCommand { elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'A': elseBranch := p.parseCommand() return append( []Command { IsPrevStartCommand { elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'e': elseBranch := p.parseCommand() return append( []Command { IsEndCommand { elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case 'E': elseBranch := p.parseCommand() return append( []Command { IsNextEndCommand { elseJump: len(elseBranch) + 1, }, }, elseBranch..., ) case ':': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) return []Command { LabelCommand { label: label, }, } case 'b': labelToken := p.next() if labelToken.typ != TokenLabel { panic("Missing branch label") } label, _ := utf8.DecodeRuneInString(labelToken.val) return []Command { BranchPlaceholderCommand { label: label, }, } default: panic("Invalid command") } } func (p *parser) parseCommand() []Command { token := p.next() switch token.typ { case TokenLBrace: children := p.parseCommandSequence() if p.next().typ != TokenRBrace { panic("Missing matching }") } return children case TokenRBrace, TokenEOF: p.rewind(token) return nil 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) parseCommandSequence() []Command { var commands []Command for { nextToken := p.peek() if nextToken.typ == TokenEOF || nextToken.typ == TokenRBrace { return commands } commands = append(commands, p.parseCommand()...) } } func Parse(tokens chan Token) []Command { p := parser { tokenStream: tokens, rewinds: nil, } program := p.parseCommandSequence() labels := make(map[rune]int) for i, command := range program { switch label := command.(type) { case LabelCommand: labels[label.label] = i } } for i, command := range program { switch branch := command.(type) { case BranchPlaceholderCommand: destination, exists := labels[branch.label] if !exists { panic("Tried to branch to a label that doesn't exist") } program[i] = JumpCommand {destination} } } return program }