FTH(1) | General Commands Manual | FTH(1) |
fth
—
fth |
[-DdQqrv ] [-C
so‐lib‐path]
[-E eval]
[-e eval]
[-f init‐file]
[-I fs‐path]
[-S “lib
init”] [-s
file] [file ...] |
fth |
[-alnp ] [-e
eval] [-F
fs]
[-i [suffix]]
[file ...] |
fth |
-V |
fth
based on John
Sadler's Ficl, Forth-inspired command
language. This manual page provides information for running and
programming fth
in a UNIX
command line environment, see libfth(3) for Forth as an
extension language.
The fth
interpreter can also be used as a
command line filter and process files in-place. The options
-a
, -e
,
-i
, -l
,
-n
, -p
and in addition
-F
are provided for this purpose, see
EXAMPLES below.
-a
-e
eval. The
split string is the field separator *fs*, default a
space. Can be combined with -n
,
-p
and in-place option
-i
[suffix]. See also options
-F
and -l
.-C
path-D
-d
-E
eval-e
.-e
eval-e
eval can be considered between
lambda: ( -- ) eval
;
and is in compile state! If multiple calls appear, all but the last are in interpret state.
For example, the following renames config.h to config.h.orig and reads it line by line. If a line occurs containing
#define
HAVE_COMPLEX_DOUBLE
replaces entire line with
/* #undef HAVE_COMPLEX_DOUBLE
*/
otherwise writes the original line to
config.h. The first -e
sets the global Forth variable reg to a regexp
object, the second -e
uses this variable for
testing. The eval of the first
-e
is evaluated in interpret state before
reading the file while the eval of the second
-e
is evaluated in compile state in a nameless
lambda:
(--
)
eval ; word while reading the file line by
line:
% fth -ni.orig \ -e '/#define HAVE_COMPLEX_DOUBLE/ value reg' \ -e 'reg *line* re= if \ "/* #undef HAVE_COMPLEX_DOUBLE */\n" \ else \ *line* \ then' config.h
-F
fs-a
. See
also implicit loop and in-place options -i
,
-l
, -n
and
-p
.-f
file-Q
to
prevent loading initialization files.
-I
path-i
[suffix]-e
eval on each
line in-place. If no suffix was specified, writes to
the same file after processing, otherwise copies original to the backup
file before reading it. See option -e
above for an
example and see also implicit loop options -a
,
-F
, -l
,
-n
-p
.
Note: There are no spaces allowed between
-i
and suffix if
suffix was specified!-l
\n
’ when used
with implicit loop options -n
,
-p
and in-place option
-i
[suffix]. See also options
-a
and -F
.-n
-e
eval
on each line while not echoing every
*line* to standard output. See also implicit loop
and in-place options -a
,
-F
, -i
,
-l
and -p
.-p
-e
eval
on each line while echoing every *line* unaltered to
standard output. See also implicit loop and in-place options
-a
, -F
,
-i
, -l
and
-n
.-Q
-q
-r
-r
changes this
behavior to something like a
read‐eval‐loop
instead of a
read‐eval‐print‐loop
.-S
“lib init”-S "dbm
Init_dbm"
Multiple calls are possible.
-s
file-s
file. This must be the
last option given to the fth
interpreter. Options
after that are taken as script options and can be used for
getopt
processing in the script. This makes it
possible to create hash bang scripts with a first line like
#! /usr/pkg/bin/fth
-s
Note the space after #!
. It's a Forth
word starting a comment and must be separated by at least one space.
% cat hello-world.fth #! /usr/pkg/bin/fth -s ·" Hello, World!" cr
-V
-v
-
’, reads from standard input, see
EXAMPLES below.( a b c -- d )
The left side of the double dash shows the stack required by the word, the ride side shows what the word left on stack. One or both sides may be empty.
( -- )
( a -- )
( -- a )
Parse words have the following kind of stack effect:
( "name" -- )
Parse words read their input from the input stream in contrary to
regular words which expect their input on stack;
"name" is expected after the word. As a
parse word example, fth
has
help ( "name" --
)
which is used like
help apropos
This word takes its arguments from the input stream, in this case apropos, and tries to find its help strings, which are returned if found.
There is another kind of words, so called procs (see in subsection
Procs And Xts, word
<{
), they may show a slightly different stack
effect because they can have keyword and optional arguments and may look
like this:
( :key host "localhost"
:optional port 13 -- str )
Local variables are automatically defined,
host
is set to string "localhost" and
port
to integer 13, the return value of this example
is a string object. You may specify these with :host
"pumpkin" and :port
79 before calling the word. For example,
${prefix}/share/fth/fth-lib/net.fs contains
finger ( :key host
"localhost" :optional user getlogin -- str )
which can be used in either of these forms:
finger \ default values for host and
user
"yorick" finger \ default
value for host
:host "pumpkin" finger \
default value for user
"yorick" :host
"pumpkin" finger
-a
was specified when implicit loop and
in-place processing takes place, these are the corresponding first nine
single elements of *farray*.-a
was specified when implicit loop and
in-place processing takes place, prints the corresponding single elements
of *farray* with the string of
*ofs* attached, default a single space.-a
was specified when implicit loop and
in-place processing takes place, auto-split array of current input
line.map ...
end-map
loops.fth
searches for C
extension libraries (*.so).fth
searches for Forth
script files (*.fs).REG_EXTENDED
.fth
calls five hooks if they are not empty: before and
after loading source files and in interactive mode before starting and after
ending the repl as well as every time before showing the prompt.
before-load-hook
( filename -- f )before-load-hook lambda: <{ fname -- f }> "\\ loading %s\n" #( fname ) fth-print #t ; add-hook!
after-load-hook
( filename -- )after-load-hook lambda: <{ fname -- }> "\\ %s loaded\n" #( fname ) fth-print ; add-hook!
before-repl-hook
( -- )before-repl-hook reset-hook! before-repl-hook lambda: <{ -- }> ." \" cr ." \ Starting FTH on " date .string ." !" cr ." \" cr ; add-hook!
after-repl-hook
( history -- )\ 20201116031253 0 "mail" .getservbyname
after-repl-hook lambda: <{ history -- }> \ Remove duplicates from history file. history readlines array-reverse! { hary } #() "" "" { nhary hline tline } hary array-length 0 ?do hary i array-ref to hline hary i 1+ array-ref to tline nhary hline array-member? unless nhary hline array-unshift tline array-unshift drop then 2 +loop history nhary writelines ; add-hook!
before-prompt-hook
( prompt pos -- new-prompt )before-prompt-hook lambda: <{ prompt pos -- new-prompt }> "fth (%d) ok " '( pos ) string-format ; add-hook!
Or with standout and bold mode, see gl_prompt_style(3) and Tecla Variables below for *promptstyle*:
#t to *promptstyle* before-prompt-hook lambda: <{ prompt pos -- new-prompt }> "%%Sfth (%d)%%s %%Bok%%b " '( pos ) string-format ; add-hook!
M-^V
and from vi-mode to emacs-mode via
M-^E
. To start in vi-mode, put a line like
edit-mode vi
in your ~/.teclarc file. A history of the
last command-lines can be listed with ^Xh
, the last
10 history entries can be listed with M-10^Xh
. The
Tab-key or ^I
initiates word completion. If the
Forth dictionary has more than one entry starting with characters before the
cursor, show all, if only one definition exists, completes it and adds a
space after the completed word. If the Forth dictionary has no entries
starting with the characters before the cursor, try filename completion. For
complete key listings and function descriptions, see
tecla(7).
If the first character of the command-line is an exclamation point
‘!
’, history expansion similar to
csh(1) takes place:
If the first character of the command-line is a caret
‘^
’, history substitution similar to
csh(1) takes place:
^search^replace(^)
^
is optional.gl-all
, only unique history events are
entered in the history list. If set to gl-prev
and
the last history event is the same as the current, the current command is
not entered. If not defined (undef, the default), all history events are
entered.FTH_HISTORY
or, if not set,
~/.fth-history is used.FTH_HISTORY_LENGTH
or, if not
set, 100 is used.bindkey
( :optional key action -- )bindkey
( -- )bindkey
( key -- )The following sets edit-mode to vi and nobeep:
"edit-mode vi \n
nobeep" bindkey
If key is a predefined constant, sets the specific value. The following constants are valid:
The following sets edit-mode to vi and nobeep with predefined constants:
gl-vi bindkey
gl-nobeep
bindkey
bindkey
( key action -- )"^G"
"user-interrupt" bindkey
If key is a string and action is anything else, unbind key from last bind.
"^G" #f
bindkey
See tecla(7) for bindings and actions.
history
( :optional action arg -- )gl-add
arggl-clear
gl-load
[arg]gl-save
[arg]gl-show
[arg]history ⇒ returns entire history as string gl-show history ⇒ same as above 10 history ⇒ returns 10 last history events gl-show 10 history ⇒ same as above gl-load history ⇒ loads from *histfile* gl-load nil history ⇒ same as above gl-load file history ⇒ loads from FILE gl-save history ⇒ saves to *histfile* gl-save nil history ⇒ same as above gl-save file history ⇒ saves to FILE gl-add line history ⇒ adds LINE to history gl-clear history ⇒ clears entire history
history-lineno
( -- n)history-next
( -- line)history-prev
( -- line)(?)do ... (+)loop
, begin
... again
, begin ... until
, and
begin ... while ... repeat
,
fth
provides each ... end-each
and map ... end-map
loops for arrays and similar
objects.
begin
( -- ) compile-onlyagain
( -- ) compile-onlybegin
and
again
over and over again. Press
^C
to stop the endless loop.
0 begin dup . 1+ again
begin
( -- ) compile-onlywhile
( f -- ) compile-onlyrepeat
( -- ) compile-onlywhile
and repeat
and
starts over after begin
for the next test.
"test.file" io-open-read { io } begin io io-eof? not while io io-gets fth-print repeat io io-close
begin
( -- ) compile-onlyuntil
( f -- ) compile-onlybegin
and
until
as long as flag f is
#f.
"localhost" :port 79 io-nopen { io } io "mike\n" io-puts begin io io-gets fth-print io io-eof? until io io-close
do
( limit start -- ) compile-only?do
( limit start -- ) compile-onlyloop
( -- ) compile-only?do
starts only if
limit is greater than start.
3 0 do i . loop ⇒ 0 1 2
each
( obj -- val ) compile-onlyend-each
( -- ) compile-only#( 0 1 2 ) each . end-each ⇒ 0 1 2
map
( obj -- ) compile-onlymap!
( obj -- ) compile-onlyend-map
( -- obj ) compile-onlyend-map
is set as new current value
of obj.
#( 0 1 2 ) value a1 a1 map i *key* + end-map ⇒ #( 0 2 4 ) \ a copy of a1 a1 .$ ⇒ #( 0 1 2 ) a1 map! i *key* + end-map ⇒ #( 0 2 4 ) \ a1 has changed a1 .$ ⇒ #( 0 2 4 )
Interpret state loops for use outside word definitions in scripts
or in the repl work like their compile state cousins above. The body of the
following [do] ... [loop]
, [each]
... [end-each]
and [map] ... [end-map]
is in
compile state, loop indexes i
,
j
, k
and
leave
etc can be used like in colon definitions.
[do]
( limit start -- )[loop]
( -- )3 0 [do] i . [loop] ⇒ 0 1 2
[each]
( obj -- val )[end-each]
( -- )#( 0 1 2 ) [each] . [end-each] ⇒ 0 1 2
[map]
( obj -- )[map!]
( obj -- )[end-map]
( -- obj )#( 0 1 2 ) value a1 a1 [map] i *key* + [end-map] ⇒ #( 0 2 4 ) \ a copy of a1 a1 .$ ⇒ #( 0 1 2 ) a1 [map!] i *key* + [end-map] ⇒ #( 0 2 4 ) \ a1 has changed a1 .$ ⇒ #( 0 2 4 )
#()
( -- ary ).array
( ary -- )array->array
( ary1 -- ary2 )#( 0 #{ 'foo 10 } 2 ) value ary1 ary1 array->array value ary2 ary1 1 array-ref 'foo 30 hash-set! ary1 ⇒ #( 0 #{ 'foo 30 } 2 ) ary2 ⇒ #( 0 #{ 'foo 30 } 2 )
array->list
( ary -- lst )#( 0 #{ 'foo 10 } 2 ) value ary ary array->list value lst ary 1 array-ref 'foo 30 hash-set! lst ⇒ '( 0 #{ 'foo 30 } 2 ) ary ⇒ #( 0 #{ 'foo 30 } 2 )
array-append
( ary1 ary2 -- ary1+ary2 )#( 0 1 2 ) #( 3 4 ) array-append ⇒ #( 0 1 2 3 4 ) #( 0 1 2 ) 10 array-append ⇒ #( 0 1 2 10 )
array-clear
( ary -- )array-compact
( ary1 -- ary2 )
array-compact!
array-compact!
changes
the original array contents.array-concat
( vals len -- ary ) alias:
>array
array-copy
( ary1 -- ary2 )#( 0 #{ 'foo 10 } 2 ) value ary1 ary1 array-copy value ary2 ary1 1 array-ref 'foo 30 hash-set! ary1 ⇒ #( 0 #{ 'foo 30 } 2 ) ary2 ⇒ #( 0 #{ 'foo 10 } 2 )
array-delete!
( ary idx -- val )array-delete-key
( ary key -- val )array-fill
( ary val -- )array-find
( ary key -- key )array-index
( ary key -- idx )array-insert
( ary idx val -- ary2 )
array-insert!
array-insert!
changes the original array
contents.array-join
( ary sep -- str )#( 0 1 2 ) "--" array-join ⇒ "0--1--2" #( 0 1 2 ) nil array-join ⇒ "0 1 2"
array-length
( obj -- len )array-member?
( ary key -- f )array-pop
( ary -- val )array-push
( ary val -- ary' )#( 0 1 2 ) 10 array-push ⇒ #( 0 1 2 10 )
array-ref
( ary idx -- val )array-reject
( ary1 proc-or-xt args -- ary2 )
array-reject!
array-reject!
changes the
original array contents. In the example n1 corresponds to the current
array element and n2 comes from args, here 2.
#( 0 1 2 3 4 ) lambda: <{ n1 n2 -- f }> n1 n2 > ; #( 2 ) array-reject ⇒ #( 0 1 2 )
The same a bit shorter:
#( 0 1 2 3 4 ) <'> > #( 2 ) array-reject ⇒ #( 0 1 2 )
array-reverse
( ary1 -- ary2 )
array-reverse!
array-reverse!
changes the original array
contents.array-set!
( ary idx val -- )array-shift
( ary -- val )array-sort
( ary1 proc-or-xt -- ary2 )
array-sort!
array-sort!
changes the original array contents.
#( 2 1 0 ) lambda: <{ a b -- f }> a b < if -1 else a b > if 1 else 0 then then ; array-sort ⇒ #( 0 1 2 )
array-subarray
( ary start end -- subary )#( 0 1 2 3 4 ) 2 4 array-subarray ⇒ #( 2 3 ) #( 0 1 2 3 4 ) -3 -1 array-subarray ⇒ #( 2 3 4 ) #( 0 1 2 3 4 ) -3 nil array-subarray ⇒ #( 2 3 4 )
array-uniq
( ary1 -- ary2 )
array-uniq!
array-uniq!
changes the original array
contents.array-unshift
( ary val -- ary' )#( 0 1 2 ) 10 array-unshift ⇒ #( 10 0 1 2 )
array=
( ary1 ary2 -- f )array?
( obj -- f )make-array
( len :key initial-element --
ary )>assoc
( vals len -- ary )array-assoc
( ass key -- ret )array-assoc-ref
( ass key -- val )array-assoc-remove!
( ass key -- 'ass )array-assoc-set!
( ass key val -- 'ass )assoc
( ass key val -- 'ass )#() value ass ass 'a 10 assoc ⇒ #a( '( 'a . 10 ) ) ass 'b 20 assoc ⇒ #a( '( 'a . 10 ) '( 'b . 20 ) )
assoc?
( obj -- f )'()
( -- lst ).list
( lst -- )>list
( vals len -- lst )car
( lst -- val )cadr
( lst -- val )caddr
( lst -- val )cadddr
( lst -- val )cdr
( lst -- val )cddr
( lst -- val )cons
( val lst1 -- lst2 )cons2
( val1 val2 lst1 -- lst2 )cons?
( obj -- f )last-pair
( lst -- lp )'( 0 1 2 3 ) last-pair ⇒ '( 3 )
list->array
( lst -- ary )'( 0 #{ 'foo 10 } 2 ) value lst1 lst1 list->array value ary2 lst1 1 list-ref 'foo 30 hash-set! lst1 ⇒ '( 0 #{ 'foo 30 } 2 ) ary2 ⇒ #( 0 #{ 'foo 30 } 2 )
list-append
( arg0 arg1 ... argn n -- lst )list-copy
( lst1 -- ary2 )'( 0 #{ 'foo 10 } 2 ) value lst1 lst1 list-copy value lst2 lst1 1 list-ref 'foo 30 hash-set! lst1 ⇒ '( 0 #{ 'foo 30 } 2 ) lst2 ⇒ '( 0 #{ 'foo 10 } 2 )
list-delete
( lst1 key -- lst2 )
list-delete!
list-delete!
changes the original list contents.
list-fill
( lst val -- lst' )list-head
( lst1 idx -- lst2 )list-index
( lst key -- idx )list-insert
( lst1 idx val -- lst2 )list-length
( obj -- len )list-member?
( lst key -- f )list-ref
( lst idx -- val )list-reverse
( lst1 -- ary2 )list-set!
( lst idx val -- )list-slice
( lst1 idx :key count 1 -- lst2 )
list-slice!
list-slice!
changes the original list contents.
#( 0 1 1 2 ) 1 :count 2 list-slice ⇒ #( 0 2 )
list-tail
( lst1 idx -- lst2 )list=
( lst1 lst2 -- f )list?
( obj -- f )make-list
( len :key initial-element nil --
lst )nil?
( obj -- f )null?
( obj --f )pair?
( obj -- f )set-car!
( lst val -- lst' )set-cdr!
( lst val -- lst' )>alist
( vals len -- alst )acons
( key val alst1 -- alst2 )list-assoc
( alst key -- ret )list-assoc-ref
( alst key -- val )list-assoc-remove!
( alst key -- alst' )list-assoc-set!
( alst key val -- alst' )"foo" 0o755 file-mkdir
file-atime
( name -- time )time->string
.file-basename
( name ext -- base )"/home/mike/cage.snd" #f file-basename ⇒ "cage.snd" "/home/mike/cage.snd" nil file-basename ⇒ "cage" "/home/mike/cage.snd" "nd" file-basename ⇒ "cage.s" "/home/mike/cage.snd" /\.(snd|wave)$/ file-basename ⇒ "cage"
file-chdir
( path -- ) alias:
chdir
HOME
.
path may contain ‘~’ as an
abbreviation for home directory, see chdir(2).file-chmod
( name mode -- )file-chroot
( path -- )file-copy
( src dst -- )file-ctime
( name -- time )time->string
.file-delete
( name -- )file-dir
( dir -- files-ary )file-dirname
( name -- path )"/home/mike/cage.snd" file-dirname ⇒ "/home/mike"
file-eval
( name -- )include
except that name must be on stack (include is a
parse word). With file-eval
one can load files
from within word definitions. Raises load-error
exception if file-eval
fails.file-fullpath
( name -- path )file-install
( src dst mode -- f ): install-lib { src dst mode -- } src dst mode file-install if "%s --> %04o %s" '( src mode dst ) else "%s is up-to-date" '( dst ) then fth-print cr ; "libsndlib.so" "/usr/opt/lib/s7" 0o755 install-lib
file-length
( name -- len )file-match-dir
( dir reg -- files-ary )file-mkdir
( name mode -- )file-mkfifo
( name mode -- )file-mtime
( name -- time )time->string
.file-pwd
( -- path )file-realpath
( name -- path )HOME
. If
realpath(3) function exists, returns resolved path,
otherwise returns name with ‘~’
replacement.
"~" file-realpath ⇒ "/home/mike" "/usr/pkg" file-chdir ⇒ "/usr/pkg" file-pwd ⇒ "/usr/pkg" "../bin" file-realpath ⇒ "/usr/bin"
file-rename
( src dst -- )file-rmdir
( name -- )file-shell
( cmd -- str ) alias:
shell
file-split
( name -- ary )"/home/mike/cage.snd" file-split ⇒ #( "/home/mike" "cage.snd" )
file-symlink
( src dst -- )file-system
( cmd -- f )file-touch
( name time -- )\ set modification time an hour earlier "main.c" current-time 3600 - file-touch \ reset modification time to current time "main.c" nil file-touch
file-truncate
( name size -- )open-file
and
close-file
, here are the corresponding pipe words.
open-pipe
( addr u fam -- fp ior )close-pipe
( fp -- ior )This example reads from the shell command pwd(1) and prints the result to standard output:
256 constant max-line create line-buffer max-line 2 + allot s" pwd" r/o open-pipe throw value FP line-buffer max-line FP read-line throw drop line-buffer swap type FP close-pipe throw
file-block?
file-character?
file-directory?
file-executable?
file-exists?
file-fifo?
file-grpowned?
file-owned?
file-readable?
file-setgid?
file-setuid?
file-socket?
file-sticky?
file-symlink?
file-writable?
file-zero?
.hash
( hash -- )>hash
( vals len -- hash )'foo 0 'bar 1 4 >hash ⇒ #{ 'foo ⇒ 0 'bar ⇒ 1 }
hash->array
( hash -- ass )#{ 'foo 0 'bar 1 } hash->array ⇒ #( #( 'foo 0 ) #( 'bar 1 ) )
hash-clear
( hash -- )hash-copy
( hash1 -- hash2 )hash-delete!
( hash key -- ary )hash-each
( hash proc-or-xt -- )#{ 'foo 0 'bar 1 } lambda: <{ key value -- }> "%s=%s\n" #( key value ) fth-print ; hash-each
hash-find
( hash key -- ary )hash-keys
( hash -- keys )hash-length
( obj -- len )hash-map
( hash1 proc-or-xt -- hash2 )#{ 'foo 0 'bar 1 } lambda: <{ key val -- val }> val 10 + ; hash-map ⇒ #{ 'foo => 10 'bar => 11 }
hash-member?
( hash key -- f )hash-ref
( hash key -- value )hash-set!
( hash key value -- )hash-values
( hash -- values )hash=
( obj1 obj2 -- f )hash?
( obj -- f )make-hash
( -- hash ) alias:
#{}
make-hash-with-len
( size -- hash )properties
), furthermore every object has its own
property-slot (object-properties
) as well as every
word (word-properties
) where key-value pairs can be
added.
Usage of properties
:
"hello" value obj obj 'hey "joe" property-set! obj 'hey property-ref ⇒ "joe" obj 'joe property-ref ⇒ #f obj properties ⇒ #{ 'hey => "joe" } #f properties ⇒ #{ "hello" => #{ 'hey => "joe" } }
properties
( obj -- props )property-ref
( obj key -- val )property-set!
( obj key val -- )Usage of object-properties
:
"hello" value obj obj 'hey "joe" object-property-set! obj 'hey object-property-ref ⇒ "joe" obj 'joe object-property-ref ⇒ #f obj object-properties ⇒ #{ 'hey => "joe" }
object-properties
( obj -- props )object-property-ref
( obj key -- val )object-property-set!
( obj key val -- )Usage of word-properties
:
<'> noop 'hey "joe" word-property-set! <'> noop 'hey word-property-ref ⇒ "joe" <'> noop 'joe word-property-ref ⇒ #f <'> noop word-properties ⇒ #{ 'hey => "joe" 'documentation => "noop" }
word-properties
( xt -- props )word-property-ref
( xt key -- val )word-property-set!
( xt key val -- ).hook
( hook -- )create-hook
( arity help "name"
-- )2 "A simple hook." create-hook my-new-hook #( 2 0 #f ) "A simple hook." create-hook my-new-hook
hook->array
( hook -- procs ) aliases:
hook->list hook-procs
hook-add
( hook proc-or-xt -- ) alias:
add-hook!
hook-apply
( hook args -- value-list ) alias:
run-hook
2 make-hook value hk1 hk1 <'> + 2 make-proc add-hook! hk1 #( 1 2 ) run-hook ⇒ #( 3 )
hook-arity
( hook -- arity )2 make-hook hook-arity ⇒ #( 2 0 #f )
hook-clear
( hook -- ) alias:
reset-hook!
hook-delete
( hook proc-or-name -- prc ) alias:
remove-hook!
hook-empty?
( hook -- f )hook-member?
( hook proc-or-name -- f )hook-name
( hook -- name )hook-names
( hook -- name-list )hook=
( obj1 obj2 -- f )hook?
( obj -- f )make-hook
( arity -- hook )2 make-hook value my-new-hook #( 2 0 #f ) make-hook value my-new-hook
io-read
and
io-write
work with file and pipe streams as well as
with strings and sockets.
set-*stdin*
( io -- old )set-*stdout*
( io -- old )set-*stderr*
( io -- old )version-control
( -- val )set-version-control
( val -- )io-open-file
( :key fam r/o args -- io )io-open-input-file
( :key args -- io )io-open-output-file
( :key args -- io )Examples
:filename "foo" io-open-file value io1 :command "ls -lF" io-open-file value io2 :string "test string" io-open-file value io3 :socket nil :port 79 io-open-file value io4 :soft-port "test" io-open-file value io5
io-open
( name :key fam r/o if-exists overwrite --
io )io-open-read
( name -- io )io-open-write
( name :key if-exists overwrite --
io )make-file-port
make-file-input-port
make-file-output-port
make-
words are aliases
for the corresponding io-
ones.The following constants are predefined for file access mode :fam:
If the keyword :if-exists was not specified, overwrites a possible existing file if opened for writing. The following :if-exists keywords are provided:
VERSION_CONTROL
and the global Fth variable
version-control.Examples
"in-test" io-open value ro1 "in-test" :fam r/o io-open value ro2 "out-test" :fam w/o io-open value wo1 "out-test" :fam w/o :if-exists :overwrite io-open value wo2 "out-test" :fam r/w :if-exists :error io-open value rw1 "out-test" :fam r/w :if-exists :rename io-open value rw2
io-popen
( cmd :key fam r/o -- io )io-popen-read
( cmd -- io )io-popen-write
( cmd -- io )make-pipe-port
make-pipe-input-port
make-pipe-output-port
io-popen
can be
opened read/write with :fam r/a.
The make-
words are aliases for the corresponding
io-
ones. If the global environment variable
FTH_POPEN_SHELL
is defined and has a shell
pathname, popen(3) uses this shell for execution.Examples
\ read pipe "ls -lAF ~/" io-popen-read value rio \ or #( "ls" "-lAF" "~/" ) io-popen value rio rio io->string fth-print rio io-close \ write pipe "cat" io-popen-write value wio wio "hello" io-puts wio io-close \ read/write pipe (only where mode "r+" is allowed) "fth -" :fam r/a io-popen value rwio rwio "80 f2c\n" io-puts rwio io-gets fth-print rwio io-close
io-sopen
( str :key fam r/o -- io )io-sopen-read
( str -- io )io-sopen-write
( str -- io )make-string-port
make-string-input-port
make-string-output-port
make-
words are aliases
for the corresponding io-
ones.Examples
"test-string" value s s io-sopen value rio rio io-read fth-print rio io-close s :fam w/a io-sopen value aio aio " with appended contents" io-write aio io-rewind aio io-read fth-print aio io-close s fth-print ⇒ "test-string with appended contents"
io-nopen
(host :key args -- io )make-socket-port
Opens a new socket server or connects to an already
established one. host is a host name
(AF_INET/AF_INET6) or a path name (AF_UNIX). If
host is not a string, "localhost" will
be used. port is the connection port (default
1024) if domain is AF_INET/AF_INET6, otherwise
unused, and domain can be AF_INET6 (default),
AF_INET or AF_UNIX. socket(2) is opened with
domain, type and hard-coded
flags 0. fam can be r/w
(default) or server
. In the latter case, the
returned IO object is opened as a server, otherwise the IO object is
opened as a client. If connect(2) can't open with
domain AF_INET6 (ipv6), tries it with AF_INET (ipv4) instead. It raises
socket-error exception if
socket(2) or connect(2) fails. The
make-
word is an alias.
Examples
"localhost" :port 25 io-nopen value io io "HELP\r\n" io-puts io io-gets fth-print io io-close
.io
( io -- )io->string
( io -- str )io-address
( io -- ip-addr )io-close
( io -- )io-closed?
( io -- f )io-eof?
( io -- f ) alias:
io-eos?
io-exit-status
( -- n ) alias:
exit-status
io-fdopen
( fd :key fam r/o -- io )2 :fam w/o io-fdopen value err-io err-io "our error log" io-write err-io io-flush ⇒ "our error log" (on standard error)
io-filename
( io -- name ) alias:
io-hostname
io-fileno
( io -- fd ) alias:
io-fd
io-flush
( io -- )io-getc
( io -- c )io-input?
( obj -- f )io-mode
( io -- mode )io-output?
( obj -- f )io-pos-ref
( io -- pos ) alias:
io-tell
io-pos-set!
( io pos -- )io-putc
( io c -- )io-read
( io -- line ) aliases:
io-gets io-recv
io-readlines
( io -- array-of-lines )io-reopen
( io1 name :key fam io1-fam --
io2 )"1-test" io-open-write value io1 io1 "hello" io-write io1 "2-test" io-reopen value io2 io1 io-closed? ⇒ #t io2 "world" io-write io2 io-close io2 io-closed? ⇒ #t "1-test" readlines ⇒ #( "hello" ) "2-test" readlines ⇒ #( "world" ) *stderr* "error.log" io-reopen value err-io
io-rewind
( io -- )io-seek
( io offset :key whence io-seek-set --
pos )io-select
( :key readfds writefds exceptfds timeout --
f )io-tmpfile
( -- io )io-write
( io line -- ) aliases:
io-puts io-send
io-write-format
( io fmt args -- )io-writelines
( io array-of-lines -- )io=
( obj1 obj2 -- f )io?
( obj -- f )readlines
( name -- array-of-lines )writelines
( name array-of-lines -- )fd-close
( obj -- )OPEN_MAX
(128), close(2) is
used.fd-dup2
( old-fd new-fd -- )fd-ioctl
( fd request flag -- res )FIOCLEX
FIONCLEX
FIONBIO
FIOASYNC
SIGIO
.FIONREAD
FIOSETOWN
SIGIO
and SIGURG
signals (sockets).FIOGETOWN
SIGIO
and
SIGURG
signals.net-eos?
( fd -- )make-sockaddr
( host port domain -- addr )net-sendto
and
net-recvfrom
.make-socket-io
( fd -- io ) alias:
net-fd->io
net-accept
( fd host domain -- io )net-bind
( fd host port domain -- )net-connect
( fd host port domain -- io )net-listen
( fd -- )net-recv
( fd flags -- msg )net-recvfrom
( fd flags addr -- msg )net-recvfrom2
( fd flags host port domain --
msg )net-send
( fd msg flags -- )net-sendto
( fd msg flags addr -- )net-sendto2
( fd msg flags host port domain
-- )net-shutdown
( fd how -- )net-socket
( domain type -- fd )net-socketpair
( type -- fd-ary )net-getpeername
( sfd -- name )net-getsockname
( sfd -- name )gethostbyaddr
( ip -- hash )
.gethostbyaddr
( ip
-- ).gethostbyaddr
prints these entries
nicely.gethostbyname
( host -- hash )
.gethostbyname
( host
-- ).gethostbyname
prints these entries
nicely.getservbyname
( serv -- hash )
.getservbyname
( serv --
).getservbyname
prints these entries nicely.getservbyport
( port -- hash )
.getservbyport
( port --
).getservbyport
prints these entries
nicely.h_errno
( -- n )hstrerrno
( n -- str )Socket Examples
This is the simple TCP client/server network example found in Chapter 6 of UNIX Network Programming (1990) by W. Richard Stevens.
\ \ The server reads one line at a time from the socket \ and writes each line back to the sender. \ : str-echo { so -- } nil { line } begin so io-gets to line line "\n" string<> while so line io-puts repeat ; \ \ The client reads one line from file IO and writes it to the socket, \ then reads a line back from the socket and writes it to *stdout*. \ : str-cli { so io -- } nil { line } begin io io-gets to line line ".\n" string<> while so line io-puts *stdout* so io-gets io-puts repeat so "\n" io-puts ; \ \ Callback for server. \ : echo-cb { so sfd -- prc; self -- } 0 proc-create ( prc ) sfd , so , does> { self -- } self @ ( sfd ) fd-close self cell+ @ { so } so str-echo %s finished\n" '( so ) fth-stderr so io-close ; \ \ Start the TCP server at one terminal. \ : tcp-server ( -- ) "0.0.0.0" { serv-addr } "localhost" { cli-addr } 6543 { port } AF_INET SOCK_STREAM net-socket { sfd } sfd serv-addr port AF_INET net-bind sfd net-listen begin sfd cli-addr AF_INET net-accept ( so ) sfd echo-cb fork ." pid " . cr again ; \ \ Start the TCP client at a second terminal and begin writing. \ The server echos each line back. \ : tcp-client ( -- ) "localhost" { serv-addr } 6543 { port } AF_INET SOCK_STREAM net-socket { sfd } serv-addr port AF_INET net-connect ( so ) *stdin* str-cli ;
FICL_VM_STATE_INTERPRET
INTERPRET_STATE
FICL_VM_STATE_COMPILE
COMPILE_STATE
state @ COMPILE_STATE = if \ compile state action here else \ interpret state action here then
Bytes
String Constants
add-feature
( feature -- )'snd add-feature
add-load-lib-path
( path -- )"/home/mike/lib/fth" add-load-lib-path
add-load-path
( path -- )"/home/mike/share/fth" add-load-path
apropos
( obj -- ary )/do/ apropos ⇒ #( "doLocal" ... )
dl-load
( "lib" "func"
-- )dl-load dbm Init_dbm
include
( "name" -- )HOME
if name begins with
this character. With include
one can load a file
more than once. Before loading name, runs hook
before-load-hook. After loading
name, runs hook
after-load-hook. Raises
no-such-file exception if file doesn't exist. Raises
load-error if an error occurred during load.
include hello
install
( "file" -- )-e
is
necessary because the last occurrence of -e
will
be compiled but install
is a parse word and won't
work in compile state.
\ from script: install snd-test.fs install sndlib.so \ from shell command prompt: % fth -ve 'install sndlib.so' -e ''
install-file
( file -- )\ from script: "snd-test.fs" install-file "sndlib.so" install-file \ from shell command prompt: % fth -ve '"sndlib.so" install-file'
load-init-file
( file -- )HOME
dir, loads it, otherwise does nothing.
Replaces ‘~’ with HOME
if
name begins with this character.
".my-fth-init" load-init-file
provided?
( obj -- f )'fth provided? ⇒ #t 'foo provided? ⇒ #f
require
( "name" -- )HOME
if
name begins with this character. With
require
one can load files only one time. Before
loading name, runs hook
before-load-hook. After loading
name, runs hook
after-load-hook. Raises
no-such-file exception if file doesn't exist. Raises
load-error if an error occurred during load.
require hello
unshift-load-lib-path
( path -- )"/home/mike/lib/fth" unshift-load-lib-path
unshift-load-path
( path -- )"/home/mike/share/fth" unshift-load-path
fth-catch
( ?? obj exc arg -- ?? res )If something went wrong, catching depends on exc and returning on arg. If exc is #t, all exceptions will be caught, if exc is an exception, only this exception will be caught.
The return value depends an arg. If arg is nil, '( exc exc-msg ) will be returned, if arg is a proc-or-xt, the result of this will be returned, if arg is neither nil nor a proc-or-xt, arg itself will be returned.
#( 0.3 0.3 0.3 ) value ary ary 2 <'> array-ref #t nil fth-catch ⇒ 0.3 #f ary 4 <'> array-ref 'out-of-range #t fth-catch ⇒ #( 0.3 0.3 0.3 ) 4 #t : ary-handler { retval -- val } "from handler: %S\n" #( retval ) fth-print #t ( return value ) ; ary 4 0.4 <'> array-set! 'out-of-range <'> ary-handler fth-catch prints: ⇒ from handler: \ #( 'out-of-range "array-set! (ary_set) arg 2: \ 4 is out of range" ) ⇒ 0.4 #t ary 2 0.4 <'> array-set! 'out-of-range <'> ary-handler fth-catch ⇒ #f
fth-raise
( exc fmt args -- )'bad-arity "%s: %s args required, got %s" #( proc 3 2 ) fth-throw ⇒ #<bad-arity in test-proc: 3 args required, got 2> #f #f #f fth-raise ⇒ reraise last exception
fth-throw
( exc args -- )\ \ ARGS: any object \ 'bad-arity proc fth-throw ⇒ #<bad-arity in test-proc> \ \ ARGS: nil or #() \ 'bad-arity nil fth-throw ⇒ #<bad-arity: proc has bad arity> \ \ ARGS: #( string ) \ 'bad-arity #( "test-proc" ) fth-throw ⇒ #<bad-arity in test-proc> \ \ ARGS: #( fmt arg1 arg2 arg3 ) \ 'bad-arity #( "%s: %s args required, got %s" proc 3 2 ) fth-throw ⇒ #<bad-arity in test-proc: 3 args required, got 2>
stack-reset
( ?? -- )current-time
( -- secs )date
( -- str )gmtime
( secs -- ary )localtime
( secs -- ary )mktime
( ary -- secs )#( 28 40 2 14 0 112 6 13 #f 3600 "CET" ) mktime
strftime
( fmt secs -- str )"%a %b %d %H:%M:%S %Z %Y" current-time strftime ⇒ "Sat Jan 14 02:40:28 CET 2012"
strptime
( str fmt -- secs )"2012 01 14" "%Y %m %d" strptime time->string ⇒ "Sat Jan 14 02:40:28 CET 2012"
time
( -- r )time->string
( secs -- str )current-time time->string ⇒ "Sat Jan 14 02:40:28 CET 2012"
time-reset
( -- )utime
( -- utime stime )utime ⇒ 0.171875 0.0234375
closelog
( -- )environ
( -- hash )errno
( -- n )exec
( cmd -- )FTH_POPEN_SHELL
or
SHELL
is set to a shell pathname, use this shell,
otherwise use /bin/sh
to execute
cmd. If cmd is an array of
strings, no shell expansion takes place and the first element of
cmd should be a program name. See
exec(3) for more.
\ string lambda: <{}> "man exec" exec ; fork 0 waitpid exit-status 0= \ array of strings lambda: <{}> #( "man" "exec" ) exec ; fork 0 waitpid exit-status 0=
fork
( xt -- pid )lambda: <{}> "man fork" exec ; fork 0 waitpid exit-status 0=
getegid
( -- id )getenv
( name -- value )geteuid
( -- id )getgid
( -- id )gethostname
( -- str ) alias:
hostname
getlogin
( -- str )getpid
( -- id )getppid
( -- id )getrusage
( -- ary )
.getrusage
( -- ).getrusage
prints these slots.getsid
( pid -- sid )getuid
( -- id )kill
( pid sig -- )SIGKILL
.openlog
( ident logopt facility -- )putenv
( name value -- )setegid
( id -- )seteuid
( id -- )setgid
( id -- )sethostname
( str -- )setsid
( -- sid )setuid
( id -- )signal
( sig xt -- old-xt )SIGINT lambda: { sig -- } "\nSIGINT (#%d) received\n" '( sig ) fth-stderr ; signal value old-xt
sleep
( secs -- )strerror
( n -- str )strsignal
( n -- str )umask
( :optional mask nil -- mask )0o022
’, this is zero letter o zero
two two. See umask(2).wait
( -- pid )lambda: <{}> #( "man" "wait" ) exec ; fork value pid wait pid = if ." finished" then
waitpid
( pid flags -- )See waitpid(2) for more.
lambda: <{}> #( "man" "waitpid" ) exec ; fork 0 waitpid exit-status 0=
getopt
( argv opts -- c )% cat getopt-test.fth #! /usr/pkg/bin/fth -s : main ( -- ) #f { bflag } #f { ffile } #t to opterr \ getopt prints error messages begin *argv* "bf:" getopt ( ch ) dup while ( ch ) case <char> b of #t to bflag endof <char> f of optarg to ffile endof <char> ? of "usage: [-b] [-f file]\n" .stderr 1 (bye) \ exit with return code 1 endof endcase repeat ( ch ) drop optind 0 ?do *argv* array-shift drop loop *argv* array-length to *argc* "-b: %s, -f: %s\n" #( bflag ffile ) fth-print ; main 0 (bye) \ exit with return code 0 % ./getopt-test.fth ⇒ -b: #f, -f: #f % ./getopt-test.fth -b ⇒ -b: #t, -f: #f % ./getopt-test.fth -bf outfile ⇒ -b: #t, -f: outfile % ./getopt-test.fth -f ⇒ fth: option requires an argument -- f ⇒ usage: [-b] [-f file] % ./getopt-test.fth -h ⇒ fth: illegal option -- h ⇒ usage: [-b] [-f file]
getopt-long
( argv opts longopts -- c ): long-test #f { bflag } #f { ffile } #f to opterr #( #( "flag" no-argument <char> b ) #( "file" required-argument <char> f ) ) { opts } begin *argv* "bf:" opts getopt-long ( ch ) dup while ( ch ) case <char> b of #t to bflag endof <char> f of optarg to ffile endof <char> ? of "-%c requires an argument" #( optopt ) fth-warning endof endcase repeat drop ( ch ) optind 0 ?do *argv* array-shift drop loop *argv* array-length to *argc* "-b, --flag (default #f): %s\n" #( bflag ) fth-print "-f, --file (default #f): %s\n" #( ffile ) fth-print ;
(bye)
( n -- )_exit!
( n -- ).cflags
( -- )
config-cflags
( --
str ).libs
( -- )
config-libs
( --
str ).long-version
( -- )
.version
( -- ).memory
( -- ).prefix
( -- )
config-prefix
( --
str )at-exit
( obj -- )lambda: <{ -- }> "test.file" file-delete ; at-exit
configure-args
( -- str )ver
( -- addr len )ver type ⇒ "1.4.2 (2020/11/20) [x86_64-pkgsrc-netbsd]"
bignum?
complex? even?
exact?
fixnum? float?
inexact?
inf? integer?
long-long?
nan? number?
odd?
prime? ratio?
rational?
ulong-long? unsigned?
0<
0<> 0>
0<=
0= 0>=
negative?
zero? positive?
<
<> >
<= =
>=
u<
u<> u>
u<=
u= u>=
d0<
d0<> d0>
d0<=
d0= d0>=
dnegative?
dzero? dpositive?
1 d0>= ⇒ #t 1.0 d0< ⇒ #f
d<
d<> d>
d<=
d= d>=
1 2.0 d< ⇒ #t 1.0 2 d= ⇒ #f
du<
du<> du>
du<=
du= du>=
1 2.0 du< ⇒ #t 1.0 2 du= ⇒ #f
c0<>
c0=
1.0+1.0i c0<> ⇒ #t 0.0+0.0i c0= ⇒ #t 1 c0= ⇒ #f
c<>
c=
1.0+1.0i 1+i c= ⇒ #t 1+0i 2.0 c<> ⇒ #t 1 1+0i c= ⇒ #t
b0<
b0<> b0>
b0<=
b0= b0>=
b<
b<> b>
b<=
b= b>=
q0<
q0<> q0>
q0<=
q0= q0>=
q<
q<> q>
q<=
q= q>=
f>s
( x -- n ) aliases:
d>s c>s b>s q>s
s>d
( x -- d ) aliases:
f>d >llong make-long-long
s>ud
( x -- ud ) aliases:
f>ud make-ulong-long
s>f
( x -- r ) aliases: d>f c>f b>f
q>fs>c
( x -- c ) aliases:
f>c q>c >c
s>b
( x -- b ) aliases:
f>b >bignum make-bignum
s>q
( x -- q ) aliases:
f>q c>q
1+
1- 2+ 2- 2* 2/
abs
negate
+ - *
/
max
min
d2*
d2/
dabs
dnegate
d+
d- d* d/
dmax
dmin
1/f
f2* f2/
fabs
fnegate
f+
f- f* f/
fmax
fmin
f**
alias: fpow
facos
( r1 -- r2 )facosh
( r1 -- r2 )falog
( r1 -- r2 )fasin
( r1 -- r2 )fasinh
( r1 -- r2 )fatan
( r1 -- r2 )fatan2
( r1 r2 -- r3 )fatanh
( r1 -- r2 )fceil
( r1 -- r2 )fcos
( r1 -- r2 )fcosh
( r1 -- r2 )fexp
( r1 -- r2 )fexpm1
( r1 -- r2 )flog
( r1 -- r2 )flog10
( r1 -- r2 )flog2
( r1 -- r2 )flog1p
( r1 -- r2 ) alias:
flogp1
floor
( r1 -- r2 )fround
( r1 -- r2 )fsin
( r1 -- r2 )fsincos
( r1 -- r2 r3 )fsinh
( r1 -- r2 )fsqrt
( r1 -- r2 )ftan
( r1 -- r2 )ftanh
( r1 -- r2 )ftrunc
( r1 -- r2 )fsincos
returns sin(r1) and cos(r1). r1 can be any type of
number but should fit in ficlFloat.
1/c
cnegate
c+
c- c* c/
c**
alias: cpow
Complex-I
( -- I )cabs
( c1 -- c2 )cabs2
( c1 -- c2 )cacos
( c1 -- c2 )cacosh
( c1 -- c2 )carg
( c1 -- c2 )casin
( c1 -- c2 )casinh
( c1 -- c2 )catan
( c1 -- c2 )catan2
( c1 c2 -- c3 )catanh
( c1 -- c2 )ccos
( c1 -- c2 )ccosh
( c1 -- c2 )cexp
( c1 -- c2 )clog
( c1 -- c2 )clog10
( c1 -- c2 )conj
( c1 -- c2 )conjugate
( c1 -- c2 )csin
( c1 -- c2 )csinh
( c1 -- c2 )csqrt
( c1 -- c2 )ctan
( c1 -- c2 )ctanh
( c1 -- c2 )magnitude
( c1 -- c2 )b2*
b2/
babs
bnegate
b+
b- b* b/
b**
alias: bpow
bmax
bmin
bgcd
( x y -- z )blcm
( x y -- z )broot
( b1 u -- b2 n )bsqrt
( b1 -- b2 n )bmod
( b1 b2 -- b3 n )b/mod
( b1 b2 -- b3 b4 n )blshift
( b1 n -- b2 )brshift
( b1 n -- b2 )1/q
qabs qceil qfloor qnegate
q+
q- q* q/
q**
qpow
fegetround
( -- n )fesetround
( n -- )See fenv(3), fegetround(3), and fesetround(3).
>complex
( r1 r2 -- c ) alias:
make-rectangular
denominator
( x -- n )exact->inexact
( x -- r )imag-ref
( x -- r ) alias:
image-ref
inexact->exact
( x -- n )inf
( -- inf )make-polar
( real theta -- c )make-ratio
( num den -- q )nan
( -- NaN )numerator
( x -- n )rationalize
( x err -- y )real-ref
( x -- r )rand-seed-ref
( -- seed )rand-seed-set!
( seed -- )frandom
( r -- -r...+r )random
( r -- 0.0..r ).r
( n1 n2 -- )17 3 .r ⇒ | 17 |
bn.
( b -- )17.0 bn. ⇒ |17 |
c.
( c -- )17.0+1.0i c. ⇒ |17.0+1.0i |
d.
( d -- )17 d. ⇒ |17 |
d.r
( d n -- )17 3 d.r ⇒ | 17 |
f.r
( r n -- )17.0 3 f.r ⇒ |17.000 |
q.
( q -- )17.0 q. ⇒ |17/1 |
u.r
( u n -- )17 3 u.r ⇒ | 17 |
ud.
( ud -- )17 ud. ⇒ |17 |
ud.r
( ud n -- )17 3 ud.r ⇒ | 17 |
uf.r
( r len-all len-after-comma -- )17.0 8 3 uf.r ⇒ | 17.000 | 17.0 8 2 uf.r ⇒ | 17.00 |
backtrace
( -- ) alias:
bt
frame-depth
( -- n ) alias: stack-levelobject-print-length
( -- n )
set-object-print-length
( n
-- )gc-marked?
( obj -- f )gc-protected?
( obj -- f )gc-permanent?
( obj -- f )gc-mark
( obj -- obj )gc-protect
( obj -- obj )gc-unmark
( obj -- obj )gc-unprotect
( obj -- obj )gc-on
( -- #f )gc-off
( -- #f )gc-run
( -- )gc-stats
( -- )gc-protected-objects
( -- ary )gc-permanent-objects
( -- ary )instance-gen-ref
( obj -- gen )instance-obj-ref
( obj -- gen )instance?
( obj -- f )instance-of?
( obj type -- f )make-instance
( gen obj -- instance )make-object-type
( name -- object-type )object-type?
( obj -- f )object-name
( obj -- name )object-type-ref
( obj -- struct )object-types
( -- ary )set-object->array
( xt obj -- )set-object->string
( xt obj -- )set-object-apply
( xt obj arity -- )set-object-copy
( xt obj -- )set-object-dump
( xt obj -- )set-object-equal-p
( xt obj -- )set-object-free
( xt obj -- )set-object-inspect
( xt obj -- )set-object-length
( xt obj -- )set-object-mark
( xt obj -- )set-object-value-ref
( xt obj -- )set-object-value-set
( xt obj -- )object-
xxx
word for obj type..inspect
( obj -- ).object-name
( obj -- )cycle-ref
( obj -- val )cycle-set!
( obj value -- )cycle-start!
( obj index -- )cycle-start@
( obj -- index )cycle-start0
( obj -- )first-ref
( obj -- val )first-set!
( obj value -- )second-ref
( obj -- val )second-set!
( obj value -- )third-ref
( obj -- val )third-set!
( obj value -- )last-ref
( obj -- val )last-set!
( obj value -- )hash-id
( obj -- id )object->array
( obj -- ary )object->string
( obj -- str )object-apply
( obj args -- result ) alias:
apply
fth_set_object_apply(vct_tag, vct_ref, 1, 0, 0); /* C */ <'> enved-ref fth-enved 1 set-object-apply \ Forth
object-copy
( obj1 -- obj2 )object-debug-hook
( obj -- hook )#( 0 1 ) value ary ary .inspect ⇒ #<array[2]: #<fixnum: 0> #<fixnum: 1>> ary object-debug-hook lambda: <{ str obj -- new-str }> "debug-inspect: %s" #( obj ) string-format ; add-hook! ary .inspect ⇒ #<debug-inspect: #( 0 1 )> ary object-debug-hook hook-clear ary .inspect ⇒ #<array[2]: #<fixnum: 0> #<fixnum: 1>>
object-empty?
( obj -- f ) alias:
empty?
object-equal?
( obj1 obj2 -- f ) alias:
equal?
object-id
( obj -- id )object-index
( obj key -- index ) alias:
index
object-find
( obj key -- value ) alias:
detect
object-member?
( obj key -- f ) alias:
member?
object-inspect
( obj -- str )object-dump
( obj -- str )object-length
( obj -- len ) alias:
length
object-range?
( obj index -- f ) alias:
range?
object-ref
( obj index -- val )object-set!
( obj index value -- )object-set+!
( obj index value -- )object-set-!
( obj index value -- )object-set*!
( obj index value -- )object-set/!
( obj index value -- )object-sort
( obj cmp-xt -- ary ) alias:
sort
: numb-sort { val1 val2 -- n } val1 val2 < if -1 else val1 val2 > if 1 else 0 then then ; #( 6 2 8 1 ) <'> numb-sort object-sort ⇒ #( 1 2 6 8 )
make-soft-port
words. For example one can split the
output to the snd(1) listener and to standard error or to a
log file.
Input Example
"in-test.text" io-open-read value *io* :port-name "read-soft" :read-line lambda: <{ -- line }> *io* io-read ; :close lambda: <{ -- }> *io* io-close ; make-soft-input-port value prt prt port-read ⇒ first line of in-test.text prt port-close
Output example
The output is split to the Snd listener with
snd-print
and to standard error with
.stderr
. snd-print returns its input string which we
use for .stderr. Nothing have to be closed and a :close proc isn't
necessary.
:port-name "sndout" :write-line lambda: <{ line -- }> line snd-print ( line ) .stderr ; make-soft-output-port value prt prt "hello" port-write
make-soft-port
( :key args -- prt ):fam
keyword was not specified.make-soft-input-port
( :key args -- prt )make-soft-output-port
( :key args -- prt )The words above know the following keywords. Not all procs are required.
The prt argument in the following words may be an IO object returned from io- or port-words, or #f. In the latter case uses standard input resp. standard output.
port->string
( prt -- str )port-close
( prt -- )port-closed?
( prt -- f )port-display
( prt obj -- )port-flush
( prt -- )port-getc
( prt -- c )port-gets
( prt -- str ) alias:
port-read
port-input?
( obj -- f )port-output?
( obj -- f )port-putc
( prt c -- )port-puts
( prt str -- ) alias:
port-write
port-puts-format
( prt fmt args -- ) alias:
port-write-format
#f "hello, %s" #( "world" ) port-puts-format ⇒ hello, world
port?
( obj -- f )with-input-port
( obj :key args -- str )
obj ( io --
str )with-input-from-port
( obj :key args -- str )
obj ( --
str )with-input-from-port
points the input IO to
*stdin*.If the obj argument for the input words is nil, they read the entire IO object, otherwise they execute obj as a proc-or-xt. After reading is finished they close the IO object and return the resulting string.
lambda: <{ io -- str }> io io->string ; :filename "file.test" with-input-port <'> io-readlines :command "ls -l" with-input-port nil :string "hello" with-input-port lambda: <{ -- str }> *stdin* io->string ; :filename "file.test" with-input-from-port nil :command "ls -l" with-input-from-port nil :string "hello" with-input-from-port
with-output-port
( obj :key args -- )
obj ( io
-- )with-output-to-port
( obj :key args -- )
obj ( -- )with-error-to-port
( obj :key args -- )
obj ( -- )with-output-to-port
points the output IO to
*stdout* and
with-error-to-port
points it to
*stderr*.If the obj argument for the output words is a string, they write the string to the IO object, if obj is an array, they write its contents to the IO object, otherwise they execute obj as a proc-or-xt. After writing is finished they close the IO object.
"hello" :filename "file.test" with-output-port "file.test" readlines :command "cat" with-output-to-port "" value s lambda: <{ io -- }> io "hello" io-write ; :string s with-output-port lambda: <{ -- }> \ all three do the same and put the string to *stdout*: \ *stdout* "hello" io-write \ #f "hello" port-write ." hello" ; :string s with-output-to-port lambda: <{ -- }> *stderr* "hello" io-write ; :string s with-error-to-port
.proc
( prc -- )<'set>
( "name" --
set-name|#f )128 <'set> object-print-length execute 128 set-object-print-length
<{
( -- ){ }
and {{ }}
this form handles two keywords,
:key and :optional. Variable names are
taken from keyword and optional names. This word can span over more than
one lines but without empty lines or comments in between. If
:key and :optional is used together,
:key must come first. All keyword and optional variables
must have default values. This word is immediate and compile only and can
only be used in word definitions.
: optkey-test <{ a b c :key d 10 e 20 :optional f 30 g 40 -- ary }> #( a b c d e f g ) ; 1 2 3 optkey-test ⇒ #( 1 2 3 10 20 30 40 ) :d 11 1 :e 22 2 3 4 optkey-test ⇒ #( 1 2 3 11 22 4 40 )
<{}>
( -- ): we-dont-need-args <{}> ; <'> we-dont-need-args proc? ⇒ #t
defined?
( "name" -- f )defined? 10 ⇒ #f defined? nil ⇒ #t defined? + ⇒ #t
doc"
( <ccc>" -- ): new-word ( -- ) doc" our documentation may contain \ \"double quotes\". Escape them with a backslash." \ we do nothing ; help new-word ⇒ new-word ( -- ) our documentation may contain "double quotes". Escape them with a backslash.
documentation-ref
( obj -- str )documentation-set!
( obj str -- )get-func-name
( -- name ): new-word get-func-name .$ space 10 ; new-word ⇒ new-word 10
get-optarg
( req def -- val ): optarg-test ( a b c=33 d=44 e=55 -- ary ) 4 55 get-optarg { e } 3 44 get-optarg { d } 2 33 get-optarg { c } { a b } #( a b c d e ) ; 1 2 optarg-test ⇒ #( 1 2 33 44 55 ) 1 2 3 4 optarg-test ⇒ #( 1 2 3 4 55 ) 1 2 3 4 5 6 7 optarg-test ⇒ 1 2 #( 3 4 5 6 7 )
get-optargs
( lst req -- vals ): optargs-test ( a b c=33 d=44 e=55 -- ary ) #( 33 44 55 ) 2 get-optargs { a b c d e } #( a b c d e ) ; 1 2 optargs-test ⇒ #( 1 2 33 44 55 ) 1 2 3 4 optargs-test ⇒ #( 1 2 3 4 55 ) 1 2 3 4 5 6 7 optargs-test ⇒ 1 2 #( 3 4 5 6 7 )
get-optkey
( key def -- val ): optkey-test ( start dur keyword-args -- ary ) :frequency 440.0 get-optkey { freq } :initial-phase pi get-optkey { phase } { start dur } #( start dur freq phase ) ; 0 1 optkey-test ⇒ #( 0.0 1.0 440.0 3.14159 ) 0 2 :frequency 330.0 optkey-test ⇒ #( 0.0 2.0 330.0 3.14159 )
get-optkeys
( ary req -- vals ): optkeys-test ( start dur keyword-args -- ary ) #( :frequency 440.0 :initial-phase pi ) 2 get-optkeys { start dur freq phase } #( start dur freq phase ) ; 0 1 optkeys-test ⇒ #( 0.0 1.0 440.0 3.14159 ) 0 2 :frequency 330.0 optkeys-test ⇒ #( 0.0 2.0 330.0 3.14159 )
help
( "name" -- )help make-array \ Forth word help array \ topic
help-add!
( obj str -- )help-ref
( obj -- str )help-set!
( obj str -- )#( "behemoth" "pumpkin" "mugli" ) value hosts hosts "local-net hostnames" help-set! hosts help-ref ⇒ "local-net hostnames"
lambda:
( -- xt )lambda: ( a b -- c ) + ; value plus plus help-ref ⇒ lambda: ( a b -- c ) 1 2 plus execute ⇒ 3 1 2 lambda: ( a b -- c ) * ; execute ⇒ 2
latestxt
( -- xt )local-variables
( -- vars ): word-with-locals { foo -- } 10 { bar } local-variables each .$ space end-each ; 20 word-with-locals ⇒ #{ "bar" => 10 "foo" => 20 }
make-proc
( xt arity -- prc )<'> + 2 make-proc ⇒ + lambda: ( a b -- c ) + ; #( 2 0 #f ) make-proc ⇒ lambda-009
proc->xt
( prc -- xt )proc-apply
( prc args -- res ) alias:
run-proc
<'> + 2 make-proc value plus plus #( 5 6 ) proc-apply ⇒ 11
proc-arity
( prc -- arity )proc-create
( arity -- prc )create
it goes with
does>
.
: input-fn ( gen -- proc; dir self -- r ) { gen } 1 proc-create \ returns proc with one argument gen , \ stores gen for later use in DOES does> { dir self -- r } \ dir (ignored here) self (address) self @ \ returns our gen readin \ returns readin value ; instrument: src-simp <{ start dur amp sr sr-env fname -- }> :file fname find-file make-readin { f } :input f INPUT-FN :srate sr make-src { sc } :envelope sr-env :duration dur make-env { en } start dur run i sc en env #f src amp f* *output* outa drop loop f mus-close drop ;instrument 0 1.5 0.5 0.2 #( 0 0 50 1 100 0 ) "fyow.snd" <'> src-simp with-sound
proc-name
( prc -- name )proc-source-ref
( proc-or-xt -- str )proc-source-set!
( prc str -- )proc?
( obj -- f )running-word
( -- xt ): new-word running-word xt->name .$ space 10 ; new-word ⇒ new-word 10
see2
( "name" -- )set!
( "name" -- )128 set! object-print-length 128 set-object-print-length
set-execute
( xt -- ?? )128 <'> object-print-length set-execute 128 set-object-print-length
set-xt
( xt1 -- xt2 )128 <'> object-print-length set-xt execute 128 set-object-print-length
source-file
( xt -- file )source-line
( xt -- line )source-ref
( obj -- str )source-set!
( obj str -- )thunk?
( obj -- f )trace-var
( var proc-or-xt -- )to
.
mus-array-print-length ⇒ 8 8 value *clm-array-print-length* <'> *clm-array-print-length* lambda: <{ val -- res }> val set-mus-array-print-length ; trace-var 24 to *clm-array-print-length* *clm-array-print-length* ⇒ 24 mus-array-print-length ⇒ 24 <'> *clm-array-print-length* untrace-var
untrace-var
( var -- )word?
( obj -- f )word-create
( name -- )does>
-part as body.
: make-setter ( name -- ; hs val self -- ) { name } name "!" $+ word-create name , does> { hs val self -- } hs self @ ( slot ) val hash-set! ; "user-time" make-setter ⇒ creates setter word 'user-time!' #{} value hs hs 3.2 user-time! hs ⇒ #{ "user-time" => 3.2 }
xt->name
( xt -- str )xt->origin
( xt -- str )xt?
( obj -- f )REG_EXTENDED
. The variable can be set with the
following predefined constants, if more than one are used, combine them
with or
:
For example, set matching to ignore case before creating a new regexp:
REG_EXTENDED REG_ICASE or to *re-syntax-options*
or
:
No default is set.
/
( <ccc>/ -- reg )/(B|b)+/ ⇒ /(B|b)+/
make-regexp
( str -- reg )"(B|b)+" make-regexp value reg
re-match
( reg str start -- n )/b+/ "aabaab" 0 re-match ⇒ 1 /b+/ "aabaac" 3 re-match ⇒ -1
re-search
( reg str start range -- n )/b+/ "aabaab" 2 1 re-search ⇒ 2 /b+/ "aabaab" 0 1 re-search ⇒ -1
re/
( space<ccc>/ -- reg )re/ (B|b)+/ ⇒ /(B|b)+/
regexp-match
( reg str -- len|#f ) aliases
regexp= re=
/.*(bar)/ value reg reg "foobar" regexp-match ⇒ 6 reg 0 apply ⇒ foobar reg 1 apply ⇒ bar reg 2 apply ⇒ #f
regexp-replace
( reg str1 replace -- str2 )/(foo)/ "foo-bar" "***\\1***" regexp-replace ⇒ ***foo***-bar
Note the double backslashes on back reference characters.
regexp-search
( reg str :key start 0 range -1 --
pos|#f )if
. If keyword
range is -1 (default), the entire string will be
searched.
/foo/ "foobar" :start 0 :range 6 regexp-search ⇒ #t (pos 0) /(bar)/ value reg reg "foobar" :start 0 :range 2 regexp-search ⇒ #f reg "foobar" :start 3 :range 2 regexp-search ⇒ 3 reg 0 apply ⇒ bar reg 1 apply ⇒ bar reg 2 apply ⇒ #f
regexp?
( obj -- f )/^$/ regexp? ⇒ #t
.string
( obj -- ) aliases:
.$ .g
.error
( obj -- )warning
( str -- ) alias:
warn
error
( str -- )die
( str -- )fth-warning
( fmt :optional args -- )fth-error
( fmt :optional args -- )fth-die
( fmt :optional args -- )#<warning: ...>
or in
#<error: ...>
and throw
error-exit exception or in
#<die: ...>
and exit interpreter with return
code 1 (EXIT_FAILURE); args is optional. See
string-format for
fmt description.
fth-format
( fmt :optional args -- str )fth-print
( fmt :optional args -- ).stdout
( obj -- ).stderr
( obj -- ).debug
( obj -- )fth-stdout
( fmt :optional args -- )fth-stderr
( fmt :optional args -- )fth-debug
( fmt :optional args -- ).debug
and fth-debug
wrap output
in #<DEBUG(F): ...>
. Ficl output and
standard output may differ as well as Ficl error output and standard
error. These words print to standard output and standard error.String Create Words
"
( <ccc>" -- str )"pumpkin" ⇒ "pumpkin"
""
( -- str )$"
( space<ccc>" -- str )$" pumpkin" ⇒ "pumpkin"
$>string
( addr len -- str )string>$
( str -- addr len )type
and evaluate
require this kind of string.
$cr
( -- str )$space
( -- str )$spaces
( n -- str )make-string
( len :key initial-element ' ' --
str )3 :initial-element <char> x make-string ⇒ "xxx"
string-format
( fmt args -- ) alias
format
"%04d %8.2f %b %X %o" #( 128 pi 255 255 255 ) string-format ⇒ "0128 3.14 11111111 FF 377"
The format string can have zero or more of the following flags:
#
b
, B
,
o
and O
conversion,
adds a zero before the output, for x
and
X
conversion, adds a ‘0x’
respective ‘0X’ before the output. For
a
, A
,
e
, E
,
f
, F
,
g
and G
conversion,
the result will always have a decimal point.-
0
The following conversion specifiers are known:
%
aAeEfFgG
c
bdouxX
p
object-inspect
.s
object->string
.S
m
object-dump
.General String Words
char?
( obj -- )string->array
( str -- ary )"foo" string->array ⇒ #( 102 111 111 )
string-append
( str1 str2 -- str3 ) alias:
$+
string-capitalize
( str1 -- str2 )
string-capitalize!
string-capitalize!
changes the original string
contents.string-chomp
( str1 -- str2 )
string-chomp!
\n
’ removed.
string-chomp!
changes the original string
contents.string-concat
( objs len -- str ) alias:
>string
0 1 2 " foo " "b" "a" "r" 7 >string ⇒ "012 foo bar"
string-copy
( str1 -- str2 )string-delete!
( str idx -- val )string-downcase
( str1 -- str2 )
string-downcase!
string-downcase!
changes the original
string contents.string-eval
( str -- ?? )7 "3 4 + +" string-eval ⇒ 14
string-eval-with-status
( str -- ?? status )BREAK
ERROR_EXIT
INNER_EXIT
OUT_OF_TEXT
RESTART
USER_EXIT
string-fill
( str char -- str' )string-find
( str1 key -- str2|#f )"hello world" "l" string-find ⇒ "llo world" "hello world" /ell/ string-find ⇒ "ello world"
string-index
( str key -- idx )"hello world" "orl" string-index ⇒ 7
string-insert!
( str idx val -- str' )string-length
( str -- len )string-member?
( str key -- f )"hello world" "ell" string-member? ⇒ #t "hello world" /ell/ string-member? ⇒ #t
string-pop
( str -- char )string-push
( str val -- str' ) alias:
<<
string-ref
( str idx -- val )string-replace
( str1 from to -- str2 )
string-replace!
string-replace!
changes the original string
contents.string-reverse
( str1-- str2 )
string-reverse!
string-reverse!
changes the original string
contents.string-set!
( str idx val -- )string-shift
( str -- char )string-split
( str sep -- ary )"foo:bar:baz" ":" string-split ⇒ #( "foo" "bar" "baz" ) "foo:bar:baz" "/:/" string-split ⇒ #( "foo" "bar" "baz" )
string-substring
( str1 start end -- str2 )"hello world" 2 4 string-substring ⇒ "ll" "hello world" -4 -2 string-substring ⇒ "or" "hello world" -4 nil string-substring ⇒ "orld"
string-unshift
( str val -- str' )string-upcase
( str1 -- str2 )
string-upcase!
string-upcase!
changes the original
string contents.string-cmp
( str1 str2 -- n )string<
( str1 str2 -- f )string<>
( str1 str2 -- f )string=
( str1 str2 -- f )string>
( str1 str2 -- f )string?
( obj -- f ).symbol
( sym -- )create-symbol
( "name" -- )create-symbol new-symbol ⇒ creates symbol 'new-symbol
make-symbol
( name -- sym )"new-symbol" make-symbol ⇒ 'new-symbol
symbol-name
( sym -- str )'new-symbol symbol-name ⇒ "new-symbol"
symbol=
( obj1 obj2 -- f )symbol?
( obj -- f ).keyword
( kw -- )create-keyword
( "name" -- )create-keyword new-keyword ⇒ creates keyword :new-keyword
keyword-name
( kw -- name ):new-keyword keyword-name ⇒ "new-keyword"
keyword=
( obj1 obj2 -- f )keyword?
( obj -- f )make-keyword
( name -- kw )"new-keyword" make-keyword ⇒ :new-keyword
.exception
( ex -- )create-exception
( msg "name" -- )"New test exception" create-exception new-exception ⇒ creates 'new-exception
exception-last-message-ref
( ex -- msg )exception-last-message-set!
( ex msg -- )exception-message-ref
( ex -- msg )exception-message-set!
( ex msg -- )exception-name
( ex -- name )'new-exception exception-name ⇒ "new-exception"
exception=
( obj1 obj2 -- f )exception?
( obj -- f )make-exception
( name msg -- ex )"New test exception" create-exception new-exception ⇒ creates 'new-exception
symbol->exception
( sym -- ex )fth
reads the following global environment variables:
FTH_DICTIONARY_SIZE
FTH_LOCALS_SIZE
FTH_RETURN_SIZE
FTH_STACK_SIZE
FTH_FTHPATH
FTH_HISTORY
FTH_HISTORY_LENGTH
FTH_INIT_FILE
FTH_LIBPATH
FTH_POPEN_SHELL
fth_popen()
as well as
exec
, if cmd is a string, use this shell for
execution of shell commands. If not defined,
fth_popen()
falls back to
/bin/sh, and exec
, if cmd
is a string, falls back to SHELL
or, if not set,
/bin/sh.Furthermore, fth
checks the following
shell variables:
fth
uses a global and a local initialization file and a
history file.
FTH_INIT_FILE
.FTH_HISTORY
.fth
utility exits 0 on success,
and >0 if an error occurs.
fth
interpreter:
Prints 32 Fahrenheit as Celsius (0.0):
% fth -e "32 f2c .
cr"
The following loads the C extension library
libxm.so with its initialization function
Init_libxm
() and the Forth source file
motif-script.fs, executes word
main
and exits:
% fth -S "libxm Init_libxm"
-e "main" motif-script.fs
Adds path ~/share/forth in front of *load-path*, loads Forth source file sound-script.fs and starts the repl. Initialization files ${prefix}/etc/fthrc and ~/.fthrc are loaded if they exist:
% fth -I ~/share/forth
sound-script.fs
Prints compiler and linker flags for
CFLAGS
and LIBS
:
% fth -e .cflags
% fth -e .libs
% fth -e "config-cflags .g space
config-libs .g cr"
Runs Forth script check.fth and provides
option -a
and option -b
with
argument 10 to the script which should handle at least
these two; exits if finished:
% fth -s check.fth -ab
10
If the first line in check.fth is
#! /usr/pkg/bin/fth -s
chmod 0755 check.fth
% ./check.fth -ab 10
Prints name (column 1) and login time (column 5) from the who(1) command output:
who | fth -ane '.*1* .*5*
cr'
The same with output field separator set to " == ":
who | fth -ane '" == " to
*ofs* .*1* .*5* cr'
Print login names and their uids found in /etc/passwd:
cat /etc/passwd | fth -aF: -e
'"%5s %s\n" #( *3* *1* ) fth-print'
If the only option is a dash
‘
’,
-
fth
reads from standard input and prints the result
to standard output. The following examples print the same result
(~26.7):
% echo "80 f2c" | tee foo |
fth -
% cat foo | fth -
% fth - < foo
% fth -
80 f2c <enter>
26.66666666666667
bye
%
W. Richard Stevens, UNIX Network Programming, Prentice Hall, Englewood Cliffs, N.J., 1990.
fth
version 1.4.2.
fth
is based on Ficl,
Forth-inspired command language, version 4.0.31 written by
John Sadler.
fth
and this manual page were written by
Michael Scholz
⟨mi-scholz@users.sourceforge.net⟩.
2022/09/29 | NetBSD 9.99.100 |