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 }