stred-go

Stred: Streaming Tree Editor. Like sed but for JSON. This is the go implementation
git clone https://shtanton.xyz/git/stred-go.git
Log | Files | Refs | README

parse.go (4532B)


      1 package main
      2 
      3 import (
      4 	"fmt"
      5 	"main/subex"
      6 	"strings"
      7 	"unicode/utf8"
      8 )
      9 
     10 type parser struct {
     11 	tokenStream chan Token
     12 	rewinds []Token
     13 	labels map[rune]int
     14 }
     15 func (p *parser) next() Token {
     16 	var token Token
     17 	if len(p.rewinds) == 0 {
     18 		token = <- p.tokenStream
     19 	} else {
     20 		token = p.rewinds[len(p.rewinds)-1]
     21 		p.rewinds = p.rewinds[:len(p.rewinds)-1]
     22 	}
     23 	if token.typ == TokenErr {
     24 		fmt.Println(token)
     25 		panic("Lexing error")
     26 	}
     27 	return token
     28 }
     29 func (p *parser) rewind(token Token) {
     30 	p.rewinds = append(p.rewinds, token)
     31 }
     32 func (p *parser) peek() Token {
     33 	token := p.next()
     34 	p.rewind(token)
     35 	return token
     36 }
     37 
     38 func (p *parser) parseSubex() subex.SubexAST {
     39 	delim := p.next()
     40 	if delim.typ != TokenSubstituteDelimiter {
     41 		panic("Missing substitute delimiter")
     42 	}
     43 	subexProgramToken := p.next()
     44 	if subexProgramToken.typ != TokenSubex {
     45 		panic("Missing subex from substitution")
     46 	}
     47 	var subexProgram string
     48 	if delim.val == "=" || delim.val == "~" || delim.val == "\"" || delim.val == "`" || delim.val == "^" {
     49 		subexProgram = delim.val + subexProgramToken.val + delim.val
     50 	} else {
     51 		subexProgram = subexProgramToken.val
     52 	}
     53 	reader := subex.NewStringRuneReader(subexProgram)
     54 	subexAST := subex.Parse(reader)
     55 	delim = p.next()
     56 	if delim.typ != TokenSubstituteDelimiter {
     57 		panic("Missing end substitute delimiter")
     58 	}
     59 	return subexAST
     60 }
     61 
     62 func (p *parser) parseBasicCommand(commands []Command, commandChar rune) []Command {
     63 	switch commandChar {
     64 		case 'p':
     65 			return append(commands, PrintValueCommand{})
     66 		case 'd':
     67 			return append(commands, DeleteValueCommand{})
     68 		case 'D':
     69 			return append(commands, DeletePathCommand{})
     70 		case 'n':
     71 			return append(commands, NextCommand{})
     72 		case 'N':
     73 			return append(commands, AppendNextCommand{})
     74 		case 's', 'S':
     75 			ast := p.parseSubex()
     76 			subex := subex.CompileTransducer(ast)
     77 			switch commandChar {
     78 				case 's':
     79 					return append(commands, SubstituteValueCommand {subex}, JumpCommand {len(commands) + 3})
     80 				case 'S':
     81 					return append(commands, SubstitutePathCommand {subex}, JumpCommand {len(commands) + 3})
     82 				default:
     83 					panic("Unreachable!?!?")
     84 			}
     85 		case 'o':
     86 			return append(commands, NoopCommand{})
     87 		case 'x':
     88 			return append(commands, SwapXRegCommand{})
     89 		case 'X':
     90 			return append(commands, AppendXRegCommand{})
     91 		case 'y':
     92 			return append(commands, SwapYRegCommand{})
     93 		case 'Y':
     94 			return append(commands, AppendYRegCommand{})
     95 		case 'z':
     96 			return append(commands, SwapZRegCommand{})
     97 		case 'Z':
     98 			return append(commands, AppendZRegCommand{})
     99 		case 'k':
    100 			return append(commands, SwapPathCommand{})
    101 		case 'K':
    102 			return append(commands, AppendPathCommand{})
    103 		case ':':
    104 			labelToken := p.next()
    105 			if labelToken.typ != TokenLabel {
    106 				panic("Missing branch label")
    107 			}
    108 			label, _ := utf8.DecodeRuneInString(labelToken.val)
    109 			p.labels[label] = len(commands)
    110 			return commands
    111 		case 'b':
    112 			labelToken := p.next()
    113 			if labelToken.typ != TokenLabel {
    114 				panic("Missing branch label")
    115 			}
    116 			label, _ := utf8.DecodeRuneInString(labelToken.val)
    117 			return append(commands, BranchPlaceholderCommand {label})
    118 		default:
    119 			panic("Invalid command")
    120 	}
    121 }
    122 
    123 func (p *parser) parseCommand(commands []Command) []Command {
    124 	token := p.next()
    125 	switch token.typ {
    126 		case TokenLBrace:
    127 			jumpToBlockCommand := &JumpCommand{0}
    128 			commands = append(commands, JumpCommand {len(commands) + 2}, jumpToBlockCommand)
    129 			commands = p.parseCommands(commands)
    130 			if p.next().typ != TokenRBrace {
    131 				panic("Missing matching }")
    132 			}
    133 			jumpToBlockCommand.destination = len(commands)
    134 			return commands
    135 		case TokenCommand:
    136 			commandChar, _, err := strings.NewReader(token.val).ReadRune()
    137 			if err != nil {
    138 				panic("Error reading a command character!?")
    139 			}
    140 			return p.parseBasicCommand(commands, commandChar)
    141 		default:
    142 			panic("Invalid token, expected command")
    143 	}
    144 }
    145 
    146 func (p *parser) parseCommands(commands []Command) []Command {
    147 	for {
    148 		nextToken := p.peek()
    149 		if nextToken.typ == TokenEOF || nextToken.typ == TokenRBrace {
    150 			return commands
    151 		}
    152 		commands = p.parseCommand(commands)
    153 	}
    154 }
    155 
    156 func Parse(tokens chan Token) []Command {
    157 	p := parser {
    158 		tokenStream: tokens,
    159 		rewinds: nil,
    160 		labels: make(map[rune]int),
    161 	}
    162 	program := p.parseCommands(nil)
    163 	for i, command := range program {
    164 		switch branch := command.(type) {
    165 			case BranchPlaceholderCommand:
    166 				destination, exists := p.labels[branch.label]
    167 				if !exists {
    168 					panic("Tried to branch to a label that doesn't exist")
    169 				}
    170 				program[i] = JumpCommand {destination}
    171 		}
    172 	}
    173 	return program
    174 }