Skip to content

enif77/EFrt

Repository files navigation

EFrt

EFrt is a embeddable FORTH language implementation.

Packages

NuGet Version Downloads
EFrt EFrt EFrt
EFrt.Core EFrt.Core EFrt.Core
EFrt.Libs.Core EFrt.Libs.Core EFrt.Libs.Core
EFrt.Libs.CoreExt EFrt.Libs.CoreExt EFrt.Libs.CoreExt
EFrt.Libs.Double EFrt.Libs.Double EFrt.Libs.Double
EFrt.Libs.DoubleExt EFrt.Libs.DoubleExt EFrt.Libs.DoubleExt
EFrt.Libs.Exception EFrt.Libs.Exception EFrt.Libs.Exception
EFrt.Libs.Floating EFrt.Libs.Floating EFrt.Libs.Floating
EFrt.Libs.FloatingExt EFrt.Libs.FloatingExt EFrt.Libs.FloatingExt
EFrt.Libs.Object EFrt.Libs.Object EFrt.Libs.Object
EFrt.Libs.String EFrt.Libs.String EFrt.Libs.String
EFrt.Libs.Tools EFrt.Libs.Tools EFrt.Libs.Tools
EFrt.Libs.ToolsExt EFrt.Libs.ToolsExt EFrt.Libs.ToolsExt

Links

Forth related links

General links

CREATE or BUILDS?

Data types

  • cell: A 32 bit data unit (int).
  • single cell integer: 32 bit signed integer number (int, 1 cell). Ex.: 123
  • double cell integer: 64 bit signed integer number (long, 2 cells). Ex.: 123L
  • char: 16 bit integer (char).
  • byte: 8 bit integer (byte).
  • floating point: 64 bit float number (double, 1 cell). Ex.: 123.0 123D
  • string: A double quote terminated strings, stored on the objects stack. Ex.: S" Hello!"
  • object: Any user data reference (object).

Stacks

  • Data stack: Main stack for user data. Holds all 32bit and 64bit integers.
  • Floating point stack: Stack for 64 bit floats.
  • Return stack: Stack for interpreter internal use. Holds 32 bit signed integers.
  • Object stack: Can hold any object and strings.
  • Exception stack: Not accessible for users. Its used internally by THROW and CATCH words.
  • Input source stack: Stack for keeping the inputs. Used by the EVALUATE word.

Heaps

  • Data heap: Main heap for user data. It is an array of bytes. Words working with it are ensuring the cell/char etc. alignment, when needed.
  • Object heap: Holds all objects and strings.

Libraries

Numbers

Unknown words are parsed as numbers, following C/C# conventions. Double cell integer must have the L or the l suffix to be recognized as a double cell integer. Any problem with parsing a number makes the parser to return a word, which is unknown, and it leads to an error.

unsigned-single-cell-integer :: digit-sequence .
unsigned-double-cell-integer :: digit-sequence ( 'L' | 'l' ) .
unsigned-floating-point-number :: digit-sequence ( 'D' | 'd' ) .
unsigned-number :: unsigned-single-cell-integer | unsigned-double-cell-integer | unsigned-floating-point-number .
unsigned-floating-point-number :: ( digit-sequence '.' fractional-part [ 'e' scale-factor ] ) | ( digit-sequence 'e' scale-factor ) .
scale-factor :: [ sign ] digit-sequence .
fractional-part :: digit-sequence .
sign :: '+' | '-' .

Note: Forth style numbers parsing using the BASE variable will be implemented later.

Strings

Words like S" or ABORT" expect a series of characters following in the input stream terminated by the " (double quote) character.

Special characters escaping

Special chars escaping is supported by the S\" word. It uses the Forth 2012 standard and is extended by the C# conventions.

\a BEL (alert, ASCII 7)
\b BS (backspace, ASCII 8)
\e ESC (escape, ASCII 27)
\f FF (form feed, ASCII 12)
\l LF (line feed, ASCII 10)
\m CR/LF pair (ASCII 13, 10)
\n newline (implementation dependent, e.g., CR/LF, CR, LF, LF/CR)
\q double-quote(ASCII 34)
\" double-quote (ASCII 34)
\r CR (carriage return, ASCII 13)
\t HT (horizontal tab, ASCII 9)
\v VT (vertical tab, ASCII 11)
\z NUL (no character, ASCII 0)
\0 NUL (no character, ASCII 0)
\\ backslash itself (ASCII 92)
\uxxxx UTF16 (A sequence of 4 hex characters.)
\x or \X UTF16 (A sequence of 1 to 4 hex characters.)

NOTE: The \U escape sequence will be added later.

Debugging

Simple debugging is possible - word TRACE can turn listing of executed words on/off.


1 TRACE  \ Turns executed words listing on.
0 TRACE  \ Turns executed words listing on.

: a 123 . CR ;
1 TRACE
a

Trace: A
Trace: LITERAL
Trace: .
123 Trace: CR

Trace: ExitControlWord

Interpreter has two public events - ExecutingWord and WordExecuted - to notify a caller that a word is going to be executed and that a word was executed.

