\ 4tH source patcher - Copyright 2008, 2009 J.L. Bezemer \ You can redistribute this file and/or modify it under \ the terms of the GNU General Public License include lib/ncoding.4th \ for selected structure members include lib/files.4th \ for REWIND include lib/scanfile.4th \ for SCAN-FILE include lib/argopen.4th \ for ARG-OPEN include lib/row.4th \ for ROW include lib/startend.4th \ for STARTS? include lib/parsname.4th \ for PARSE-NAME include lib/padding.4th \ for padding the fields include lib/leading.4th \ for -LEADING \ *** \ Data section \ *** 256 constant #size \ size of array; assume all tokens used 151 constant #available \ how many tokens are still available 32 constant /mnemonic \ the size of a name_4th[] mnemonic 12 constant /cmds_mask \ the width of a cmds_4th.h column 12 constant /save_marg \ left margin of save_4th() 19 constant /load_marg \ left margin of load_4th() 13 constant /name_mask \ the width of a name_4th[] column 5 constant #name_cols \ the number of name_4th[] columns char ~ constant tilde \ ASCII code of ~ [hex] 35d [decimal] constant version# \ 4tH version for this script \ all comp_4th() types 0 enum _CONS_ enum _IMME_ constant _WORD_ \ check for correct 4tH version version# 4th# [negate] [+] [if] [abort] [then] \ structure for a word struct width +field name \ the name of the word nell +field length \ the length of the word 16 +field token \ C macro for the token 1 +field datatype \ datatype of the word 32 +field fixedparm \ any fixed parameter (e.g. constant) nell +field symbol \ number of symboltable entries nell +field correction \ any allocation corrections 16 +field delimiter \ any delimiters (e.g. strings) 32 +field cfunction \ the C function of an immediate word end-struct /word \ structure for a token struct 16 +field tname \ the name of the token 1 +field tparm \ does it take any parameters nell +field tcode \ numeric value of the token 32 +field tmnemo \ mnemonic of name_4th[] end-struct /token #size /mnemonic [*] string mnemonics \ array for mnemonics #size /word [*] string words \ array for words #size /token [*] string tokens \ array for token #size string temp \ string for premature read string #size string #using \ string for temporary formatting \ calculate addresses from index :this words does> swap /word * + ; :this tokens does> swap /token * + ; :this mnemonics does> swap /mnemonic * + ; \ translation table for booleans create booleans ," yes" true , ," no" false , NULL , \ look it up straight away :this booleans does> 2 string-key row 0= abort" Bad boolean" nip nip cell+ @c ; \ get the value \ translation table for datatypes create datatypes ," constant" _CONS_ , ," immediate" _IMME_ , ," word" _WORD_ , NULL , \ look it up straight away :this datatypes does> 2 string-key row 0= abort" Bad datatype" nip nip cell+ @c ; \ get the value variable #user-words \ number of defined words variable #user-tokens \ number of defined tokens variable #mnemonics \ number of defined mnemonics \ *** \ Generic words \ *** ( a1 n1 a2 n2 -- a1 n1 f) : marker? tib count -leading 2over starts? nip nip ; : "," [char] , emit ; ( --) : restore-line temp count type cr ; ( --) : save-line 0 parse temp place ; ( --) : ?number number error? abort" Bad number" ; : DONE ." .. done" cr ; ( --) : .tib 0 parse type cr ; ( ..) : .all begin refill while .tib repeat ; \ copies everything up to marker : >marker ( a n --) begin refill \ get new line while \ compare it to marker marker? 0= \ marker found? while \ if marker not found .tib \ print the line repeat 2drop \ drop marker ; : skip>marker ( a n --) begin refill \ get new line while \ compare it to marker marker? 0= \ marker found? while \ if marker not found repeat 2drop \ drop marker ; \ inserts parameter tokens : io-user ( n --) >r #user-tokens @ 0 ?do \ save margin i tokens -> tparm c@ \ get the parameter if \ if it is set j spaces \ write margin spaces and the token ." case (" i tokens -> tname count type ." ):" cr then loop .tib r> drop \ write line in tib ; \ *** \ This subsystem reads the initial definitions \ *** \ read word from file : read-word ( n -- n+1) >r \ save index tilde parse tuck r@ words -> name place r@ words -> length n! \ copy data to structure tilde parse r@ words -> token place tilde parse datatypes r@ words -> datatype c! tilde parse r@ words -> fixedparm place tilde parse ?number r@ words -> symbol n! tilde parse ?number r@ words -> correction n! tilde parse r@ words -> delimiter place tilde parse r@ words -> Cfunction place r> 1+ \ increase index ; \ read a token from file : read-token ( n -- n+1) >r \ save index tilde parse r@ tokens -> tname place tilde parse booleans r@ tokens -> tparm c! tilde parse r@ tokens -> tmnemo place r> 1+ \ increase index ; \ read tokens from file : read-tokens ( h -- h) s" [tokens]" scan-file 0= abort" Cannot find [tokens]" 2drop 0 begin refill while tib count -trailing nip while read-token repeat dup #available > abort" Too many tokens" dup #user-tokens ! . ." tokens read" cr ; \ read words from file : read-words ( h -- h) s" [words]" scan-file 0= abort" Cannot find [words]" 2drop 0 begin refill while tib count -trailing nip while read-word repeat dup #user-words ! . ." words read" cr ; \ *** \ This subsystem patches cmds_4th.h \ *** \ read current tokens : read-cmds ( -- n) 0 begin \ start with 0 tib count s" /* ranges */" starts? 0= while \ all tokens read 2drop parse-name nip \ if not an empty line if parse-name 2drop parse-name ?number max then refill 0= abort" Cannot find /* ranges */" repeat \ take the higher token 2drop save-line \ save /* ranges */ line ; \ assign number to new tokens : scan-cmds ( --) s" #define NOOP" scan-file 0= abort" Cannot find NOOP token" 2drop read-cmds #user-tokens @ 0 ?do 1+ dup i tokens -> tcode n! loop drop ; \ terminate cmds_4th.h properly : cmds-lastword ( --) restore-line \ write saved line ." #define LastWord4th " #user-tokens @ 1- tokens -> tname count type cr s" #define LastWord4th" scan-file 0= abort" Cannot find LastWord4th" 2drop ; \ update LastWord4th \ write the user tokens : cmds-user ( --) #user-tokens @ 0 ?do \ add all user tokens ." #define " i tokens -> tname count tuck type /cmds_mask swap - i tokens -> tcode n@ swap .r cr loop cr ; \ write new cmds_4th.h : write-cmds ( --) s" cmds_4th.h" output open error? abort" Cannot open cmds_4th.h" dup use s" /* ranges */" >marker cmds-user cmds-lastword .all close ; \ open cmds_4th.txt file : cmds_4th ( --) s" cmds_4th.txt" input open error? abort" Cannot open cmds_4th.txt" dup use scan-cmds dup rewind abort" Cannot rewind cmds_4th.txt" write-cmds close DONE \ write tokens and signal we're done ; \ *** \ This subsystem patches save_4th.c \ *** \ write the new save_4th.c : write-save ( --) s" save_4th.c" output open error? abort" Cannot open save_4th.c" dup use s" case (CALL):" >marker /save_marg io-user .all close ; \ search marker and insert tokens \ open save_4th.txt file : save_4th ( --) s" save_4th.txt" input open error? abort" Cannot open save_4th.txt" dup use write-save close DONE \ write new file and done! ; \ *** \ This subsystem patches load_4th.c \ *** \ write the new save_4th.c : write-load ( --) s" load_4th.c" output open error? abort" Cannot open load_4th.c" dup use s" case (CALL):" >marker /load_marg io-user .all close ; \ search marker and insert tokens \ open load_4th.txt file : load_4th ( --) s" load_4th.txt" input open error? abort" Cannot open load_4th.txt" dup use write-load close DONE \ write new file and done ; \ *** \ This subsystem patches name_4th.c \ *** \ get the system defined mnemonics : get-mnemonics ( -- n) 0 >r begin \ reset count refill \ get a new line while \ when not end-of -file tib count -leading -trailing s" };" compare while \ when not end-of function() begin parse-name dup while r@ mnemonics place r> 1+ >r repeat 2drop \ add mnemonic to the table repeat save-line s" ," r@ 1- mnemonics +place r> ; \ save the final mnemonic \ add the user mnemonics : add-mnemonics ( n --) #user-tokens @ tuck 0 ?do \ for all user mnemonics dup i tokens -> tmnemo count rot i + mnemonics dup >r place s" ," r> +place loop + #mnemonics ! \ add them to the table ; \ save total mnemonics \ format the currently printed mnemonic : format-mnemonic ( n1 -- a n2) #name_cols mod if space else cr 2 spaces then mnemonics count ; \ decide which layout for mnemonic \ write all mnemonics : write-mnemonics ( --) #mnemonics @ 1- dup 0 ?do \ for all but one mnemonics i dup format-mnemonic /name_mask .padding loop dup format-mnemonic 1- type cr restore-line ; \ print them \ controls all mnemonics writing : name-user ( --) get-mnemonics add-mnemonics write-mnemonics ; \ get mnemonics, add the users', write \ write the new name_4th.c : write-name ( --) s" name_4th.c" output open error? abort" Cannot open name_4th.c" dup use s" char *name_4th [] = {" >marker 0 parse type name-user close ; \ open name_4th.txt file : name_4th ( --) s" name_4th.txt" input open error? abort" Cannot open name_4th.txt" dup use write-name close DONE \ write new file and done ; \ *** \ This subsystem patches exec_4th.c \ *** \ write custom I/O functions : custom-io ( hp ho a n -- hp ho) 2drop ." /* Custom I/O functions added by 'patch4th' */" cr cr .tib s" [vm.support]" >marker cr cr dup use s" /* This function is roughly equivalent with Forths <# */" skip>marker ; \ write standard I/O functions : standard-io ( hp ho a n -- hp ho) 2drop restore-line dup use s" /* This function is roughly equivalent with Forths <# */" >marker ; \ write the new exec_4th.c : write-exec ( hp ho -- hp ho) s" exec_4th.c" output open error? abort" Cannot open exec_4th.c" >r r@ use s" #include " >marker .tib over use s" [vm.include]" scan-file 0= abort" Cannot find [vm.include]" 2drop s" [vm.globals]" >marker dup use s" static jmp_buf Thrown;" >marker .tib over use s" [vm.io]" >marker dup use s" /* This baby simply prints a single character." >marker save-line over use refill 0= abort" Cannot find [vm.support]" s" [vm.support]" marker? if standard-io else custom-io then .tib s" Main routine and the only external." >marker save-line ." User code added by 'patch4th'" cr ." */" cr cr cr over use s" [vm.vars]" >marker cr cr ." /*" cr restore-line dup use s" va_list Vals;" >marker .tib over use s" [vm.extension]" >marker dup use s" default:" >marker save-line over use s" [immediate.words]" >marker restore-line dup use .all r> close ; \ open name_4th.txt file : exec_4th ( --) s" exec_4th.txt" input open error? abort" Cannot open exec_4th.txt" dup use write-exec close DONE \ write new file and done ; \ *** \ token insertion subsystem \ *** \ several formatting words : .{ spaces ." { " ; \ print an aligned curly brace : >"string" #using >r s| "| r@ place count r@ +place s| "| r> +place ; : >string count #using place ; \ copy string to #using : +, s" ," #using +place ; \ append a comma to #using : .padded #using count rot .padding ; \ print #using with padding : .blurted #using count type ; \ print #using without formatting : .} count type ." }," cr ; \ finish the line \ this word prints a constant token : .constant 9 .{ dup words -> length n@ 0 .r "," space dup words -> name >"string" +, .blurted space dup words -> token >string +, .blurted space words -> fixedparm .} ; \ this word prints an immediate token : .immediate ( i --) 2 .{ dup words -> length n@ 0 .r "," dup words -> symbol n@ 3 .r "," dup words -> correction n@ 3 .r "," space dup words -> name >"string" +, 15 .padded dup words -> delimiter >string +, 7 .padded words -> cfunction .} ; \ this words print a simple word : .word 9 .{ dup words -> length n@ 0 .r "," space dup words -> name >"string" +, .blurted space words -> token .} ; \ matching methods with types create methods _CONS_ , ' .constant , _IMME_ , ' .immediate , _WORD_ , ' .word , NULL , :this methods does> 2 num-key row 0= abort" Bad datatype" nip cell+ @c execute ; \ get the size from the table : get-size ( -- n) parse-name 2drop \ drop curly brace [char] , parse -leading -trailing \ get the number number error? if drop max-n then 0 >in ! ; \ check if it is a valid number \ check if token should be included : applicable? ( t l i -- i t f) >r r@ words -> length n@ = ( t f) over r@ words -> datatype c@ = and ( t f) r> -rot ( i t f) ; \ insert tokens at appropriate place : insert-tokens ( t l i -- t l i) 2dup 2>r width min 1+ swap do #user-words @ 0 ?do \ if a word is applicable dup j i applicable? if methods else drop drop then loop \ apply the appropriate method (print) loop 2r> ; \ this checks the table against value ( t l -- t l i f) : size? get-size over over > dup >r 0= if insert-tokens then r> .tib ; \ inner loop of token detection : next? ( l -- l f) begin max-n refill while drop size? while drop repeat nip dup max-n <> ; \ drop lower count and create flag \ outer loop of token detection : patch-tokens 1 begin next? while 1+ repeat 2drop ; \ words for patching in tokens \ *** \ This subsystem patches comp_4th.c \ *** \ write the new name_4th.c : write-comp ( --) s" comp_4th.c" output open error? abort" Cannot open comp_4th.c" dup use s" This list contains all immediate words." >marker save-line ." User code added by 'patch4th'" cr ." */" cr cr cr >r >r dup use r> r> .all cr cr ." /*" cr restore-line over use s" static Immed4th ImmedList [] = {" >marker .tib _IMME_ patch-tokens s" static Word4th WordList [] = {" >marker .tib _WORD_ patch-tokens s" static Const4th ConstList [] = {" >marker .tib _CONS_ patch-tokens over use .all close ; \ open comp_4th.txt file : comp_4th ( --) s" comp_4th.txt" input open error? abort" Cannot open comp_4th.txt" dup use write-comp close DONE \ write new file and done ; \ *** \ This is the control subsystem that monitors all patching \ *** \ main word : patch-4th ( --) argn 2 < abort" Usage: patch4th patch-file" ." Opening " 1 dup args type cr input swap arg-open ." .. " read-tokens \ read tokens ." .. " read-words \ read words ." Processing cmds_4th.txt" cr cmds_4th ." Processing save_4th.txt" cr save_4th ." Processing load_4th.txt" cr load_4th ." Processing name_4th.txt" cr name_4th ." Processing exec_4th.txt" cr exec_4th ." Processing comp_4th.txt" cr comp_4th ." Closing " 1 args type cr close \ close file DONE \ signal all done ; patch-4th