> How is Factor different from Forth?
> Forth is untyped, not garbage collected, and close to the machine. For flow control, Forth code tends to use immediate words. Variables and arrays tend to be global, and programs aren't usually written in a functional manner. Factor is very different from this. It is dynamically typed, offering a high degree of reflection. Unreferenced objects are garbage collected with a generational garbage collector. Factor code is a little bit more distant from the machine, though the C FFI allows using words like malloc and mmap. For flow control, Factor generally uses quotations, allowing flexible higher order functions; parsing words are used mostly for definitions and data literals. Variables are dynamically or statically scoped (see below), and arrays are just objects which don't need to be treated specially.
Though you're right, forths are unusually commercial. Forth is old, and in some ways it stayed old. A lot of people that use it now have used it since the 80s, so they're quite used to paying for compilers.
There's also the fact that forths are used in very niche places, and the companies that use them are happy to pay up.
You'll find a very open community with really nice people though.
: parse-digit ( u -- u -1 | 0 0 )
{ DUP $30 $3a WITHIN } { $30 - TRUE }
{ DUP $41 $5b WITHIN } { $37 - TRUE }
{ DUP $61 $7b WITHIN } { $57 - TRUE }
{ ." parse-digit 1: " TODO } 3 CASE ;
: add-digit ( u u -- u flag )
." add-digit 0: " TODO
;
: parse-digits-0 ( u addr len -- u flag )
{ OVER B@ -ROT { parse-digit } 2DIP ROT
{ { SWAP base B@ * + } 2DIP 1 /STRING parse-digits-0 } WHEN
} { DROP TRUE } ?COND ;
: parse-digits ( u addr len -- u flag )
{ parse-digits-0 } { DROP FALSE } ?COND ;
: parse-prefix-if-exists ( addr len -- addr len 0|1|2 )
DUP {
OVER B@
{ DUP $2d = } { DROP 1 /STRING 1 }
{ DUP $7e = } { DROP 1 /STRING 2 }
{ DROP 0 } 2 CASE
} { 0 } COND ;
: parse-base-if-exists ( addr len -- addr len base )
DUP {
OVER B@
{ DUP $23 = } { DROP 1 /STRING #10 }
{ DUP $24 = } { DROP 1 /STRING $10 }
{ DUP $25 = } { DROP 1 /STRING %10 }
{ DUP $5e = } { DROP 1 /STRING ^10 }
{ DROP 2DUP $3a SCAN DUP } {
1 /STRING { NIP - 1- 10 BASE B! 0 -ROT parse-digits } 2KEEP
ROT { ROT } {
." parse-base-if-exists 1: " TODO
} COND
\ ." parse-base-if-exists 2: " TODO
}
{ 2DROP base B@ } 5 CASE
} { base B@ } COND ;
: with-saved-base ( x*i { x*i -- x*j } -- x*j )
base B@ SWAP DIP base B! ;
: apply-prefix ( u 0|1|2 -- n )
{ } { NEGATE } { INVERT } 3 SWITCH ;
: parse-number ( addr len -- u -1 | 0 0 )
{ parse-base-if-exists base B! parse-prefix-if-exists
{ 0 -ROT parse-digits } DIP
SWAP { apply-prefix TRUE } { DROP FALSE } COND
} with-saved-base ;
(This being an incomplete rewrite of the bootstrapped text interpreter's number parser; as I said, nowhere near ready.)