====== 4d2006-01 Listings ====== ===== xchar ===== \ xchar reference implementation: UTF-8 (and ISO-LATIN-1) \ environmental dependency: characters are stored as bytes \ environmental dependency: lower case words accepted base @ hex 80 Value maxascii : xc-size ( xc -- n ) dup maxascii u< IF drop 1 EXIT THEN \ special case ASCII $800 2 >r BEGIN 2dup u>= WHILE 5 lshift r> 1+ >r dup 0= UNTIL THEN 2drop r> ; : xc@+ ( xcaddr -- xcaddr' u ) count dup maxascii u< IF EXIT THEN \ special case ASCII 7F and 40 >r BEGIN dup r@ and WHILE r@ xor 6 lshift r> 5 lshift >r >r count \ dup C0 and 80 <> abort" malformed character" 3F and r> or REPEAT r> drop ; : xc!+ ( xc xcaddr -- xcaddr' ) over maxascii u< IF tuck c! char+ EXIT THEN \ special case ASCII >r 0 swap 3F BEGIN 2dup u> WHILE 2/ >r dup 3F and 80 or swap 6 rshift r> REPEAT 7F xor 2* or r> BEGIN over 80 u< 0= WHILE tuck c! char+ REPEAT nip ; : xc!+? ( xc xcaddr u -- xcaddr' u' flag ) >r over xc-size r@ over u< IF ( xc xc-addr1 len r: u1 ) \ not enough space drop nip r> false ELSE >r xc!+ r> r> swap - true THEN ; \ scan to next/previous character : xchar+ ( xcaddr -- xcaddr' ) xc@+ drop ; : xchar- ( xcaddr -- xcaddr' ) BEGIN 1 chars - dup c@ C0 and maxascii <> UNTIL ; : xstring+ ( xcaddr u -- xcaddr u' ) over + xchar+ over - ; : xstring- ( xcaddr u -- xcaddr u' ) over + xchar- over - ; : +xstring ( xc-addr1 u1 -- xc-addr2 u2 ) over dup xchar+ swap - /string ; : -xstring ( xc-addr1 u1 -- xc-addr2 u2 ) over dup xchar- swap - /string ; \ utf key and emit : xkey ( -- xc ) key dup maxascii u< IF EXIT THEN \ special case ASCII 7F and 40 >r BEGIN dup r@ and WHILE r@ xor 6 lshift r> 5 lshift >r >r key \ dup C0 and 80 <> abort" malformed character" 3F and r> or REPEAT r> drop ; : xemit ( xc -- ) dup maxascii u< IF emit EXIT THEN \ special case ASCII 0 swap 3F BEGIN 2dup u> WHILE 2/ >r dup 3F and 80 or swap 6 rshift r> REPEAT 7F xor 2* or BEGIN dup 80 u< 0= WHILE emit REPEAT drop ; \ utf size \ uses wcwidth ( xc -- n ) : wc, ( n low high -- ) 1+ , , , ; Create wc-table \ derived from wcwidth source code, for UCS32 0 0300 0357 wc, 0 035D 036F wc, 0 0483 0486 wc, 0 0488 0489 wc, 0 0591 05A1 wc, 0 05A3 05B9 wc, 0 05BB 05BD wc, 0 05BF 05BF wc, 0 05C1 05C2 wc, 0 05C4 05C4 wc, 0 0600 0603 wc, 0 0610 0615 wc, 0 064B 0658 wc, 0 0670 0670 wc, 0 06D6 06E4 wc, 0 06E7 06E8 wc, 0 06EA 06ED wc, 0 070F 070F wc, 0 0711 0711 wc, 0 0730 074A wc, 0 07A6 07B0 wc, 0 0901 0902 wc, 0 093C 093C wc, 0 0941 0948 wc, 0 094D 094D wc, 0 0951 0954 wc, 0 0962 0963 wc, 0 0981 0981 wc, 0 09BC 09BC wc, 0 09C1 09C4 wc, 0 09CD 09CD wc, 0 09E2 09E3 wc, 0 0A01 0A02 wc, 0 0A3C 0A3C wc, 0 0A41 0A42 wc, 0 0A47 0A48 wc, 0 0A4B 0A4D wc, 0 0A70 0A71 wc, 0 0A81 0A82 wc, 0 0ABC 0ABC wc, 0 0AC1 0AC5 wc, 0 0AC7 0AC8 wc, 0 0ACD 0ACD wc, 0 0AE2 0AE3 wc, 0 0B01 0B01 wc, 0 0B3C 0B3C wc, 0 0B3F 0B3F wc, 0 0B41 0B43 wc, 0 0B4D 0B4D wc, 0 0B56 0B56 wc, 0 0B82 0B82 wc, 0 0BC0 0BC0 wc, 0 0BCD 0BCD wc, 0 0C3E 0C40 wc, 0 0C46 0C48 wc, 0 0C4A 0C4D wc, 0 0C55 0C56 wc, 0 0CBC 0CBC wc, 0 0CBF 0CBF wc, 0 0CC6 0CC6 wc, 0 0CCC 0CCD wc, 0 0D41 0D43 wc, 0 0D4D 0D4D wc, 0 0DCA 0DCA wc, 0 0DD2 0DD4 wc, 0 0DD6 0DD6 wc, 0 0E31 0E31 wc, 0 0E34 0E3A wc, 0 0E47 0E4E wc, 0 0EB1 0EB1 wc, 0 0EB4 0EB9 wc, 0 0EBB 0EBC wc, 0 0EC8 0ECD wc, 0 0F18 0F19 wc, 0 0F35 0F35 wc, 0 0F37 0F37 wc, 0 0F39 0F39 wc, 0 0F71 0F7E wc, 0 0F80 0F84 wc, 0 0F86 0F87 wc, 0 0F90 0F97 wc, 0 0F99 0FBC wc, 0 0FC6 0FC6 wc, 0 102D 1030 wc, 0 1032 1032 wc, 0 1036 1037 wc, 0 1039 1039 wc, 0 1058 1059 wc, 1 0000 1100 wc, 2 1100 115f wc, 0 1160 11FF wc, 0 1712 1714 wc, 0 1732 1734 wc, 0 1752 1753 wc, 0 1772 1773 wc, 0 17B4 17B5 wc, 0 17B7 17BD wc, 0 17C6 17C6 wc, 0 17C9 17D3 wc, 0 17DD 17DD wc, 0 180B 180D wc, 0 18A9 18A9 wc, 0 1920 1922 wc, 0 1927 1928 wc, 0 1932 1932 wc, 0 1939 193B wc, 0 200B 200F wc, 0 202A 202E wc, 0 2060 2063 wc, 0 206A 206F wc, 0 20D0 20EA wc, 2 2329 232A wc, 0 302A 302F wc, 2 2E80 303E wc, 0 3099 309A wc, 2 3040 A4CF wc, 2 AC00 D7A3 wc, 2 F900 FAFF wc, 0 FB1E FB1E wc, 0 FE00 FE0F wc, 0 FE20 FE23 wc, 2 FE30 FE6F wc, 0 FEFF FEFF wc, 2 FF00 FF60 wc, 2 FFE0 FFE6 wc, 0 FFF9 FFFB wc, 0 1D167 1D169 wc, 0 1D173 1D182 wc, 0 1D185 1D18B wc, 0 1D1AA 1D1AD wc, 2 20000 2FFFD wc, 2 30000 3FFFD wc, 0 E0001 E0001 wc, 0 E0020 E007F wc, 0 E0100 E01EF wc, here wc-table - Constant #wc-table \ inefficient table walk: : wcwidth ( xc -- n ) wc-table #wc-table over + swap ?DO dup I 2@ within IF I 2 cells + @ UNLOOP EXIT THEN 3 cells +LOOP 1 ; : x-width ( xcaddr u -- n ) 0 rot rot over + swap ?DO I xc@+ swap >r wcwidth + r> I - +LOOP ; : char ( "name" -- xc ) bl word count drop xc@+ nip ; : [char] ( "name" -- rt:xc ) char postpone Literal ; immediate \ switching encoding is only recommended at startup \ only two encodings are supported: UTF-8 and ISO-LATIN-1 80 Constant utf-8 100 Constant iso-latin-1 : set-encoding to maxascii ; : get-encoding maxascii ; base ! \ finis ===== hexadocu ===== \ hexadocu - sudoku in hex hex \ Diesmal ist es einfacher, alles im Hexmodus zu betreiben 12 Value spielebenen \ So viele Ebenen brauche ich Create Aufgabe \ ab hier wird die Aufgabe gespeichert ," E A 56 F8 " ," 65 E 18 F 03A" ," 3 7B 65 D 2 " ," 8 B 34 5 " ," 07 19 2 " ," 9B 2 0F7 8D 6" ," 5 E7 FBD16 C " ," D 6 3 2 0 A 7" ," D C 287 " ," 73BE 9C0A82 6 " ," A 1 7E B9 " ," 29 0 64D A " ," 476 F A0 2" ," B C3A 5480 EF " ," DE9 0C2 4 F5 18" ," F 5 B 19 4D3" Create Spielfeld \ Jetzt folgt der Speicherbereich fuer das Spiel 10 10 spielebenen * * \ so viele Bytes braucht's schon allot here value spielfeldende \ ---------------------------------------------- Verwaltung ------------------------- \ Da es nur ein Spielfeld gibt, wird mit absoluten Adressen gerechnet : ebene ( n -- adr ) \ gibt die Anfangsadresse der Ebene n zurueck 10 10 * * spielfeld + ; : zse>adr ( zeile spalte ebene -- adr ) \ gibt die Adresse der gewaehlten Speicherstelle 10 10 * * -rot swap 10 * + + spielfeld + ; : leer? ( zeile spalte ebene -- flag ) \ ist die gewaehlte Zelle leer? zse>adr c@ FF = ; : neu ( -- ) \ Aufgabe ins Spielfeld uebertragen Aufgabe 10 0 DO count 0 DO count bl = IF FF ELSE dup 1- 1 s>number drop THEN 0 ebene J 10 * i + + c! LOOP LOOP drop ; : vorbesetzen ( -- ) \ Jede Ebene wird mit 'ihrem' Wert gefuellt 10 0 DO 10 0 DO 10 0 DO K J I 1+ zse>adr I swap c! LOOP LOOP LOOP 11 ebene 10 10 * erase ; : fuelle_zeile ( n zeile ebene -- ) \ eine ganze Zeile mit einem Wert fuellen ebene swap 10 * + 10 rot fill ; : fuelle_spalte ( n spalte ebene -- ) \ eine ganze Spalte mit einem Wert fuellen ebene + 10 0 DO 2dup c! 10 + LOOP 2drop ; : fuelle_quadrat ( n Index ebene -- ) \ Ein Spielquadrat mit einem Wert fuellen (Index 0-F) ebene swap 4 /mod 40 * swap 4 * + + 4 0 DO 2dup swap 4 swap fill 10 + LOOP 2drop ; : loesche_saeule? ( zeile spalte -- ) \ bei besetzter Zelle alle Moeglichkeiten stornieren 2dup 0 leer? \ wenn Spielfeld (noch) leer ist ... IF 2drop \ tu nichts ELSE swap 10 * + 10 0 \ ansonsten: DO FF over I 1+ ebene + c! \ trage in jeder Ebene den Leermarker (FF) ein LOOP drop THEN ; : zaehle_saeule ( zeile spalte -- ) \ Eintraege einer Saeule ( moegliche Werte fuer die swap 10 * + \ entsprechende Zelle) zaehlen, Ergebnis in Ebene 11 0 pad ! \ speichern 10 0 DO dup I 1+ ebene + c@ FF <> IF 1 pad +! THEN LOOP 11 ebene + pad @ swap c! ; : zaehle_saeulen ( -- ) \ fuer jede Zelle die Anzahl der Moeglichkeiten ermitteln 10 0 DO 10 0 DO I J loesche_saeule? I J zaehle_saeule LOOP LOOP ; Variable gueltige_Zahl \ hier wird der gueltige Wert gespeichert : notiere_wert ( zeile spalte -- ) \ falls es nur einen Wert gibt, ihn notieren 0 gueltige_Zahl ! swap 10 * + dup 11 ebene + c@ 1 = IF 10 0 DO dup I 1+ ebene + c@ dup FF <> IF gueltige_Zahl +! ELSE drop THEN LOOP dup 11 ebene + 0 swap c! gueltige_Zahl @ swap 0 ebene + c! ELSE drop THEN ; : schreibe_zahlen ( -- ) \ fuer jede Zelle den 'einen' Wert notieren 10 0 DO 10 0 DO J I notiere_wert LOOP LOOP ; : wert! ( n zeile spalte -- ) \ einen Wert in eine Zelle schreiben swap 10 * + 0 ebene + c! ; \ ----------------------------- Erlaubte Zahlen pruefen ------------------------- : markiere_zeile ( zeile -- ) \ benutzte Zahlen als besetzt markieren (Zeilen/Ebene) dup 10 * spielfeld + 10 0 DO dup I + c@ dup FF <> \ zeile adr n flag IF FF 3 pick rot 1+ fuelle_zeile ELSE drop THEN LOOP 2drop ; : markiere_zeilen ( -- ) \ dies fuer alle Zeilen 10 0 DO I markiere_zeile LOOP ; : markiere_spalte ( spalte -- ) \ benutzte Zahlen als besetzt markieren (Spalten/Ebene) dup spielfeld + 10 0 DO dup I 10 * + c@ dup FF <> IF FF 3 pick rot 1+ fuelle_spalte ELSE drop THEN LOOP 2drop ; : markiere_spalten ( -- ) \ ... fuer alle Spalten 10 0 DO I markiere_spalte LOOP ; : sz>index ( spalte zeile -- index ) \ errechne aus Koordinaten den Index eines Quadrates 4 / swap 4 / 4 * + ; : markiere_quadrat ( Index -- ) \ benutzte Zahlen als besetzt markieren (Quadrate/Ebene) dup 4 /mod 40 * swap 4 * + 0 ebene + 4 0 DO 4 0 DO count dup FF <> IF 1+ 2 pick swap FF -rot fuelle_quadrat ELSE drop THEN LOOP 10 4 - + LOOP 2drop ; : markiere_quadrate ( -- ) \ fuer alle Quadrate 10 0 DO I markiere_quadrat LOOP ; : markiere_spiel \ alle Felder des Spieles pruefen und markieren markiere_quadrate markiere_zeilen markiere_spalten ; Variable minimum \ Speicherstelle : finde_minima ( -- ) \ welche 'leere' Zelle hat die wenigsten Loesungen 10 minimum ! 11 ebene 10 10 * 0 DO count dup 0<> IF minimum @ min minimum ! ELSE drop THEN LOOP drop ; : loese_1 ( -- flag ) \ alle Zellen, die eindeutig (1) sind, loesen vorbesetzen markiere_spiel zaehle_saeulen finde_minima minimum @ 1 = IF schreibe_zahlen true ELSE false THEN ; \ ----------------------------- Spielstaende merken ---------------------------- Create zug_stack 3 10 10 * * allot \ einen Stack fuer die Spielzuege einrichten Variable zug# 0 zug# \ sozusagen der Zug-Stackpointer : merke_zug ( n n n -- ) \ Wertetripel in den Zugstack schreiben 2dup zug# @ 3 * zug_stack + dup >R 2 + c! R@ 1 + c! 2 pick R> c! zug# @ 1+ 10 10 * mod zug# ! ; \ 'ueberfluessige' ? Ueberlaufsicherung : hole_zug ( n -- n n n ) \ Wertetripel vom Zugstack holen 3 * zug_stack + count swap count swap c@ ; : .zuege ( -- ) \ den Zugstack ausgeben (jeweils letzte Eintraege) 0 50 at ." Wert Zeile Spalte" zug# @ dup 18 - dup 0 <= IF drop 0 0 ELSE dup THEN -rot ?DO I over - 2+ 50 at I hole_zug rot 4 .r swap 6 .r 6 .r LOOP drop ; : zeige_dateien ( -- ) s" dir zuege*.f " evaluate ; \ der Name sagt's Variable datei_ID \ Platzhalter : save_zuege ( -- ) \ Zugstack (human readable) in Datei speichern s" zuege_" pad place base @ &10 base ! time&date rot 0 <# [char] _ hold # # #> pad +place swap 0 <# [char] _ hold # # #> pad +place 0 <# [char] _ hold # # # # #> pad +place 0 <# [char] : hold # # #> pad +place 0 <# [char] : hold # # #> pad +place 0 <# # # #> pad +place s" .f" pad +place base ! pad count r/w create-file cr drop datei_ID ! zug# @ 0 DO 3 0 DO J 3 * I + zug_stack + c@ 0 <# bl hold # # #> datei_ID @ write-file drop LOOP 0A pad c! pad 1 datei_ID @ write-file drop LOOP datei_ID @ close-file drop ." Getan!" ; : lade_zuege ( c-addr count -- ) \ Zugstack aus Datei fuellen 0 zug# ! r/o open-file IF datei_ID ! BEGIN pad &10 datei_ID @ read-file nip nip WHILE zug# @ 3 * zug_stack + 3 0 DO I over + pad I + 2 s>number drop swap c! LOOP drop zug# @ 1+ zug# ! REPEAT datei_ID @ close-file THEN ; \ ----------------------------- Statistik -------------------------------------- Create Doppelte 16 cells allot \ Speicherbereich fuer benutzte Werte Variable Fehler Fehler off \ flag : frei_vorbereiten ( -- ) \ Fehlerabfrage initialisieren 0 pad ! Fehler off Doppelte 16 FF fill ; : gueltig_zaehlen ( n -- ) \ sind Eintraege doppelt vorhanden? dup FF = IF drop 1 pad +! ELSE dup doppelte + dup c@ ff <> IF 2drop Fehler on bell ELSE c! THEN THEN ; : frei_quadrat ( index -- n ) \ alle Werte eines Spielquadrates miteinander vergleichen frei_vorbereiten 4 /mod 40 * swap 4 * + 0 ebene + 4 0 DO 4 0 DO count gueltig_zaehlen LOOP 10 4 - + LOOP drop pad @ ; : frei_zeile ( zeile -- n ) \ alle Werte einer Zeile miteinander vergleichen frei_vorbereiten 10 * 0 ebene + 10 0 DO count gueltig_zaehlen LOOP drop pad @ ; : frei_spalte ( spalte -- n ) \ alle Werte einer Spalte miteinander vergleichen frei_vorbereiten 0 ebene + 10 0 DO dup c@ gueltig_zaehlen 10 + LOOP drop pad @ ; : Moeglichkeiten ( -- d ) \ Moeglichkeiten errechnen (Ueberlauf!!!!!) 1 s>d 10 0 do 10 0 do J I 11 zse>adr c@ dup 0<> IF s>d d* ELSE drop THEN LOOP LOOP ; : .Moeglichkeiten ( -- ) \ Ausgeben Moeglichkeiten ." Moegl.: " ud. ; \ ----------------------------- Anzeige ---------------------------------------- : .wert ( c -- ) \ Spielfeldwert ausgeben, wenn gueltig! dup FF = IF drop ." " ELSE 2 .r THEN ; : .trenner ( -- ) ." +-----------+-----------+-----------+-----------+" ; : .spalten ( -- ) ." 0 1 2 3 4 5 6 7 8 9 A B C D E F" ; : .zeile ( adr -- adr ) ." |" 4 0 DO 3 0 DO count .wert ." " LOOP count .wert ." |" LOOP ; : .saeule ( zeile spalte -- ) \ alle Moeglichkeiten einer Zelle anzeigen 1 zse>adr 10 0 DO dup c@ dup FF <> IF . ELSE drop THEN 10 10 * + LOOP drop ; Variable Statistik? Statistik? on \ flag : .Fehler ( -- ) \ gibt ein X aus, falls ein Fehler bemerkt wurde Fehler @ IF ." X" ELSE ." " THEN ; : .ebene ( n -- ) \ Zeigt eine Spielfeldebene formatiert an page ebene cr .spalten cr .trenner 4 0 DO 4 0 DO cr j 4 * I + . .zeile j 4 * I + 2 .r Statistik? @ IF ." --> " J 4 * I + frei_zeile . .Fehler THEN LOOP cr .trenner Statistik? @ IF ." " 4 0 DO ." - " J 4 * I + frei_quadrat . .Fehler LOOP THEN LOOP drop cr .spalten Statistik? @ IF cr ." " 10 0 DO I frei_spalte . .Fehler LOOP THEN .zuege 1A 2 at ; : .spielfeld \ Zeigt das Spielfeld 0 .ebene ; \ \ : .wuerfel \ Tasteneingabe bei eingeschaltetem Numpad dort ... schei... 'key' \ cr 0 .ebene 1 \ BEGIN cr dup ." Ebene: " . \ key \ dup 1b = IF 2drop true THEN \ dup 38 ( FF52 ) = IF drop 1+ 11 min dup .ebene false then \ dup 32 ( FF54 ) = IF drop 1- 0 max dup .ebene false then \ UNTIL ; \ ------------------------- Kuerzel ----------------- : start ( -- ) neu vorbesetzen 0 .ebene ; : automatik ( -- n ) \ loest alle eindeutigen Zellen 0 >R Begin r> 1+ >R loese_1 false = UNTIL r> ; : au ( -- ) automatik drop \ zeigt das Spielfeld 0 .ebene ." " .moeglichkeiten ; : z! ( wert zeile spalte -- ) \ einen Wert bei den Koordinaten eintragen merke_zug wert! automatik 0 .ebene ." " . ." Zuege!" .moeglichkeiten ; : wiederhole ( -- ) \ alle Eintraege des Zugstacks nochmal abspielen neu zug# @ 0 ?DO I hole_zug wert! automatik drop LOOP 0 .ebene ." " .moeglichkeiten ; : ? ( zeile spalte -- werte ) \ Moegliche Werte fuer die Koordinaten anzeigen ." :" .saeule ; : .v 11 .ebene .moeglichkeiten ; \ Anzahl der Moeglichkeiten pro Zelle anzeigen : z ( -- ) zug# @ 1- 0 max zug# ! wiederhole ; \ einen Zug zurueckgehen : n ( -- ) zug# @ 1+ 100 min zug# ! wiederhole ; \ einen Zug wiederherstellen (nochmal) \ ----------------------- Loslegen ----------------- start 2 f 1 z! \ drei 6 8 0 z! \ moegliche E 5 E z! \ Eingaben \ 10 0 [do] [i] dup 6 swap z! [loop] \ 10 0 [do] [i] dup 7 swap z! [loop] \ finis ===== Regexp compile ===== \ Regexp compile \ Copyright (C) 2005 Free Software Foundation, Inc. \ This file is part of Gforth. \ Gforth is free software; you can redistribute it and/or \ modify it under the terms of the GNU General Public License \ as published by the Free Software Foundation; either version 2 \ of the License, or (at your option) any later version. \ This program is distributed in the hope that it will be useful, \ but WITHOUT ANY WARRANTY; without even the implied warranty of \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \ GNU General Public License for more details. \ You should have received a copy of the GNU General Public License \ along with this program; if not, write to the Free Software \ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. \ The idea of the parser is the following: \ As long as there's a match, continue \ On a mismatch, LEAVE. \ Insert appropriate control structures on alternative branches \ Keep the old pointer (backtracking) on the stack \ I try to keep the syntax as close to a real regexp system as possible \ All regexp stuff is compiled into one function as forward branching \ state machine \ special control structure : FORK ( compilation -- orig ; run-time f -- ) \ core POSTPONE call >mark ; immediate restrict : JOIN ( orig -- ) postpone THEN ; immediate restrict \ Charclasses : +bit ( addr n -- ) + 1 swap c! ; : -bit ( addr n -- ) + 0 swap c! ; : @+ ( addr -- n addr' ) dup @ swap cell+ ; 0 Value cur-class : charclass ( -- ) Create here dup to cur-class $100 dup allot erase ; : +char ( char -- ) cur-class swap +bit ; : -char ( char -- ) cur-class swap -bit ; : ..char ( start end -- ) 1+ swap ?DO I +char LOOP ; : or! ( n addr -- ) dup @ rot or swap ! ; : and! ( n addr -- ) dup @ rot and swap ! ; : +class ( class -- ) $100 0 ?DO @+ swap cur-class I + or! cell +LOOP drop ; : -class ( class -- ) $100 0 ?DO @+ swap invert cur-class I + and! cell +LOOP drop ; : char? ( addr class -- addr' flag ) >r count r> + c@ ; \ Charclass tests : c? ( addr class -- ) ]] char? 0= ?LEAVE [[ ; immediate : -c? ( addr class -- ) ]] char? ?LEAVE [[ ; immediate charclass digit '0 '9 ..char charclass blanks 0 bl ..char \ bl +char #tab +char #cr +char #lf +char ctrl L +char charclass letter 'a 'z ..char 'A 'Z ..char charclass any 0 $FF ..char #lf -char : \d ( addr -- addr' ) ]] digit c? [[ ; immediate : \s ( addr -- addr' ) ]] blanks c? [[ ; immediate : .? ( addr -- addr' ) ]] any c? [[ ; immediate : -\d ( addr -- addr' ) ]] digit -c? [[ ; immediate : -\s ( addr -- addr' ) ]] blanks -c? [[ ; immediate : ` ( -- ) ]] count [[ char ]] Literal <> ?LEAVE [[ ; immediate \ A word for string comparison : $= ( addr1 addr2 u -- f ) tuck compare ; : ,=" ( addr u -- ) tuck ]] dup SLiteral $= ?LEAVE Literal + noop [[ ; : =" ( " -- ) '" parse ,=" ; immediate \ loop stack Variable loops $40 3 * cells allot : 3@ ( addr -- a b c ) dup >r 2 cells + @ r> 2@ ; : 3! ( a b c addr -- ) dup >r 2! r> 2 cells + ! ; : loops> ( -- addr ) -3 loops +! loops @+ swap cells + 3@ ; : >loops ( addr -- ) loops @+ swap cells + 3! 3 loops +! ; : BEGIN, ( -- ) ]] BEGIN [[ >loops ; : DONE, ( -- ) loops @ IF loops> ]] DONE [[ THEN ]] noop [[ ; \ variables Variable vars &18 cells allot Variable varstack 9 cells allot Variable varsmax : >var ( -- addr ) vars @+ swap 2* cells + vars @ varstack @+ swap cells + ! 1 vars +! 1 varstack +! ; : var> ( -- addr ) -1 varstack +! varstack @+ swap cells + @ 1+ 2* cells vars + ; \ start end 0 Value end$ 0 Value start$ : !end ( addr u -- addr ) over + to end$ dup to start$ ; : $? ( addr -- addr flag ) dup end$ u< ; : ^? ( addr -- addr flag ) dup start$ u> ; : ?end ( addr -- addr ) ]] dup end$ u> ?LEAVE [[ ; immediate \ start and end : \^ ( addr -- addr ) ]] ^? ?LEAVE [[ ; immediate : \$ ( addr -- addr ) ]] $? ?LEAVE [[ ; immediate \ regexp block \ FORK/JOIN are like AHEAD THEN, but producing a call on AHEAD \ instead of a jump. : (( ( addr u -- ) vars off varsmax off loops off ]] FORK AHEAD BUT JOIN !end [[ BEGIN, ; immediate : )) ( -- addr f ) ]] ?end drop true EXIT [[ DONE, ]] drop false EXIT THEN [[ ; immediate \ greedy loops \ Idea: scan as many characters as possible, try the rest of the pattern \ and then back off one pattern at a time : drops ( n -- ) 1+ cells sp@ + sp! ; : {** ( addr -- addr addr ) 0 ]] Literal >r BEGIN dup [[ BEGIN, ; immediate ' {** Alias {++ ( addr -- addr addr ) immediate : n*} ( sys n -- ) >r ]] r> 1+ >r $? 0= UNTIL dup [[ DONE, ]] drop [[ r@ IF r@ ]] r@ Literal u< IF r> 1+ drops false EXIT THEN [[ THEN r@ ]] r> 1+ Literal U+DO FORK BUT [[ ]] IF I' I - [[ r@ 1- ]] Literal + drops true UNLOOP EXIT THEN LOOP [[ r@ IF r@ ]] Literal drops [[ THEN rdrop ]] false EXIT JOIN [[ ; immediate : **} 0 postpone n*} ; immediate : ++} 1 postpone n*} ; immediate \ non-greedy loops \ Idea: Try to match rest of the regexp, and if that fails, try match \ first expr and then try again rest of regexp. : {+ ( addr -- addr addr ) ]] BEGIN [[ BEGIN, ; immediate : {* ( addr -- addr addr ) ]] {+ dup FORK BUT IF drop true EXIT THEN [[ ; immediate : *} ( addr addr' -- addr' ) ]] dup end$ u> UNTIL [[ DONE, ]] drop false EXIT JOIN [[ ; immediate : +} ( addr addr' -- addr' ) ]] dup FORK BUT IF drop true EXIT [[ DONE, ]] drop false EXIT THEN *} [[ ; immediate : // ( -- ) ]] {* 1+ *} [[ ; immediate \ alternatives \ idea: try to match one alternative and then the rest of regexp. \ if that fails, jump back to second alternative : THENs ( sys -- ) BEGIN dup WHILE ]] THEN [[ REPEAT drop ; : {{ ( addr -- addr addr ) 0 ]] dup BEGIN [[ vars @ ; immediate : || ( addr addr -- addr addr ) vars @ varsmax @ max varsmax ! ]] nip AHEAD [[ >r >r >r vars ! ]] DONE drop dup [[ r> r> r> ]] BEGIN [[ vars @ ; immediate : }} ( addr addr -- addr addr ) vars @ varsmax @ max vars ! ]] nip AHEAD [[ >r >r >r drop ]] DONE drop LEAVE [[ r> r> r> THENs ; immediate \ match variables : \( ( addr -- addr ) ]] dup [[ >var ]] ALiteral ! [[ ; immediate : \) ( addr -- addr ) ]] dup [[ var> ]] ALiteral ! [[ ; immediate : \0 ( -- addr u ) start$ end$ over - ; : \: ( i -- ) Create 2* 1+ cells vars + , DOES> ( -- addr u ) @ 2@ tuck - ; : \:s ( n -- ) 0 ?DO I \: LOOP ; 9 \:s \1 \2 \3 \4 \5 \6 \7 \8 \9 \finis