Building (Ubuntu 20.04)

.NET 6.0

https://dotnet.microsoft.com/download/dotnet/6.0

Download the latest .NET 6 version and install it to the /home/user/Devel/bin/dotnet directory.

mkdir -p $HOME/Devel/bin/dotnet && tar zxf dotnet-sdk-6.0.101-linux-x64.tar.gz -C $HOME/Devel/bin/dotnet
export DOTNET_ROOT=$HOME/Devel/bin/dotnet
export PATH=$PATH:$HOME/Devel/bin/dotnet

The PATH and the DOTNET_ROOT variables can be set permanently in the .bashrc file.

Run:

nano .bashrc

in your home directory and add to the end of the file:

export DOTNET_ROOT=$HOME/Devel/bin/dotnet
export PATH=$PATH:$HOME/Devel/bin/dotnet

PowerShell

Install the latest powershell snap.

sudo snap install powershell --classic

Mono

https://www.mono-project.com/download/stable/

Get the latest stable version.

nuget.exe

https://www.nuget.org/downloads

Download the latest stable version and save it to the /home/user/Devel/bin/nuget directory.

build.ps configuration

Set the $toolsPath and the $useMonoToRunNuget variables to match your build environment (Linux or Windows).

Build

powershell -ExecutionPolicy Unrestricted ./build.ps1 -Configuration Release

Nugets

