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

subexast.go (13554B)


      1 package subex
      2 
      3 import (
      4 	"fmt"
      5 	"main/walk"
      6 )
      7 
      8 // A node in the AST of a subex
      9 type SubexAST interface {
     10 	compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState
     11 }
     12 
     13 // Process the first subex, then the second, splitting the input text in two
     14 type SubexASTConcat struct {
     15 	First, Second SubexAST
     16 }
     17 func (ast SubexASTConcat) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
     18 	return ast.First.compileWith(ast.Second.compileWith(next, slotMap, runic), slotMap, runic)
     19 }
     20 func (ast SubexASTConcat) String() string {
     21 	return fmt.Sprintf("(%v)(%v)", ast.First, ast.Second)
     22 }
     23 
     24 // Processing a subex and storing the output in a slot instead of outputting it
     25 type SubexASTStore struct {
     26 	Match SubexAST
     27 	Slot rune
     28 }
     29 func (ast SubexASTStore) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
     30 	id := slotMap.getId(ast.Slot)
     31 	newNext := ast.Match.compileWith(&SubexStoreEndState {
     32 		slot: id,
     33 		next: next,
     34 	}, slotMap, runic)
     35 
     36 	if !runic {
     37 		return &SubexCaptureBeginState {
     38 			next: newNext,
     39 		}
     40 	} else {
     41 		return &SubexCaptureRunesBeginState {
     42 			next: newNext,
     43 		}
     44 	}
     45 }
     46 func (ast SubexASTStore) String() string {
     47 	return fmt.Sprintf("$%c(%v)", ast.Slot, ast.Match)
     48 }
     49 
     50 // Try to run the first subex, if it fails then backtrack and use the second
     51 type SubexASTOr struct {
     52 	First, Second SubexAST
     53 }
     54 func (ast SubexASTOr) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
     55 	return &SubexGroupState {
     56 		ast.First.compileWith(next, slotMap, runic),
     57 		ast.Second.compileWith(next, slotMap, runic),
     58 	}
     59 }
     60 func (ast SubexASTOr) String() string {
     61 	return fmt.Sprintf("(%v)|(%v)", ast.First, ast.Second)
     62 }
     63 
     64 type ConvexRange struct {
     65 	Start, End int
     66 }
     67 func (cr ConvexRange) minmax() (int, int) {
     68 		if cr.Start == -1 {
     69 			return cr.End, -1
     70 		} else if cr.End == -1 {
     71 			return cr.Start, -1
     72 		} else if cr.Start < cr.End {
     73 			return cr.Start, cr.End
     74 		} else {
     75 			return cr.End, cr.Start
     76 		}
     77 }
     78 func (cr ConvexRange) decrement() ConvexRange {
     79 	if cr.Start == -1 {
     80 		return ConvexRange{-1, cr.End - 1}
     81 	} else if cr.End == -1 {
     82 		return ConvexRange{cr.Start - 1, -1}
     83 	} else {
     84 		return ConvexRange{cr.Start - 1, cr.End - 1}
     85 	}
     86 }
     87 func (cr ConvexRange) compile(content SubexAST, next SubexState, slotMap *SlotMap, runic bool) SubexState {
     88 	min, _ := cr.minmax()
     89 	if min != 0 {
     90 		return content.compileWith(cr.decrement().compile(content, next, slotMap, runic), slotMap, runic)
     91 	}
     92 	if cr.Start == -1 {
     93 		state := &SubexGroupState {nil, next}
     94 		state.first = content.compileWith(state, slotMap, runic)
     95 		return state
     96 	}
     97 	if cr.End == -1 {
     98 		state := &SubexGroupState {next, nil}
     99 		state.second = content.compileWith(state, slotMap, runic)
    100 		return state
    101 	}
    102 
    103 	if cr.End == 0 {
    104 		state := next;
    105 		for i := 0; i < cr.Start; i += 1 {
    106 			state = &SubexGroupState {
    107 				content.compileWith(state, slotMap, runic),
    108 				next,
    109 			}
    110 		}
    111 		return state
    112 	} else {
    113 		state := next;
    114 		for i := 0; i < cr.End; i += 1 {
    115 			state = &SubexGroupState {
    116 				next,
    117 				content.compileWith(state, slotMap, runic),
    118 			}
    119 		}
    120 		return state
    121 	}
    122 }
    123 
    124 // Try to run the subex a number of times that is one of the numbers in the acceptable range
    125 // Prioritising the left
    126 type SubexASTRepeat struct {
    127 	Content SubexAST
    128 	Acceptable []ConvexRange
    129 }
    130 func (ast SubexASTRepeat) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    131 	var state SubexState = &SubexDeadState{}
    132 	for _, convex := range ast.Acceptable {
    133 		state = &SubexGroupState {state, convex.compile(ast.Content, next, slotMap, runic)}
    134 	}
    135 	return state
    136 }
    137 func (ast SubexASTRepeat) String() string {
    138 	return fmt.Sprintf("(%v){...}", ast.Content)
    139 }
    140 
    141 // Read in a single specific Atom and output it unchanged
    142 type SubexASTCopyScalar struct {
    143 	Scalar walk.Scalar
    144 }
    145 func (ast SubexASTCopyScalar) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    146 	return &SubexCopyState{
    147 		filter: selectScalarFilter {ast.Scalar},
    148 		next: next,
    149 	}
    150 }
    151 func (ast SubexASTCopyScalar) String() string {
    152 	return fmt.Sprintf("a")
    153 }
    154 
    155 type SubexASTCopyAnyRune struct {}
    156 func (ast SubexASTCopyAnyRune) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    157 	return &SubexCopyRuneState {
    158 		next: next,
    159 		filter: anyRuneFilter{},
    160 	}
    161 }
    162 
    163 type SubexASTCopyRune struct {
    164 	rune rune
    165 }
    166 func (ast SubexASTCopyRune) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    167 	return &SubexCopyRuneState {
    168 		next: next,
    169 		filter: selectRuneFilter {ast.rune},
    170 	}
    171 }
    172 
    173 // Read in a single atom that must be a boolean and output it unchanged
    174 type SubexASTCopyBool struct {}
    175 func (ast SubexASTCopyBool) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    176 	return &SubexCopyState {
    177 		next: next,
    178 		filter: anyBoolFilter{},
    179 	}
    180 }
    181 func (ast SubexASTCopyBool) String() string {
    182 	return "?"
    183 }
    184 
    185 // Read in a single atom that must be a number and output it unchanged
    186 type SubexASTCopyNumber struct {}
    187 func (ast SubexASTCopyNumber) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    188 	return &SubexCopyState {
    189 		next: next,
    190 		filter: anyNumberFilter{},
    191 	}
    192 }
    193 func (ast SubexASTCopyNumber) String() string {
    194 	return "%"
    195 }
    196 
    197 // Read in a full string value and copy it out unchanged
    198 // # is equivalent to "_{-0}"
    199 // TODO
    200 // type SubexASTCopyString struct {}
    201 // func (ast SubexASTCopyString) compileWith(next SubexState, slotMap *SlotMap) SubexState {
    202 // 	stringAtomState := &SubexCopyStringAtomState {
    203 // 		next: nil,
    204 // 	}
    205 // 	stringContentState := &SubexGroupState {
    206 // 		&SubexCopyScalarState {
    207 // 			scalar: walk.NewAtomStringTerminal(),
    208 // 			next: next,
    209 // 		},
    210 // 		stringAtomState,
    211 // 	}
    212 // 	stringAtomState.next = stringContentState
    213 // 	return &SubexCopyScalarState {
    214 // 		scalar: walk.NewAtomStringTerminal(),
    215 // 		next: stringContentState,
    216 // 	}
    217 // }
    218 // func (ast SubexASTCopyString) String() string {
    219 // 	return "#"
    220 // }
    221 
    222 // Read in any single Atom and output it unchanged
    223 type SubexASTCopyAnyValue struct {}
    224 func (ast SubexASTCopyAnyValue) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    225 	return &SubexCopyState {
    226 		next: next,
    227 		filter: anyValueFilter{},
    228 	}
    229 }
    230 func (ast SubexASTCopyAnyValue) String() string {
    231 	return "."
    232 }
    233 
    234 type OutputContentAST interface {
    235 	compile(slotMap *SlotMap) OutputContent
    236 }
    237 
    238 type OutputLoadAST struct {
    239 	slot rune
    240 }
    241 func (ast OutputLoadAST) compile(slotMap *SlotMap) OutputContent {
    242 	return OutputLoad {slotMap.getId(ast.slot)}
    243 }
    244 
    245 type OutputValueLiteralAST struct {
    246 	atom walk.Value
    247 }
    248 func (ast OutputValueLiteralAST) compile(slotMap *SlotMap) OutputContent {
    249 	return OutputValueLiteral {ast.atom}
    250 }
    251 
    252 type OutputRuneLiteralAST struct {
    253 	rune walk.StringRuneAtom
    254 }
    255 func (ast OutputRuneLiteralAST) compile(slotMap *SlotMap) OutputContent {
    256 	return OutputRuneLiteral {ast.rune}
    257 }
    258 
    259 // Output a series of Atoms without reading anything from input
    260 type SubexASTOutput struct {
    261 	Replacement []OutputContentAST
    262 }
    263 func (ast SubexASTOutput) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    264 	var content []OutputContent
    265 	for _, el := range ast.Replacement {
    266 		content = append(content, el.compile(slotMap))
    267 	}
    268 	return &SubexOutputState{
    269 		content: content,
    270 		next: next,
    271 	}
    272 }
    273 func (ast SubexASTOutput) String() string {
    274 	return "=...="
    275 }
    276 
    277 // Read in a repeated subex separated by a delimiter. Greedy
    278 type SubexASTJoin struct {
    279 	Content, Delimiter SubexAST
    280 }
    281 func (ast SubexASTJoin) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    282 	afterContentState := &SubexGroupState {
    283 		nil,
    284 		next,
    285 	}
    286 	manyContentsState := ast.Content.compileWith(afterContentState, slotMap, runic)
    287 	afterContentState.first = ast.Delimiter.compileWith(manyContentsState, slotMap, runic)
    288 	return &SubexGroupState {
    289 		manyContentsState,
    290 		next,
    291 	}
    292 }
    293 func (ast SubexASTJoin) String() string {
    294 	return fmt.Sprintf("(%v);(%v)", ast.Content, ast.Delimiter)
    295 }
    296 
    297 // Run each input Atom through a map to produce an output Atom
    298 // Atoms not in the map cause this to not match
    299 // type SubexASTRange struct {
    300 // 	Parts map[walk.Atom]walk.Atom
    301 // }
    302 // func (ast SubexASTRange) compileWith(next SubexState, slotMap *SlotMap) SubexState {
    303 // 	return &SubexRangeState {
    304 // 		parts: ast.Parts,
    305 // 		next: next,
    306 // 	}
    307 // }
    308 // func (ast SubexASTRange) String() string {
    309 // 	return fmt.Sprintf("[abc=xyz]")
    310 // }
    311 
    312 // Run content, if content is a list of booleans, OR them, if all values are castable to numbers, sum them and output the total
    313 // Reject if neither of these cases match
    314 type SubexASTSum struct {
    315 	Content SubexAST
    316 }
    317 func (ast SubexASTSum) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    318 	return &SubexCaptureBeginState {
    319 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    320 			next: next,
    321 			calculate: sumValues,
    322 		}, slotMap, runic),
    323 	}
    324 }
    325 func (ast SubexASTSum) String() string {
    326 	return fmt.Sprintf("(%v)+", ast.Content)
    327 }
    328 
    329 // Like sum but for AND and product
    330 type SubexASTProduct struct {
    331 	Content SubexAST
    332 }
    333 func (ast SubexASTProduct) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    334 	return &SubexCaptureBeginState {
    335 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    336 			next: next,
    337 			calculate: multiplyValues,
    338 		}, slotMap, runic),
    339 	}
    340 }
    341 func (ast SubexASTProduct) String() string {
    342 	return fmt.Sprintf("(%v)*", ast.Content)
    343 }
    344 
    345 // Runs the content Subex, if all outputted atoms can be cast to numbers, outputs them all negated
    346 // Rejects if this fails
    347 type SubexASTNegate struct {
    348 	Content SubexAST
    349 }
    350 func (ast SubexASTNegate) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    351 	return &SubexCaptureBeginState {
    352 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    353 			next: next,
    354 			calculate: negateValues,
    355 		}, slotMap, runic),
    356 	}
    357 }
    358 func (ast SubexASTNegate) String() string {
    359 	return fmt.Sprintf("(%v)-", ast.Content)
    360 }
    361 
    362 // Runs the content Subex and collects the output
    363 // If it is a list of atoms castable to numbers, it takes the reciprocal of them all and outputs them
    364 // Else it rejects
    365 type SubexASTReciprocal struct {
    366 	Content SubexAST
    367 }
    368 func (ast SubexASTReciprocal) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    369 	return &SubexCaptureBeginState {
    370 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    371 			next: next,
    372 			calculate: reciprocalValues,
    373 		}, slotMap, runic),
    374 	}
    375 }
    376 func (ast SubexASTReciprocal) String() string {
    377 	return fmt.Sprintf("(%v)/", ast.Content)
    378 }
    379 
    380 // Runs the content Subex and collects the output
    381 // Maps over the values in the output, casting each to a boolean, notting each and then outputs them
    382 // Rejects if it cannot cast to boolean
    383 type SubexASTNot struct {
    384 	Content SubexAST
    385 }
    386 func (ast SubexASTNot) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    387 	return &SubexCaptureBeginState {
    388 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    389 			next: next,
    390 			calculate: notValues,
    391 		}, slotMap, runic),
    392 	}
    393 }
    394 func (ast SubexASTNot) String() string {
    395 	return fmt.Sprintf("(%v)!", ast.Content)
    396 }
    397 
    398 // Runs the content Subex and collects the output
    399 // Replaces it with true if all output values are equal and false otherwise
    400 type SubexASTEqual struct {
    401 	Content SubexAST
    402 }
    403 func (ast SubexASTEqual) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    404 	return &SubexCaptureBeginState {
    405 		next: ast.Content.compileWith(&SubexArithmeticEndState {
    406 			next: next,
    407 			calculate: equalValues,
    408 		}, slotMap, runic),
    409 	}
    410 }
    411 
    412 // Does nothing
    413 type SubexASTEmpty struct {}
    414 func (ast SubexASTEmpty) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    415 	return next
    416 }
    417 func (ast SubexASTEmpty) String() string {
    418 	return "()"
    419 }
    420 
    421 // Discards the output from the content subex
    422 type SubexASTDiscard struct {
    423 	Content SubexAST
    424 }
    425 func (ast SubexASTDiscard) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    426 	newNext := ast.Content.compileWith(&SubexDiscardState {next}, slotMap, runic)
    427 	if !runic {
    428 		return &SubexCaptureBeginState {
    429 			next: newNext,
    430 		}
    431 	} else {
    432 		return &SubexCaptureRunesBeginState {
    433 			next: newNext,
    434 		}
    435 	}
    436 }
    437 func (ast SubexASTDiscard) String() string {
    438 	return fmt.Sprintf("(%v)$_", ast.Content)
    439 }
    440 
    441 // Go into an array, pass the content each of the values in the array to eat and then leave the array
    442 type SubexASTEnterArray struct {
    443 	Content SubexAST
    444 }
    445 func (ast SubexASTEnterArray) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    446 	return &SubexCaptureBeginState {
    447 		next: &SubexIncrementNestState {
    448 			next: &SubexCopyState {
    449 				filter: anyArrayFilter{},
    450 				next: &SubexDiscardState {
    451 					next: &SubexCaptureBeginState {
    452 						next: ast.Content.compileWith(
    453 							&SubexDiscardTerminalState {
    454 								terminal: walk.ArrayEndTerminal{},
    455 								next: &SubexDecrementNestState {
    456 									next: &SubexConstructArrayState {next: next},
    457 								},
    458 							},
    459 							slotMap,
    460 							runic,
    461 						),
    462 					},
    463 				},
    464 			},
    465 		},
    466 	}
    467 }
    468 
    469 type SubexASTEnterString struct {
    470 	Content SubexAST
    471 }
    472 func (ast SubexASTEnterString) compileWith(next SubexState, slotMap *SlotMap, runic bool) SubexState {
    473 	return &SubexCaptureBeginState {
    474 		next: &SubexIncrementNestState {
    475 			next: &SubexCopyState {
    476 				filter: anyStringFilter{},
    477 				next: &SubexDiscardState {
    478 					next: &SubexCaptureRunesBeginState {
    479 						next: ast.Content.compileWith(
    480 							&SubexDecrementNestState {
    481 								next: &SubexDiscardTerminalState {
    482 									terminal: walk.StringEndTerminal{},
    483 									next: &SubexConstructStringState {next: next},
    484 								},
    485 							},
    486 							slotMap,
    487 							true,
    488 						),
    489 					},
    490 				},
    491 			},
    492 		},
    493 	}
    494 }