Skip to content

phreda4/r3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

r3 programming language

r3 is a concatenative language of the forth family, more precisely it takes elements of ColorForth. Word colors are encoded by a prefix: in r3 this prefix is explicit.

This is the second experiment, now all the Cells are 64 bits long, have more option for diferenct bits size in memory and only have access to Operating System with Dinamic Library, this make more easy to compile to other OS.

Now the graphic library is not mandatory, then boot in console mode.

This make posible utilice any Library, test with SDL2, SDLmixer, SDLTTF and FFMPEG. I'm interest in connect with OPENGL, VULKAN, FCGI, MariaDB or whatever need.

This version start in WIN enviroment, I try to run in LINUX and Raspberry PI 4 commming next, I not have mac but the R3d4 is working on this system, I hope this working too. See 'mainl.r3' for try the linux version.

The vm used here is https://github.com/phreda4/r3evm

Youtube Videos

https://www.youtube.com/playlist?list=PLTXylaG4SqxP86WQXKJBbPiNWYk5AF6Jn

More code to play

https://github.com/phreda4/r3-games download and decompress in the r3/r3 folder

Version for others OS

It is possible to run r3 on Linux/Mac/Rpi, the virtual machine code is commented about this, the version for Linux is in the distribution. If anyone is interested in running R3 on these or other platforms, write to me to clarify doubts and collaborate with the port.

How the language works

A WORD is defined as a sequence of letters separated by spaces, there are three exceptions to improve the expressiveness of the language that are seen later.

Each word can be a number or is searched in the DICTIONARY.

If it is a valid number, in decimal 33, binary %101, hexadecimal $7f or fixed point 0.1 its value is pushed to DATA STACK.

Like all FORTH, the DATA STACK is the memory structure used to perform intermediate calculations and pass parameters between words.

If the word is NOT a valid number, then it is searched in the DICTIONARY, if it is not found, it is an ERROR, the rule is "every word used must be defined before".

The language has a BASIC DICTIONARY that is already defined, from which new WORDS are defined that will be used to build the program.

The start of execution is marked with the word colon : alone, not as a prefix.

The code is in a text file, you can include code files (word definitions) with the prefix caret ^ indicating the file name below.

The included code will only add to the caller's dictionary words that are marked as exported ( prefixes :: or ## ).

If the included code contains the start definition "colon" :, it will be executed before starting the program, to initialize structures or states.

We use | to indicate comment until the end of the line (the first exception to word separation).

We use ^ to indicate include a library, the name end in the end of the line too.

Each word can take and/or leave values of the DATA STACK, this is expressed with a state diagram of the stack before -- and after the word.

for example

+ | a b -- c

the word + takes the two elements from the top of the DATA STACK, consumes them and leaves the result. In addition to a modification in the stack, there may also be a lateral action, for example:

; | --

It does not consume or produce values in the stack but end a word

Prefixes in words

  • | ignored until the end of the line, this is a comment
  • ^ the name of the file to be included is taken until the end of the line, this allows filenames with spaces.
  • " the end of quotation marks is searched to delimit the content, if there is a double quotation mark "" it is taken as a quotation mark included in the string.
  • : define action
  • :: define action and this definition prevails when a file is included (* exported)
  • # define data
  • ## define exported data
  • $ define hexadecimal number
  • % defines binary number, allows the . like 0
  • ' means the direction of a word, this address is pushed to DATA STACK, it should be clarified that the words of the BASIC DICTIONARY have NO address, but those defined by the programmer, yes.

Programming occurs when we define our own words. We can define words as actions with the prefix:

:addmul + * ;

or data with the prefix #

#lives 3

: only is the beginning of the program, a complete program in r3 can be the following

:sum3 dup dup + + ;

: 2 sum3 ;

Conditional and Repeat

The way to build conditionals and repetitions is through the words ( and )

for example:

5 >? ( drop 5 )

The meaning of these 6 words is: check the top of the stack with 5, if it is greater, remove this value and stack a 5.

The condition produces a jump at the end of the code block if it is not met. It becomes an IF block.

r3 identifies this construction when there is a conditional word before the word (. If this does not happen the block represents a repetition and, a conditional in that this repetition that is not an IF is used with the WHILE condition.

for example:

1 ( 10 <?
	1 + ) drop

account from 1 to 9, while the Top of stack is less 10.

You have to notice some details:

There is no IF-ELSE construction, this is one of the differences with: r4, on the other hand, ColorForth also does not allow this construction, although it seems limiting, this forces to factor the part of the code that needs this construction, or reformulate the code.

In: r4 could be constructed as follows

...
1? ( notzero )( zero )
follow

It must become:

:choice 1? ( nocero ; ) zero ;

...
choice
follow

Sometimes it happens that rethinking code logic avoids ELSE without the need to do this factoring. There are also tricks with bit operations that allow you to avoid conditionals completely but this no longer depends on the language.

Another feature to note that it is possible to perform a WHILE with multiple output conditions at different points, I do not know that this construction exists in another language, in fact it emerged when the way to detect the IF and WHILE was defined

'list ( c@+
	1?
	13 <>?
	emit ) 2drop

Does this repetition meet that the byte obtained is not 0 1? and that is not 13 13 <>?, in any of the two conditions the WHILE ends

Another possible construction, that if it is in other FORTH, is the definition that continues in the following. For example, define1 adds 3 to the top of the stack while define2 adds 2.

:define1 | n -- n+3
	1 +
:define2 | n -- n+2
	2 + ;

Recursion

Recursion occurs naturally, when the word is defined with : it appears in the dictionary and it is possible to call it even when its definition is not closed.

:fibonacci | n -- f
	2 <? ( 1 nip ; )
	1 - dup 1 - fibonacci swap fibonacci + ;

Call Optimization

When the last word before a ; is a word defined by the programmer, both in the interpreter and in the compiler, the call is translated into JMP or jump and not with a CALL or call with return, this is commonly called TAIL CALL and saves a return in the chain of words called.

This feature can convert a recursion into a loop with no callback cost, the following definition has no impact on the return stack.

:loopback | n -- 0
	0? ( ; )
	1 -
	loopback ;

BASE word list:

;	| End of Word
(  )	| Word block to build IF and WHILE
[  ]	| Word block to build nameless definitions
EX	| Run a word through your address

Conditional, together with blocks make the control structures

There are three types of comparers: The simple ones, compare but do not affect stack. a -- a

0? 1?	| Zero and non-zero conditionals
+? -?	| Conditional positive and negative

The doubles, compare two numbers in stack and consume one, a b -- a

<? >?	| Comparison conditions
=? >=? 	| Comparison conditions
<=? <>?	| Comparison conditions
AND? 	| Logical conditioners AND 
NAND?	| Logical conditioners NOT AND

And the interval comparator that consumes two numbers, a b c -- a

BT?	| Conditional between

Words to modify the DATA STACK

This words modify the data stack, copy or eliminate values in the stack.

DUP	| a -- a a
DROP	| a --
OVER	| a b -- a b a
PICK2	| a b c -- a b c a
PICK3	| a b c d -- a b c d a
PICK4	| a b c d e -- a b c d e a
SWAP	| a b -- b a
NIP	| a b -- b
ROT	| a b c -- b c a
2DUP	| a b -- a b a b
2DROP	| a b --
3DROP	| a b c --
4DROP	| a b c d --
2OVER	| a b c d -- a b c d a b
2SWAP	| a b c d -- c d a b

Words to modify the RETURN STACK

>R	| a --		; r: -- a
R>	| -- a 		; r: a --
R@	| -- a 		; r: a -- a

Logical operators

AND	| a b -- c
OR	| a b -- c
XOR	| a b -- c
NOT	| a -- b

Arithmetic Operators

