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

subexstate.go (11067B)


      1 package subex
      2 
      3 // TODO: Simplify this implementation by combining similar states into one type
      4 // e.g. Combine all of the copy states into a single type that has a filter function
      5 
      6 import (
      7 	"main/walk"
      8 )
      9 
     10 // A state of execution for the transducer
     11 type SubexState interface {
     12 	// Eat a Atom and transition to any number of new states
     13 	eat(aux auxiliaryState, char walk.Edible) []SubexBranch
     14 	// Find accepting states reachable through epsilon transitions and return their outputs
     15 	accepting(aux auxiliaryState) []OutputStack
     16 }
     17 
     18 // Try first, if it fails then try second
     19 type SubexGroupState struct {
     20 	first, second SubexState
     21 }
     22 func (state SubexGroupState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
     23 	otherAux := aux.cloneStore()
     24 	return append(state.first.eat(aux, char), state.second.eat(otherAux, char)...)
     25 }
     26 func (state SubexGroupState) accepting(aux auxiliaryState) []OutputStack {
     27 	otherAux := aux.cloneStore()
     28 	return append(state.first.accepting(aux), state.second.accepting(otherAux)...)
     29 }
     30 
     31 type SubexCopyState struct {
     32 	next SubexState
     33 	filter valueFilter
     34 }
     35 func (state SubexCopyState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
     36 	value, isValue := edible.(walk.Value)
     37 	if !isValue || !state.filter.valueFilter(value) {
     38 		return nil
     39 	}
     40 	return []SubexBranch{{
     41 		state: state.next,
     42 		aux: aux.topAppend(walk.ValueList{value}),
     43 	}}
     44 }
     45 func (state SubexCopyState) accepting(aux auxiliaryState) []OutputStack {
     46 	return nil
     47 }
     48 
     49 type SubexCopyRuneState struct {
     50 	next SubexState
     51 	filter runeFilter
     52 }
     53 func (state SubexCopyRuneState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
     54 	r, isRune := edible.(walk.StringRuneAtom)
     55 	if !isRune || !state.filter.runeFilter(r) {
     56 		return nil
     57 	}
     58 	return []SubexBranch{{
     59 		state: state.next,
     60 		aux: aux.topAppend(walk.RuneList{r}),
     61 	}}
     62 }
     63 func (state SubexCopyRuneState) accepting(aux auxiliaryState) []OutputStack {
     64 	return nil
     65 }
     66 
     67 // Just pushes to the OutputStack and hands over to the next state
     68 // Used to capture the output of the state being handed over to
     69 type SubexCaptureBeginState struct {
     70 	next SubexState
     71 }
     72 func (state SubexCaptureBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
     73 	return state.next.eat(aux.pushOutput(walk.ValueList{}), char)
     74 }
     75 func (state SubexCaptureBeginState) accepting(aux auxiliaryState) []OutputStack {
     76 	return state.next.accepting(aux.pushOutput(walk.ValueList{}))
     77 }
     78 func (state SubexCaptureBeginState) String() string {
     79 	return "CaptureBeginState"
     80 }
     81 
     82 type SubexCaptureRunesBeginState struct {
     83 	next SubexState
     84 }
     85 func (state SubexCaptureRunesBeginState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
     86 	return state.next.eat(aux.pushOutput(walk.RuneList{}), char)
     87 }
     88 func (state SubexCaptureRunesBeginState) accepting(aux auxiliaryState) []OutputStack {
     89 	return state.next.accepting(aux.pushOutput(walk.RuneList{}))
     90 }
     91 
     92 // Discard the top of the OutputStack
     93 type SubexDiscardState struct {
     94 	next SubexState
     95 }
     96 func (state SubexDiscardState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
     97 	_, newAux := aux.popOutput()
     98 	return state.next.eat(newAux, char)
     99 }
    100 func (state SubexDiscardState) accepting(aux auxiliaryState) []OutputStack {
    101 	_, newAux := aux.popOutput()
    102 	return state.next.accepting(newAux)
    103 }
    104 
    105 // Pop the top of the OutputStack which contains the stuff outputted since the start of the store
    106 // This outputted data gets stored in a slot
    107 type SubexStoreEndState struct {
    108 	slot int
    109 	next SubexState
    110 }
    111 func (state SubexStoreEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
    112 	toStore, aux := aux.popOutput()
    113 	aux = aux.withValue(state.slot, toStore)
    114 	return state.next.eat(aux, char)
    115 }
    116 func (state SubexStoreEndState) accepting(aux auxiliaryState) []OutputStack {
    117 	toStore, aux := aux.popOutput()
    118 	aux = aux.withValue(state.slot, toStore)
    119 	return state.next.accepting(aux)
    120 }
    121 
    122 // A part of an output literal, either an Atom or a slot from which to load
    123 type OutputContent interface {
    124 	// Given the current store, return the ValueList produced by the TransducerOutput
    125 	buildValues(Store) walk.ValueList
    126 	// Given the current store, return the RuneList produced by the TransducerOutput
    127 	buildRunes(Store) walk.RuneList
    128 }
    129 
    130 // An OutputContent which is just a Value literal
    131 type OutputValueLiteral struct {
    132 	value walk.Value
    133 }
    134 func (replacement OutputValueLiteral) buildValues(store Store) walk.ValueList {
    135 	return walk.ValueList{replacement.value}
    136 }
    137 func (replacement OutputValueLiteral) buildRunes(store Store) walk.RuneList {
    138 	// TODO: serialise to JSON
    139 	panic("Unimplemented!")
    140 }
    141 
    142 // An OutputContent which is just a rune literal
    143 type OutputRuneLiteral struct {
    144 	rune walk.StringRuneAtom
    145 }
    146 func (replacement OutputRuneLiteral) buildValues(store Store) walk.ValueList {
    147 	// TODO: Try to deserialise
    148 	panic("Unimplemented!")
    149 }
    150 func (replacement OutputRuneLiteral) buildRunes(store Store) walk.RuneList {
    151 	return walk.RuneList {replacement.rune}
    152 }
    153 
    154 // An OutputContent which is a slot that is loaded from
    155 type OutputLoad struct {
    156 	slot int
    157 }
    158 func (replacement OutputLoad) buildValues(store Store) walk.ValueList {
    159 	values, isValues := store[replacement.slot].(walk.ValueList)
    160 	if !isValues {
    161 		panic("Tried to output non-values list")
    162 	}
    163 	return values
    164 }
    165 func (replacement OutputLoad) buildRunes(store Store) walk.RuneList {
    166 	runes, isRunes := store[replacement.slot].(walk.RuneList)
    167 	if !isRunes {
    168 		panic("Tried to output non-runes as runes")
    169 	}
    170 	return runes
    171 }
    172 
    173 // Don't read in anything, just output the series of data and slots specified
    174 type SubexOutputState struct {
    175 	content []OutputContent
    176 	next SubexState
    177 }
    178 // Given a store, return what is outputted by an epsilon transition from this state
    179 // TODO: separate into buildValues and buildRunes
    180 func (state SubexOutputState) build(store Store) walk.ValueList {
    181 	var result walk.ValueList
    182 	for _, part := range state.content {
    183 		result = append(result, part.buildValues(store)...)
    184 	}
    185 	return result
    186 }
    187 func (state SubexOutputState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
    188 	content := state.build(aux.store)
    189 	nextStates := state.next.eat(aux.topAppend(content), char)
    190 	return nextStates
    191 }
    192 func (state SubexOutputState) accepting(aux auxiliaryState) []OutputStack {
    193 	content := state.build(aux.store)
    194 	outputStacks := state.next.accepting(aux.topAppend(content))
    195 	return outputStacks
    196 }
    197 
    198 // A final state, transitions to nothing but is accepting
    199 type SubexNoneState struct {}
    200 func (state SubexNoneState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
    201 	return nil
    202 }
    203 func (state SubexNoneState) accepting(aux auxiliaryState) []OutputStack {
    204 	return []OutputStack{aux.outputStack}
    205 }
    206 
    207 // A dead end state, handy for making internals work nicer but technically redundant
    208 type SubexDeadState struct {}
    209 func (state SubexDeadState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
    210 	return nil
    211 }
    212 func (state SubexDeadState) accepting (aux auxiliaryState) []OutputStack {
    213 	return nil
    214 }
    215 
    216 // Read in an Atom and apply a map to generate an Atom to output
    217 // If the input isn't in the map transition to nothing
    218 // TODO
    219 // type SubexRangeState struct {
    220 // 	parts map[walk.Atom]walk.Atom
    221 // 	next SubexState
    222 // }
    223 // func (state SubexRangeState) eat(aux auxiliaryState, char walk.Atom) []SubexBranch {
    224 // 	out, exists := state.parts[char]
    225 // 	if !exists {
    226 // 		return nil
    227 // 	} else {
    228 // 		return []SubexBranch{{
    229 // 			state: state.next,
    230 // 			outputStack: topAppend(outputStack, []walk.Atom{out}),
    231 // 			store: store,
    232 // 		}}
    233 // 	}
    234 // }
    235 // func (state SubexRangeState) accepting(aux auxiliaryState) []OutputStack {
    236 // 	return nil
    237 // }
    238 
    239 
    240 type SubexArithmeticEndState struct {
    241 	next SubexState
    242 	calculate func(walk.ValueList) (walk.ValueList, error)
    243 }
    244 func (state SubexArithmeticEndState) eat(aux auxiliaryState, char walk.Edible) []SubexBranch {
    245 	toCompute, aux := aux.popOutput()
    246 	values, isValues := toCompute.(walk.ValueList)
    247 	if !isValues {
    248 		panic("Tried to do arithmetic on non-values")
    249 	}
    250 	result, err := state.calculate(values)
    251 	if err != nil {
    252 		return nil
    253 	}
    254 	return state.next.eat(aux.topAppend(result), char)
    255 }
    256 func (state SubexArithmeticEndState) accepting(aux auxiliaryState) []OutputStack {
    257 	toCompute, aux := aux.popOutput()
    258 	values, isValues := toCompute.(walk.ValueList)
    259 	if !isValues {
    260 		panic("Tried to do arithmetic on non-values")
    261 	}
    262 	result, err := state.calculate(values)
    263 	if err != nil {
    264 		return nil
    265 	}
    266 	return state.next.accepting(aux.topAppend(result))
    267 }
    268 
    269 type SubexDiscardTerminalState struct {
    270 	terminal walk.Terminal
    271 	next SubexState
    272 }
    273 func (state SubexDiscardTerminalState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
    274 	if edible != state.terminal {
    275 		return nil
    276 	}
    277 	return []SubexBranch{{
    278 		state: state.next,
    279 		aux: aux,
    280 	}}
    281 }
    282 func (state SubexDiscardTerminalState) accepting(aux auxiliaryState) []OutputStack {
    283 	return nil
    284 }
    285 
    286 type SubexConstructArrayState struct {
    287 	next SubexState
    288 }
    289 func (state SubexConstructArrayState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
    290 	outputs, aux := aux.popOutput()
    291 	values, isValues := outputs.(walk.ValueList)
    292 	if !isValues {
    293 		panic("Tried to create an array from non-values")
    294 	}
    295 	array := walk.ArrayStructure(values)
    296 	return state.next.eat(aux.topAppend(walk.ValueList{array}), edible)
    297 }
    298 func (state SubexConstructArrayState) accepting(aux auxiliaryState) []OutputStack {
    299 	outputs, aux := aux.popOutput()
    300 	values, isValues := outputs.(walk.ValueList)
    301 	if !isValues {
    302 		panic("Tried to create an array from non-values")
    303 	}
    304 	array := walk.ArrayStructure(values)
    305 	return state.next.accepting(aux.topAppend(walk.ValueList{array}))
    306 }
    307 
    308 type SubexConstructStringState struct {
    309 	next SubexState
    310 }
    311 func (state SubexConstructStringState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
    312 	outputs, aux := aux.popOutput()
    313 	runes, isRunes := outputs.(walk.RuneList)
    314 	if !isRunes {
    315 		panic("Tried to create a string from non-runes")
    316 	}
    317 	s := walk.StringStructure(runes)
    318 	return state.next.eat(aux.topAppend(walk.ValueList{s}), edible)
    319 }
    320 func (state SubexConstructStringState) accepting(aux auxiliaryState) []OutputStack {
    321 	outputs, aux := aux.popOutput()
    322 	runes, isRunes := outputs.(walk.RuneList)
    323 	if !isRunes {
    324 		panic("Tried to create a string from non-runes")
    325 	}
    326 	s := walk.StringStructure(runes)
    327 	return state.next.accepting(aux.topAppend(walk.ValueList{s}))
    328 }
    329 
    330 type SubexIncrementNestState struct {
    331 	next SubexState
    332 }
    333 func (state SubexIncrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
    334 	return state.next.eat(aux.incNest(), edible)
    335 }
    336 func (state SubexIncrementNestState) accepting(aux auxiliaryState) []OutputStack {
    337 	return state.next.accepting(aux.incNest())
    338 }
    339 func (state SubexIncrementNestState) String() string {
    340 	return "IncrementNestState"
    341 }
    342 
    343 type SubexDecrementNestState struct {
    344 	next SubexState
    345 }
    346 func (state SubexDecrementNestState) eat(aux auxiliaryState, edible walk.Edible) []SubexBranch {
    347 	return state.next.eat(aux.decNest(), edible)
    348 }
    349 func (state SubexDecrementNestState) accepting(aux auxiliaryState) []OutputStack {
    350 	return state.next.accepting(aux.decNest())
    351 }