Upload new ./nuget/*.nupkg and ./nuget/*.snupkg packages to nuget.org.

Examples

( Hello world! )
: hello S" Hello, world!" S. CR ;

( A simplified hello-world! )
: greet ." Hello, I speak Forth!" CR ;

( Large letter F )
: STAR 42 EMIT ;
: STARS 0 DO  STAR  LOOP ;
: MARGIN CR 30 SPACES ;
: BLIP MARGIN STAR ;
: BAR MARGIN 5 STARS ;
: F BAR BLIP BAR BLIP BLIP CR ;

( do-loop, that runs 5 times )
: doit 5 0 DO ." hello" CR LOOP ;

( do-loop, that runs 5 times and shows the current I value ) 
: doit 5 0 DO ." hello" 1 SPACES I . CR LOOP ;

( do-loop, that breaks after I > 4 ) 
: doit 10 0 DO ." hello" 1 SPACES I DUP . CR 4 > IF LEAVE THEN LOOP ;
  
( begin-until loop, that prins even numbers from 10 to 0 )
: doit 10 BEGIN DUP . CR 2 - DUP 0< UNTIL ;

( infinite loop )
: doit BEGIN ." hello" CR AGAIN ;

( loop writes "hello" 10 times )
: doit 10 BEGIN DUP 0> WHILE ." hello " 1 - DUP . CR REPEAT ;

( Messing with programmers... )
: 1 2 ;
1 1 + . CR  \ What result are you expecting here? :-)

( Constants )
123 CONSTANT C1  \ Defines a constant C1.
C1 . CR          \ Prints the value of the constant - 123.

( Variables )
VARIABLE A   \ Defines a single cell variable A.
123 A !      \ Stores 123 into the variable A.
A @ . CR     \ Fetches and prints out the value of the variable A.
A ? CR       \ The same thing - "?" is a shortcut for "@ .". 

2VARIABLE B  \ Defines a double cell variable B.
1.5 B 2!     \ Stores 1.5 float (a double cell value) into the variable B.
B 2@ F.      \ Fetches and prints out the double cell (float) value of the variable B.

( 100 cells long array )
VARIABLE arr            \ Variable for storring the "address" of the first cell of the new array.
HERE arr !              \ Getting the first cell address.
100 ALLOT               \ Allocation of the array.
HERE . CR               \ Will print out the index ("address") of the last cell of the new array.
123 arr !               \ Stores 123 to the first cell of the array arr.
456 arr 1 CELLS + !     \ Stores 456 to the second cell of the array arr.
arr @ . CR              \ Gets and prints out the contents of the first cell of the array arr (123).
arr ? CR                \ Shorter version of the previous example.
arr 1 CELLS + ? CR      \ Gets and prints out the contents of the second cell of the array arr (456).

( Storing a number on the heap and printing it out )
123 , HERE 1 CELLS - @ . CR

( Factorial of N - without RECURSE )
: factorial DUP 0= IF DROP 1 ELSE DUP 1- FACTORIAL * THEN ;

( Factorial of N - with RECURSE )
: factorial DUP 0= IF DROP 1 ELSE DUP 1- RECURSE * THEN ;

5 fatorial . CR  \ 120

( Indexed array )
( Source: http://www.forth.org/svfig/Len/definwds.htm )
: indexed-array (n -- ) (i -- a)
     CREATE CELLS ALLOT
     DOES> SWAP CELLS + ;

20 indexed-array foo  \ Make a 1-dimensional array with 20 cells
 3 foo                \ Put addr of fourth element on the stack

( 80 cells long buffer/array )
CREATE buffer 80 CELLS ALLOT

( ' and EXECUTE )
: goodbye ." Goodbye" CR ;
: hello ." Hello" CR ;
VARIABLE a
: greet a @ execute ;   \ Expects an execution token (xt) of a word.
' hello a !             \ Set the variable a to the xt of the word hello.
greet                   \ greet will say "Hello".
' goodbye a !           \ Set the variable a to the xt of the word goodbye.
greet                   \ greet will say "Goodbye".

( ' ['] and EXECUTE )
( Source: https://www.forth.com/starting-forth/9-forth-execution/ ) 
( 1 ) : hello    ." Hello " ;
( 2 ) : goodbye  ." Goodbye " ;
( 3 ) VARIABLE 'aloha  ' hello 'aloha !
( 4 ) : aloha    'aloha @ EXECUTE ;

aloha                   \ Prints out "Hello".
' goodbye 'aloha !      \ Sets the 'aloha variable to xt of the goodbye word.
aloha                   \ Prints out "Goodbye".

: say  ' 'aloha ! ;

say hello
aloha                   \ Prints out "Hello".
say goodbye
aloha                   \ Prints out "Goodbye".

: coming   ['] hello   'aloha ! ;
: going    ['] goodbye 'aloha ! ;

coming                  \ Sets 'aloha to the xt of the word hello.
aloha                   \ Prints out "Hello".
going                   \ Sets 'aloha to the xt of the word goodbye.
aloha                   \ Prints out "Goodbye".

( Forvard declaration )
( Source: And so Forth..., J.L. Bezemer )
-1 VALUE (step2)            \ A place to store a reference (execution token) to the word step2.
: step2 (step2) EXECUTE ;   \ The step2 word. Without a body for now.
: step1 1+ DUP . CR step2 ; \ The step1 word, calling the step2 word.
:noname 1+ DUP . CR step1 ; \ The body of the step2 word. :NONAME leaves an execution token of the created word on the stack.
TO (step2)                  \ Sets the body of the word step2 (using the value of the VALUE (step2)).
1 step1                     \ Executes the step1 word, that in turn calls the step2 word, which calls the step1 word...

( Unloop )
: unloop-test 10 1 DO I DUP . CR 5 > IF ." Exiting..." CR UNLOOP EXIT THEN LOOP ." Never printer out..." ;

( Abort with a message )
: abort-with-message 10 1 DO I DUP . CR 5 > ABORT" Too big!" LOOP ." Never printer out..." ;

( Exceptions )
: th THROW ." Thrown" CR ;
: ca ." pre-t" CR ['] th CATCH ." post-t" CR ;
0 ca   \ No exception thrown.
-1 ca  \ Like ABORT.
-2 ca  \ Like ABORT" mesg".
1 ca   \ User exception.

( Is a word defined? )
: is-word-defined? ['] ' CATCH 0= IF ." Defined" ELSE ." Undefined" THEN  DROP ( Drop the product of the ' word.) ;
: w? is-word-defined? CR ;  \ Just a shortcut for the is-word-defined? word.
w? bla  \ Undefined
w? IF   \ Defined

( How the word . "dot" can be implemented... )
( n -- )                  \ Display n.
: . DUP ABS 0             \ Prepare.
   <# #S  ROT SIGN #>     \ Convert to string.
   TYPE SPACE ;           \ Output the created string.
   
( Prints a counted string )
( c-addr -- )
(c-addr --)
: printc    ( c-addr )
  DUP       ( c-addr c-addr )
  C@        ( c-addr count )
  1+        ( c-addr count )
  1         ( c-addr count start )
  DO        ( c-addr )
  DUP       ( c-addr c-addr )
  I         ( c-addr c-addr I )
  CHARS     ( c-addr c-addr bytes )
  +         ( c-addr c-addr+index )
  C@        ( c-addr char )
  EMIT      ( c-addr )
  LOOP ;
  

Roman numerals for two bytes chars

Source: Thinking Forth, Leo Brodie

Create romans      ( ones) char I c, char V c,
                   ( tens) char X c, char L c,
               ( hundreds) char C c, char D c,
              ( thousands) char M c,

Variable column# ( current_offset)

: ones 0 column# ! ;
: tens 4 column# ! ;
: hundreds 8 column# ! ;
: thousands 12 column# ! ;

: column ( -- address-of-column ) romans column# @ + ;

: .symbol ( offset -- ) column + c@ emit ;

: oner 0 .symbol ;
: fiver 2 .symbol ;
: tener 4 .symbol ;

: oners ( #-of-oners -- )
    ?dup IF 0 DO oner LOOP THEN ;

: almost ( quotient-of-5/ -- )
    oner IF tener ELSE fiver THEN ;

: digit ( digit -- )
    5 /mod over 4 = IF almost drop ELSE IF fiver THEN
    oners THEN ;

: roman ( number -- ) 
    1000 /mod thousands digit
     100 /mod  hundreds digit
      10 /mod      tens digit
                   ones digit
  SPACE ;

Acknowledgements

JetBrains kindly provides EFrt with a free open-source licence for their Resharper and Rider.

  • Resharper makes Visual Studio a much better IDE
  • Rider is fast & powerful cross platform .NET IDE

image

About

Embeddable Forth language implementation.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages