aboutsummaryrefslogtreecommitdiff
path: root/elymas/lib/sys/optroutines.ey
diff options
context:
space:
mode:
Diffstat (limited to 'elymas/lib/sys/optroutines.ey')
-rw-r--r--elymas/lib/sys/optroutines.ey742
1 files changed, 742 insertions, 0 deletions
diff --git a/elymas/lib/sys/optroutines.ey b/elymas/lib/sys/optroutines.ey
index b61ce67..848c31b 100644
--- a/elymas/lib/sys/optroutines.ey
+++ b/elymas/lib/sys/optroutines.ey
@@ -1,5 +1,747 @@
# less elegant, but faster implementations of various things
+## regex support, this time with assembly
+# ideas taken from http://swtch.com/~rsc/regexp/regexp3.html
+# based upon the pure-elymas implemenation in standardClient.ey
+<
+ txt .consume .|hu "%" defq
+
+ <
+ "../compiler/elymasAsmOps.ey" include
+ > /ops sys .asm .defv
+
+ sys .asm "::" via
+ sys .asm .ops ":" via
+ sys .asm .ops .|label "@" deffd
+ { :labelRecord [ } "[[" deffd
+ { ] :labelResolve } "]]" deffd
+
+ 0 ==:MATCH 1 ==:TERM 2 ==:JUMP 3 ==:SPLIT 4 ==:SAVE 5 ==:FIRST 6 ==:LAST
+ 7 ==:FLOATBEGIN 8 ==:TERMBEGIN
+
+ { ==b ==a [
+ [ SPLIT 1 a len 2 add ]
+ a _ len dearray
+ [ JUMP b len 1 add ]
+ b _ len dearray
+ ] } /alternative deffd
+
+ |cat /sequence deffd
+
+ { ==?a [ # TODO measure separate + implementation performance impact
+ [ JUMP a len 1 add ]
+ a _ len dearray
+ [ SPLIT a len neg 1 ]
+ ] } /star deffd
+
+ { ==?p [
+ [ TERM p ]
+ ] } /terminal deffd
+
+ { -- 1 }" terminal ==:TERMANY
+ { { eq }_ terminal } =*:TERMCHAR
+
+ { ==?i ==?a [
+ [ SAVE i 2 mul ]
+ a _ len dearray
+ [ SAVE i 2 mul 1 add ]
+ ] } /capture deffd
+
+ { [ ] } /empty deffd
+
+ { ==?str
+ str len 0 eq {
+ 1 neg
+ } {
+ 0 str *
+ } ? *
+ } /head deffd
+
+ { 1 -01 str .postfix } /tail deffd
+
+ { 0 -01 * -101 head eq } "^" deffd
+ { deffd }' /install deffd
+ [ "(" ")" "[" "]" "-" "|" "^" "*" "+" "." "$" "\\" "?" ] { ==?c
+ { _ head 0 c * eq } "^" c cat install
+ } each
+
+ {
+ 0 ==?currentCapture
+
+ { # "(parse) re: " -101 cat dump
+
+ seq ==?a
+ ^| {
+ tail parse ==?b
+ a b alternative =a
+ } rep
+ a
+ } /parse deffst
+
+ { # "(seq) re: " -101 cat dump
+
+ empty _ ==?a
+ ==?l
+
+ { # "(seq loop) re: " -101 cat dump
+ _ head 1 neg eq -01
+ ^| -01
+ ^) -01
+ -0321 or or not
+ } {
+ [ { ^* } {
+ l star =l
+ tail
+ } { ^+ } {
+ l l star sequence =l
+ tail
+ } { ^? } {
+ l empty alternative =l
+ tail
+ } { 1 } {
+ a l sequence =a
+ atom =l
+ } ] conds
+ } loop
+ a l sequence
+ } /seq deffst
+
+ { # "(atom) re: " -101 cat dump
+ empty ==?a
+
+ [ { ^( } {
+ currentCapture ==thisCapture
+ currentCapture 1 add =currentCapture
+ tail parse thisCapture capture =a
+ ^) not { ") expected" die } rep
+ tail
+ } { ^[ } {
+ tail
+ ^^ {
+ tail chars =*nset
+ { nset not }' ==?set
+ ^] not { "] expected" die } rep
+ tail
+ }' {
+ chars ==?set
+ ^] not { "] expected" die } rep
+ tail
+ }' ? *
+ set terminal =a
+ } { ^. } {
+ TERMANY =a
+ tail
+ } { ^^ } {
+ [ [ FIRST ] ] =a
+ tail
+ } { ^$ } {
+ [ [ LAST ] ] =a
+ tail
+ } { ^\ } {
+ tail
+ [ { ^d } {
+ { _ 0 "0" * ge -01 0 "9" * le and }" terminal =a
+ tail
+ } { ^n } {
+ { 0 "\n" * eq }" terminal =a
+ tail
+ }
+ [ "." "[" "]" "?" "*" "+" "$" "^" "\\" "(" ")" ] { ==c
+ { _ head 0 c * eq } {
+ { 0 c * eq } terminal =a
+ tail
+ }
+ } each
+ { 1 } {
+ "invalid character '" "' after \\ in regex" -120 cat cat die
+ } ] conds
+ } { 1 } {
+ _ head TERMCHAR =a
+ tail
+ } ] conds
+
+ # "(atom end) re: " -101 cat dump
+ a
+ } /atom deffst
+
+ { # "(chars) re: " -101 cat dump
+ ^] {
+ tail chars2 =*s
+ { _ s -01 0 "]" * eq or }' ==?set
+ }' {
+ chars2 ==?set
+ }' ? *
+ set
+ } /chars deffst
+
+ { # "(chars2) re: " -101 cat dump
+ ^- {
+ tail chars2 =*s
+ { _ s -01 0 "-" * eq or }' ==?set
+ }' {
+ charsR ==?set
+ }' ? *
+ set
+ } /chars2 deffst
+
+ { # "(charsR) re: " -101 cat dump
+ charsN ==?set
+ { ^] not } {
+ set =*s1
+ charsN =*s2
+ { _ s1 -01 s2 or }' =set
+ } loop
+ set
+ } /charsR deffst
+
+ { # "(charsN) re: " -101 cat dump
+ _ head ==?start
+ ^\ {
+ tail
+ [ { ^\ } {
+ 0 "\\" * =start
+ } { ^n } {
+ 0 "\n" * =start
+ } { 1 } {
+ "invalid character '" "' after \\ in regex" -120 cat cat die
+ } ] conds
+ } rep
+ tail
+ ^- {
+ tail
+ _ head ==?end
+ ^\ {
+ tail
+ [ { ^\ } {
+ 0 "\\" * =end
+ } { ^n } {
+ 0 "\n" * =end
+ } { 1 } {
+ "invalid character '" "' after \\ in regex" -120 cat cat die
+ } ] conds
+ } rep
+ tail
+ { _ start ge -01 end le and }' ==?set
+ }' {
+ { start eq }' ==?set
+ }' ? *
+ set
+ } /charsN deffst
+
+
+ [
+ { -- [[ # MATCH
+ matched sys .asm .rawAddress 24 add /rax :movqImmReg
+ 1 /rbp :orqImm8Reg # mark lowest bit so successful match is always non-zero
+ /rbp /rax :movqRegMem
+
+ # clear clist
+ entryPointsOffset /r8 /rax :leaqMemDisp32Reg
+ /rax /r8 :movqRegMem
+ 1 entryPointsOffset 1 sub 8 div 1 add range { 8 mul ==offset
+ 0 offset /r8 :andqImm8MemDisp32
+ } each
+
+ :retn
+ ]] }
+ { 1 -01* ==?p [[ # TERM
+ 0 256 range p grep ==acceptedChars
+ # TODO: optimize a few common cases instead of always rendering bitmaps
+
+ # rcx == length of string
+ # rdx == index of character under test
+
+ /rdx /rcx :cmpqRegReg
+ /stringExhausted :jleLbl8
+
+ /rax /rax :xorqRegReg
+ /rdx /rsi /al :movbMemIndexReg
+ /rax /bitfield :btqRegRipLbl32
+ /notMatched :jncLbl8
+
+ programCounter 1 add /rdi :movqImmReg
+ pushToNlist _ len dearray
+
+ @stringExhausted
+ @notMatched
+ :retn
+
+ @bitfield
+ 0 32 range { ==i
+ [ 0 8 range { i 8 mul add p * } each ] 2 math .unbase
+ } each
+ # position maxPosition lt {
+ # position string * 1 code * { pc 1 add thread updateThread nlist .add }" rep
+ # }" rep
+ ]] }
+ { ==instr [[ # JUMP
+ programCounter 1 instr * add 8 mul /r10 :jmpqMemDisp32
+
+ # pc 1 code add thread cloneThread iPush
+ ]] }
+ { ==instr [[ # SPLIT
+ prog len 64 add programCounter 1 instr * add add /rdi :movqImmReg
+ /rdi /r9 :btsqRegMem
+ /firstTargetRedundant :jcLbl8
+ programCounter 1 instr * add 8 mul /r10 :callqMemDisp32
+
+ @firstTargetRedundant
+ prog len 64 add programCounter 2 instr * add add /rdi :movqImmReg
+ /rdi /r9 :btsqRegMem
+ /secondTargetRedundant :jcLbl8
+ programCounter 2 instr * add 8 mul /r10 :jmpqMemDisp32
+
+ @secondTargetRedundant
+ :retn
+ # pc 2 code add thread cloneThread iPush
+ # pc 1 code add thread cloneThread iPush
+ ]] }
+ { ==instr [[ # SAVE
+ allocateCaptureGroup sys .asm .rawAddress 24 add /rax :movqImmReg
+ /rax :callqReg
+ # rax == newly allocated capture object
+
+ /r11 /rbx :movqRegReg
+ /rax 8 /rbx :movqRegMemDisp8
+
+ 0 captureInfoSize 8 div range { 8 mul ==offset
+ offset /rbp /rbx :movqMemDisp32Reg
+ /rbx offset /rax :movqRegMemDisp32
+ } each
+ /rax /rbp :movqRegReg # rbp capture object (as usual)
+
+ # actually save new position into capture info
+ /rdx 1 instr * 8 mul /rbp :movqRegMemDisp32
+
+ programCounter 1 add 8 mul /r10 :jmpqMemDisp32
+
+ # pc 1 add thread fullCloneThread
+ # position 1 code -2102 threadGetCaptures =[]
+ # iPush
+ ]] }
+ { -- [[ # FIRST
+ /rdx /rdx :testqRegReg
+ /atBeginning :jzLbl8
+ :retn
+
+ @atBeginning
+ programCounter 1 add 8 mul /r10 :jmpqMemDisp32
+
+ # position 0 eq { pc 1 add thread cloneThread iPush }" rep
+ ]] }
+ { -- [[ # LAST
+ /rdx /rcx :cmpqRegReg
+ /atEnd :jzLbl8
+ :retn
+
+ @atEnd
+ programCounter 1 add 8 mul /r10 :jmpqMemDisp32
+
+ # position maxPosition eq { pc 1 add thread cloneThread iPush }" rep
+ ]] }
+ { -- [[ # FLOATBEGIN
+ programCounter 1 add 8 mul /r10 :callqMemDisp32
+
+ programCounter /rdi :movqImmReg
+ pushToNlist _ len dearray
+ :retn
+ ]] }
+ { 1 -01* ==?p [[ # TERMBEGIN
+ 0 256 range p grep ==acceptedChars
+ # TODO: optimize a few common cases instead of always rendering bitmaps
+
+ # rcx == length of string
+ # rdx == index of character under test
+
+ # check nlist has zero entries
+ entryPointsOffset /r9 /rax :leaqMemDisp32Reg
+ /rax /r9 :cmpqRegMem
+ /stopSkipping :jnzLbl8
+
+ # check nlist has one entry
+ entryPointsOffset 16 add /r8 /rax :leaqMemDisp32Reg
+ /rax /r8 :cmpqRegMem
+ /stopSkipping :jnzLbl8
+
+ acceptedChars len 1 eq {
+ /rcx /rbx :movqRegReg
+ /rdx /rsi /rdi :leaqMemIndexReg
+ /rdx /rcx :subqRegReg
+ /stringExhaustedInitially :jzLbl8
+
+ 0 acceptedChars * /al :movbImmReg
+ :repnz :scasb
+ /charNotMatched :jnzLbl8
+
+ /rbx /rcx :movqRegReg
+ 1 neg /rdi /rdx :leaqMemDisp8Reg
+ /rsi /rdx :subqRegReg
+ /termMatched :jmpLbl8
+
+ @stringExhaustedInitially
+ @charNotMatched
+ /rbx /rcx :movqRegReg
+ /stringExhausted :jmpLbl32
+ } {
+ @skipLoop
+ /rdx /rcx :cmpqRegReg
+ /stringExhausted :jleLbl32
+
+ /rax /rax :xorqRegReg
+ /rdx /rsi /al :movbMemIndexReg
+ /rax /bitfield :btqRegRipLbl32
+ /termMatched :jcLbl8
+
+ 1 /rdx :addqImm8Reg
+ /skipLoop :jmpLbl8
+ } ? *
+
+ @stopSkipping
+
+ /rdx /rcx :cmpqRegReg
+ /stringExhausted :jleLbl8
+
+ /rax /rax :xorqRegReg
+ /rdx /rsi /al :movbMemIndexReg
+ /rax /bitfield :btqRegRipLbl32
+ /notMatched :jncLbl8
+
+ @termMatched
+ programCounter 1 add /rdi :movqImmReg
+ pushToNlist _ len dearray
+
+ @notMatched
+
+ programCounter /rdi :movqImmReg
+ pushToNlist _ len dearray
+
+ @stringExhausted
+ :retn
+
+ @bitfield
+ 0 32 range { ==i
+ [ 0 8 range { i 8 mul add p * } each ] 2 math .unbase
+ } each
+ # position maxPosition lt {
+ # position string * 1 code * { pc 1 add thread updateThread nlist .add }" rep
+ # }" rep
+ # 1 code /p deffs
+ # nlist .size not clist .size 1 eq and {
+ # { position maxPosition lt { position string * p not }" { 0 }" ? * }" {
+ # position 1 add =position
+ # }" loop
+ # }" rep
+
+ # position maxPosition lt {
+ # position string * p {
+ # pc 1 add thread cloneThread nlist .add
+ # }" rep
+ # thread nlist .add
+ # }" rep
+ ]] }
+ ] =*codeSemantics
+
+ parse ==prog --
+
+ # handling of common pattern starts
+ prog 0 -01 * 0 -01 * FIRST eq {
+ [
+ 1 prog len range { prog * } each
+ ] =prog
+ } {
+ prog 0 -01 * 0 -01 * TERM eq {
+ [
+ [ TERMBEGIN 1 0 prog * * ]
+ 1 prog len range { prog * } each
+ ] =prog
+ } {
+ [
+ [ FLOATBEGIN ]
+ ] prog cat =prog
+ } ? *
+ } ? *
+
+ prog [
+ [ MATCH ]
+ ] cat =prog
+
+ # prog dump
+
+ # data format for thread lists
+ # 0: current fill as pointer to after last entry
+ # 8: entry point already present in list bitfield
+ # 8 + proglen / 8: entry point split to in previous iteration
+ # (this is just a handy memory area to use for this, see SPLIT code)
+ # 8 + to_8_bytes(proglen / 4):
+ # 0: thread entry point (as index into prog/progCode)
+ # 8: thread capture pointer
+ 8
+ prog len 2 mul 1 sub 64 div 1 add 8 mul add _ ==entryPointsOffset
+ prog len 16 mul add
+ _ str .alloc ==clist
+ str .alloc ==nlist
+
+ # data for capture groups, this is a memory buffer managed as a free
+ # list which persists across regex invocations.
+ # 0: address of first free capture group, or 0 if no free block is known
+ # 8, 16: start, end index in string (or address of next free group)
+ # TODO: Measure memory impact and think whether total count can be reduced,
+ # e.g. to number of SAVE instructions times capture entries
+ 8
+ currentCapture 16 mul _ ==captureInfoSize prog len _ ==captureInfoCount mul
+ add str .alloc _ str .zero ==captures
+
+ 8 str .alloc ==matched
+
+ [[
+ # rdi == target program instruction index
+ # rbp == address of capture object to use
+ 64 /rdi /rax :leaqMemDisp8Reg
+ /rax /r9 :btsqRegMem
+ /alreadyQueued :jcLbl8
+
+ /r9 /rax :movqMemReg
+ 16 /r9 :addqImm8Mem
+ /rdi /rax :movqRegMem
+ /rbp 8 /rax :movqRegMemDisp8
+
+ @alreadyQueued
+ ]] ==pushToNlist
+
+ [[ currentCapture 0 gt {
+ @restartAfterCollection
+ captures sys .asm .rawAddress 24 add /rdi :movqImmReg
+ /rax /rax :xorqRegReg
+ /rdi /rax :orqMemReg
+ /runCollection :jzLbl8
+
+ /rax /rbx :movqMemReg
+ /rbx /rdi :movqRegMem
+ :retn
+
+ @runCollection
+ # Mark all blocks used by entries from clist and nlist
+ clist sys .asm .rawAddress 24 add /rdi :movqImmReg
+ /markCaptureInfo :callqLbl32
+ nlist sys .asm .rawAddress 24 add /rdi :movqImmReg
+ /markCaptureInfo :callqLbl32
+
+ # Collect unmarked blocks into free list
+ captures sys .asm .rawAddress 24 add /rdi :movqImmReg
+ 8 captureInfoSize captureInfoCount mul add /rdi /rbx :leaqMemDisp32Reg # rbx == end of capture memory
+ 8 /rdi /rax :leaqMemDisp8Reg # rax == block tested for mark bit
+
+ @collectLoop
+ /rbx /rax :cmpqRegReg
+ /collectLoopDone :jgeLbl8
+
+ 63 /rax :btqImm8Mem
+ /blockNotFree :jcLbl8
+
+ /rdi /rdi :movqMemReg
+ /rdi /rax :movqRegMem
+ captures sys .asm .rawAddress 24 add /rdi :movqImmReg
+ /rax /rdi :movqRegMem
+
+ @blockNotFree
+ captureInfoSize /rax :addqImm32Reg
+ /collectLoop :jmpLbl8
+ @collectLoopDone
+
+ # Reset all mark bits
+ 8 /rdi /rax :leaqMemDisp8Reg # rax == block to reset mark bit in
+ @resetLoop
+ /rbx /rax :cmpqRegReg
+ /resetLoopDone :jgeLbl8
+ 63 /rax :btrqImm8Mem
+ captureInfoSize /rax :addqImm32Reg
+ /resetLoop :jmpLbl8
+ @resetLoopDone
+ /restartAfterCollection :jmpLbl32
+
+ @markCaptureInfo
+ /rdi /rbx :movqMemReg # rbx == address after last entry
+ entryPointsOffset 8 add /rdi :addqImm32Reg # rdi == capture group of first entry
+
+ @markLoop
+ /rdi /rbx :cmpqRegReg
+ /markLoopDone :jleLbl8
+ /rdi /rax :movqMemReg # rax == address of capture information
+ 63 /rax :btsqImm8Mem # mark topmost bit
+ 16 /rdi :addqImm8Reg
+ /markLoop :jmpLbl8
+ @markLoopDone
+ :retn
+ } rep ]] str .fromArray ==allocateCaptureGroup
+
+ 0 ==programCounter # available during assembling
+ [ prog {
+ 0 -101* codeSemantics * str .fromArray programCounter 1 add =programCounter
+ } each ] ==progCode
+
+ progCode len 8 mul str .alloc ==progCodeOffsetted
+
+ [[
+ progCode sys .asm .rawAddress 8 add /rsi :movqImmReg
+ progCodeOffsetted sys .asm .rawAddress 24 add /rdi :movqImmReg
+
+ progCode { --
+ :lodsq
+ 24 /rax :addqImm8Reg
+ :stosq
+ } each
+ :retn
+ ]] [ ] sys .asm .createFunction *
+
+ [[
+ 8 /r15 :subqImm8Reg
+ /r15 :popqMem
+
+ matched sys .asm .rawAddress 24 add /rdi :movqImmReg
+ 0 /rdi :andqImm8Mem
+
+ clist sys .asm .rawAddress 24 add /r8 :movqImmReg # r8 == list of current threads
+ nlist sys .asm .rawAddress 24 add /r9 :movqImmReg # r9 == list of next threads
+ progCodeOffsetted sys .asm .rawAddress 24 add /r10 :movqImmReg # r10 == start of program list
+
+ # clear clist
+ entryPointsOffset /r8 /rax :leaqMemDisp32Reg
+ /rax /r8 :movqRegMem
+ 1 entryPointsOffset 1 sub 8 div 1 add range { 8 mul ==offset
+ 0 offset /r8 :andqImm8MemDisp32
+ } each
+
+ # clear nlist
+ entryPointsOffset /r9 /rax :leaqMemDisp32Reg
+ /rax /r9 :movqRegMem
+ 1 entryPointsOffset 1 sub 8 div 1 add range { 8 mul ==offset
+ 0 offset /r9 :andqImm8MemDisp32
+ } each
+
+ # add thread starting at start of program
+ currentCapture 0 gt {
+ allocateCaptureGroup sys .asm .rawAddress 24 add /rax :movqImmReg
+ /rax :callqReg
+ /rax entryPointsOffset 8 add /r8 :movqRegMemDisp32
+
+ 0 captureInfoSize 8 div range { 8 mul ==offset
+ 0 offset /rax :andqImm8MemDisp32
+ } each
+ } rep
+
+ 16 /r8 :addqImm8Mem
+ 1 8 /r8 :addqImm8MemDisp8
+ 0 entryPointsOffset /r8 :andqImm8MemDisp32
+
+ /rsi :popqReg # string to match
+ 16 /rsi /rcx :movqMemDisp8Reg # rcx == length of string
+ 24 /rsi :addqImm8Reg # rsi == string to match (actual content)
+ /rdx /rdx :xorqRegReg # rdx == index of character under test
+
+ @matchLoop
+ # r8 / r9: current/next thread lists
+ # rsi, rdx: string buffer, character index
+ # rcx: length of string
+ /rdx /rcx :cmpqRegReg
+ /stringExhausted :jngeLbl32
+
+ entryPointsOffset /r8 /r11 :leaqMemDisp32Reg # r11 == address in clist to be run next
+
+ @clistLoop
+ /r11 /r8 :cmpqRegMem
+ /clistFinished :jleLbl8
+
+ /r11 /rbx :movqMemReg # rbx == program counter to execute at
+ 8 /r11 /rbp :movqMemDisp8Reg # rbp == capture object
+ 8 /rbx /r10 :callqMemIndexScale
+
+ 16 /r11 :addqImm8Reg
+ /clistLoop :jmpLbl8
+
+ @clistFinished
+
+ # test if anything is left to execute
+ entryPointsOffset /r9 /r11 :leaqMemDisp32Reg # r11 == address in nlist to be run next
+ /r11 /r9 :cmpqRegMem
+ /threadsExhausted :jleLbl8
+
+ # switch nlist to clist
+ /r8 /r9 :xchgqRegReg
+
+ # clear nlist
+ entryPointsOffset /r9 /rax :leaqMemDisp32Reg
+ /rax /r9 :movqRegMem
+ 1 entryPointsOffset 1 sub 8 div 1 add range { 8 mul ==offset
+ 0 offset /r9 :andqImm8MemDisp32
+ } each
+
+ /rdx :incqReg
+ /matchLoop :jmpLbl32
+
+ @threadsExhausted
+ matched sys .asm .rawAddress 24 add /rdi :movqImmReg
+ 0 /rdi :cmpqImm8Mem
+ /nothingMatched :jzLbl32
+ /rdi /rbp :movqMemReg
+ 0 /rbp :btrqImm8Reg # reset lowest bit, set by MATCH code
+
+ # generate capture group contents (if any)
+ currentCapture 0 gt {
+ 16 /r15 :subqImm8Reg
+
+ 24 /rsi :subqImm8Reg # rsi == string matched
+ /rsi 8 /r15 :movqRegMemDisp8
+ /rbp /r15 :movqRegMem
+
+ 0 currentCapture range reverse { ==i
+ /r15 /rbp :movqMemReg
+ i 16 mul /rbp /rax :movqMemDisp32Reg
+ 63 /rax :btsqImm8Reg
+ /rax :pushqReg
+ i 16 mul 8 add /rbp /rax :movqMemDisp32Reg
+ 63 /rax :btsqImm8Reg
+ /rax :pushqReg
+ 8 /r15 :pushqMemDisp8
+
+ str .|infix sys .asm .rawAddress /rax :movqImmReg
+ 24 /rax /rax :movqMemDisp8Reg
+ 16 /rax :addqImm8Reg
+ /rax :callqReg
+ } each
+
+ 16 /r15 :addqImm8Reg
+ } rep
+
+ 1 /rax :movqImmReg
+ 63 /rax :btsqImm8Reg
+ /rax :pushqReg
+
+ /r15 :pushqMem
+ 8 /r15 :addqImm8Reg
+ :retn
+
+ @stringExhausted
+ @nothingMatched
+
+ /rax /rax :xorqRegReg
+ 63 /rax :btsqImm8Reg
+ /rax :pushqReg
+
+ /r15 :pushqMem
+ 8 /r15 :addqImm8Reg
+ :retn
+ ]] [ scope ] sys .asm .createFunction
+ }
+> -- /enregex deffd
+
+{
+ quoted {
+ _ sys .typed .type 1 eq {
+ enregex
+ } { |enregex "*" | } ? *
+ } { enregex * } ? *
+} /regex defq
+
<
str .|postfix /postfix deffd
{ ==TOKID ==TOKSTR ==TOKINT ==TOKFLOAT