+	| a b -- c
-	| a b -- c
*	| a b -- c
/	| a b -- c
<<	| a b -- c
>>	| a b -- c
>>>	| a b -- c
MOD	| a b -- c
/MOD	| a b -- c d
*/	| a b c -- d
*>>	| a b c -- d
<</	| a b c -- d
NEG	| a -- b
ABS	| a -- b
SQRT	| a -- b
CLZ	| a -- b

Access to Memory

@ fetch a value from memory ! store a value in memory

@	| a -- [a] ; 64 bits
C@	| a -- byte[a] ; 8 bits
W@	| a -- word[a] ; 16 bits
D@	| a -- dword[a] ; 32 bits
@+	| a -- b [a] ; 64 bits
C@+	| a -- b byte[a] ; 8 bits
W@+	| a -- b word[a] ; 16 bits
D@+	| a -- b dword[a] ; 32 bits
!	| a b -- ; 64 bits
C!	| a b -- ; 8 bits
W!	| a b -- ; 16 bits
D!	| a b -- ; 32 bits
!+	| a b -- c ; 64 bits
C!+	| a b -- c ; 8 bits
W!+	| a b -- c ; 16 bits
D!+	| a b -- c ; 32 bits
+!	| a b -- ; 64 bits
C+!	| a b -- ; 8 bits
W+!	| a b -- ; 16 bits
D+!	| a b -- ; 32 bits

Help registers facility

Registers to traverse memory and read, copy or fill values

>A	| a --	; pop DATA STACK to A
A>	| -- a	; push A in DATA STACK
A+	| a --	; add to A

A@	| -- a	; 64 bits
A!	| a --
A@+	| -- a
A!+	| a --

CA@		| -- a	; 8 bits
CA!		| a --
CA@+	| -- a
CA!+	| a --

DA@		| -- a	; 32 bits
DA!		| a --
DA@+	| -- a
DA!+	| a --

>B	| a --	; pop DATA STACK to B
B>	| -- a	; push B in DATA STACK
B+	| a --	; add to B

B@	| -- a	; 64 bits
B!	| a --
B@+ | -- a
B!+ | a --

CB@	| -- a	; 8 bits
CB!	| a --
CB@+ | -- a
CB!+ | a --

DB@	| -- a	; 32 bits
DB!	| a --
DB@+ | -- a
DB!+ | a --

Copy and Memory Filling

Block memory operation, only for data memory

MOVE	| dst src cnt --	; 64 bits
MOVE>	| dst src cnt --
FILL	| dst fill cnt --
CMOVE	| dst src cnt --	; 8 bits
CMOVE>	| dst src cnt --
CFILL	| dst fill cnt --
DMOVE	| dst src cnt --	; 32 bits
DMOVE>	| dst src cnt --
DFILL	| dst fill cnt --

OS comunication

MEM		| -- a	; start free memory
LOADLIB	| "" -- a 
GETPROC | "" a -- a
SYS0	| a -- a
SYS1	| ab -- a
SYS2	| abc -- a
SYS3	| abcd -- a
SYS4	| abcde -- a
SYS5 	| abcdef -- a
SYS6	| abcdefg -- a
SYS7	| abcdefgh -- a
SYS8	| abcdefghi -- a
SYS9	| abcdefghij -- a
SYS10 	| abcdefghijk -- a

History

I start with r4 in 2005, in 2020 I develop r3 with 64 bits and some internal diferences, more clear vm and some ideas, breaking r4 code, in 2021 I experiment with DLL linking more concise with 64 bits and remove all SO conecction (now are DLL calls), I think it is a real usefull version, but R3d4 (the prev version) has implementationfor WIN,LIN, RPI and MAX, this version can have this SO but need make all the connetion, not for now.

2005 :r4 first lang,

lang: https://github.com/phreda4/r4   
vm: https://github.com/phreda4/r4MV

2020 r3 version r3d4 (this name is thinking for make complete enviroment),

lang: https://github.com/phreda4/r3d4 
vm: https://github.com/phreda4/r3vm

2021 r3 this version

lang: this repository
vm: https://github.com/phreda4/r3evm