Skip to content

it is remake of ./fplusold repo, but I rewrited all the code in Rust programming language.

License

Notifications You must be signed in to change notification settings

TwoSpikes/fplus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

F+ (Forth+)

Stack-based programming language made by me (TwoSpikes, 2022-2023) for studing purposes.

Done:

  • Turing-completeness (: and if operations)
  • Multi-line comment support (/* comment */)
  • Chars (''), strings ("") and string postfixes (""b and ""c)
  • repr(&str) and urepr(&str) functions (proper escaping like in C)
  • Function predeclaration (linking)
  • Negative numbers (-1, ---50)
  • Drop-in documentation
  • dump subcommand (dump tokens)
  • Output debug information to stderr instead of stdout
  • Command line arguments (see argc and argv)
  • Global variables that switch debug information on/off
  • include keyword
  • Nested label scopes
  • Ability to stderr printing
  • File reading
  • gettime operation — returns time in nanoseconds as i128
  • Colored debug output (Error (red) and Warning (yellow))
  • Variables that switch debug information on/off through file debug.tsconf or through command line options
  • Unsigned 64-bit numbers (same type with signed numbers) (like 30u)
  • One-line comment support (// comment)

In progress:

To do:

  • Float numbers (same type with "normal" numbers) (F64)
  • Float numbers with double precision (same type with "normal" numbers) (D64)
  • macro keyword
  • undump subcommand
  • com and token-com subcommand (for compilation)
  • addsource keyword
  • Vim and Emacs syntax highlighting (later)
  • Colored output somehow through escaping
  • C-like file reading
  • Raw file reading
  • File writing
  • Self-hosted compiler (this thing was abandoned, maybe forever)

Standard library:

  • max_2_I64 function ((b: I64, a: I64) -> I64)
  • min_2_I64 function ((b: I64, a: I64) -> I64)
  • I64ToStr function ((val: I64) -> (len: I64, I64[len]))
  • U64ToStr function ((val: U64) -> (len: I64, I64[len]))
  • F64ToStr function ((val: F64) -> (len: I64, I64[len]))
  • D64ToStr function ((val: D64) -> (len: I64, I64[len]))
  • StrToI64 function ((len: I64, arr: I64[len]) -> (I64))
  • StrToU64 function ((len: I64, arr: I64[len]) -> (U64))
  • StrToF64 function ((len: I64, arr: I64[len]) -> (F64))
  • StrToD64 function ((len: I64, arr: I64[len]) -> (D64))
  • I64ToU64 function ((val: I64) -> (U64))
  • U64ToI64 function ((val: I64) -> (I64))
  • I64ToF64 function ((val: I64) -> (F64))
  • F64ToI64 function ((val: F64) -> (I64))
  • U64ToF64 function ((val: U64) -> (F64))
  • F64ToU64 function ((val: F64) -> (U64))
  • I64ToD64 function ((val: I64) -> (D64))
  • D64ToI64 function ((val: D64) -> (I64))
  • U64ToD64 function ((val: U64) -> (D64))
  • D64ToU64 function ((val: D64) -> (U64))
  • F64ToD64 function ((val: F64) -> (D64))
  • D64ToF64 function ((val: D64) -> (F64))
  • veccpy function

Supported compilation modes:

  • Simulation (default)
  • C (not implemented yet)

Compiler

Installing (Building):

$ make

If without Make:

$ cargo build --release
$ install ./target/release/fplus $PREFIX/bin

(I use $PREFIX because I am using Termux to create this project and Make does not work without $PREFIX)

Dependences:

  • Cargo
  • make (unneccesary)

Usage:

Usage:
$ fplus SUBCOMMAND [OPTION]... [SOURCE]... -- [ARG]...

SUBCOMMAND (insensitive to register):                                       {sim s}                 Simulate program                                    {version ver v}         Print version information and exit
{usage use u help h ?} info information                                                                     Print help information and exit
{dump d}                Dump the tokens of the program.                     {error e}               Print error code and information about them

OPTION (insensitive to register):                                           {-o --output} FILE              dump output to FILE                         --lex-debug -lex-debug}         show debug information during lexing        --only-lex -only-lex}           stop on lexing (for debugging purposes)     --link-debug -link-debug}       show debug information during linking       --only-link -only-link}         stop on linking (for debugging purposes)    --link-debug-succed             show [linking succed]                       --parse-debug -parse-debug}     show debug information during parsing       --parse-debug-state             show State information during parsing       --parse-debug-id                show scope-id information during parsing    --parse-debug-call              show function calling information during parsing                                                                        --parse-debug-string            show string information during parsing      --parse-debug-include           show including information                  --parse-debug-include-adding    TODO                                        --parse-debug-include-succed    TODO                                        --sim-debug                     show debug information during simulation
--sim-debug-puts                show debug information debore printing      --max-include-level NUMBER
                                set max include level (now is 500)          --disable-colors                disable terminal colors

While building, you must be in fplus directory (it is important)

VERSION:

F+, a stack-based interpreting programming language
written on Rust v.1.68.2
version: 0.1.0-5
download: https://github.com/TwoSpikes/fplus
2022-2023 @ TwoSpikes

Error codes:

This message will be shown with error (or e) subcommand:

errorcodes:
E0                    Cannot open file

EXAMPLES:

sim subcommand examples:

$ fplus sim main.tspol

ALSO KNOWN AS

$ fplus sim main.tspol --

F+ will simulate ./main.tspol file

$ fplus sim main.tspol -- a b c

F+ will simulate ./main.tspol file with a b c command line arguments

dump subcommand examples:

$ fplus dump subc-dump.tspl -o subc-dump-file.txt

It will translate F+ code to tokens and write this to subc-dump-file.txt:

2:1:PUSHNTH
2:11:DROPNTH
2:19:NBROT
2:25:Push(2)
2:27:MUL
2:29:PLUS
3:1:DUMP
3:8:PUTS
3:13:Push(6969)
3:18:PRINT
-2:-2:Push(0)

If you will not provide -o option, tokens will dump on the stdout instead (with debug information).

-o option examples:

$ fplus sim hello-world.tspl -o hello-world-output.txt

It will write

Hello, World!

into the hello-world-output.txt file.

If you will not provide -o option, Hello, World! will be printed to the stdout instead.

Programming language

Stack

Stack is a dynamic array of 64-bit integer numbers (from -9223372036854775808 (-2^63) to 9223372036854775807 (2^63-1)).

Pushing numbers

34 36

This program will push 34 and 36 on the stack.
Stack: [34, 36]

Copying elements (numbers) — pushnth

Pushnth takes one argument: (I64) a
First, provide this argument, and second, write pushnth

If stack is [10, 11, 12, 13, 14],
After providing argument (3), it will be [10, 11, 12, 13, 14, 3]
It will consume argument and take third element from right counting from 0 (it is 11).

Argument 0 1 2 3 4 5
Index (from right) 4 3 2 1 0 NA
Element (from right) 14 13 12 11 10 NA
Element (from left) 10 11 12 13 14 NA
34 36
0 pushnth

Stack: [34, 36, 36]

Arithmetic

Plus +

Consumes 2 arguments: (I64) a, (I64) b
Returns: (I64)(a + b)

34 35 +

Stack: [69]

To sum up more than 2 numbers:

1 1 + 1 +

Stack: [3]

To substract, use Negative numbers:

69 -21 +

Stack: [48]

Multiply *

Consumes 2 arguments: (I64) a, (I64) b
Returns: (I64)(a * b)

2 3 *

Stack: [6]

-49 2 *

Stack: [-98]

Division /

Consumes 2 arguments: (I64) a, (I64) b
Returns: (I64)(a / b)

2 / 3

Stack: [0]

48 -2 /

Stack: [-24]

Less than <

Consumes 2 arguments: (I64) a, (I64) b
Returns: (boolean)(a < b)

34 35 <

Stack: [1] because (34 < 35 <=> true)

35 34 <

Stack: [0] because (35 < 34 <=> false)

34 34 <

Stack: [0] because (34 < 34 <=> false)

Equals =

Consumes 2 arguments: (I64) a, (I64) b
Returns: (boolean)(a = b)

34 34 =

Stack: [1] because (34 = 34 <=> true)

35 34 =

Stack: [0] because (35 = 34 <=> false)

Boolean

Not !

Consumes one argument: (boolean) a
Returns: (boolean) !a

0 !

Stack: [1] because (!0 <=> 1)

35 34 <
!

Stack: [1] because

  1. 35 < 34 <=> false
  2. !Ans <=> true
1 ! !

Stack: [1] because

  1. !1 <=> 0
  2. !Ans <=> 1
69 ! !

Stack: [1] because

  1. !69 <=> 0
  2. !Ans <=> 1

Or |

Consumes 2 arguments: (boolean) a, (boolean) b
Returns: (boolean)(a | b)

0 1 |

Stack: [1] because (0 | 1 <=> true)

0 0 |

Stack: [0] because (0 | 0 <=> false)

1 1 |

Stack: [1] because (1 | 1 <=> true)

Same will happen if all 1's will change to bigger numbers because Or anyways will cast arguments to boolean.

Command line arguments

Providing

$ ./target/release/fplus sim main.tspl -- a b c

This option will provide main.tspl, a, b and c command line arguments. This is how to get access to him:

Argc argc

Consumes 0 arguments.
Returns number of provided command line arguments.

argc

Stack: [4]

Argv argv

Consumes 1 argument: (I64) a
Returns String with a-th command line argument.

2 argv

Stack: [98, 1] or "b"

All this programs will work like that only if you provide a, b and c command line arguments.

Files

Reading read

Consumes 1 String.
Return 1 String: file's content with given name.

"main.tspl" read

Stack: [108, 112, 115, 116, 46, 110, 105, 97, 109, 9, 16] or '"main.tspl" read'

Special

Empty op empty_op

Does nothing. Just need for debugging purposes.

Dump dump

Consumes 1 argument: (I64) a
Print a without converting it from ASCII to string.
Need for debugging purposes.

69 dump

Stdout is empty.
Stderr: "69"

Exit exit

Exit the program

Consumes 1 argument: (I64) exitcode
Exits the program. It does not need to return anything.

0 exit

Stderr: '[Simulation of "main.tspl" succed]'

1 exit

Stderr: '[Simulation of "main.tspl" finished with exit code 1]'

Input, Output (I/O)

Print char putc

Consumes 1 argument: (I64) chr
Returns nothing.
Prints chr as ASCII to stdout.

69 putc

Stdout: "E"

Print string puts

I will write how to make String later.

65 66 67 3 putsln

Stdout: "abc"

"Hello, World!" putsln

Stdout: "Hello, World!"

Literals

String

String is a pointer to a data variable that contains the bytes (i64) and the string length at the end.

"Hello, World!"

Stack: [13]

You can dereference it like this:

include std.tsplh

pub fn main
  "Hello, world!" ->vec ???

Stdout: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 13]

You can use simple escaping like in C like this:

Code Stdout
'\r' carriage return
'\n' new string
'\t' tab
'\\' backslash
'\'' quote
'\"' double quote
'\x{val}' (val is 00-FF) byte (for ANSI commands)

Strings are inverted at the stack and has its length at the end.

Empty string:

""

Stack: [0]

Functions and calls

Function

First, you need to create a function

We will call it foo:

Syntax: fn {function name}

fn foo

Main function

main function is the start of the program

If you don't have main function then program will start from first line of file you given.

You cannot have the main function (maybe later I will implement it) in files that you included.

You will define it like this:

fn main

With the previous function we created: foo:

fn foo

fn main

Function calling

You can call your function by just writing its name:

fn foo

fn main
  foo

But this program will go into the infinite loop (because it is returing to main function) so you have to exit it somewhere.

fn foo
  1 exit

fn main
  foo

Let's print string as indicator that we called it:

fn foo
  "foo called" putsln
  1 exit

fn main
  foo

Okay, now let's move on!

Actually you called it and save your next operation address to jump to it at the end of function.

Let's check:

fn foo
  "foo called" putsln
  dump
  1 exit

fn main
  foo

Stdout: "foo called\n16\n"

This address is too large because of our string.
Every char is the 1 operation (and the length of string too).

We do not need it now so I will show how to not do that:

fn foo
  "foo called" putsln
  1 exit

fn main
  #foo

Yeah, you just need to put # sign before function name

Get function address

You can get function addresses by pushing : character before its name

Let's get address of foo function

fn foo
  "foo called" putsln
  1 exit

fn main
  :foo

Stack: [0] because foo starts at first operation

Scopes

You cannot access fns outside of its scope.

fn a {
  fn a.1
  fn a.2
  fn a.3
}

fn main
  :a   /* 0 */
  :a.1 /* Error: label is private */

They are have some bug so do not use it before this message disappear.

Function arguments

Old way

You can provide arguments to function, placing them before calling:

fn foo
  dump dump dump
  0 exit

fn main
  1 2 3 #foo

Stdout: "3\n2\n1\n"

New way

fn foo
  dump dump dump
  0 exit

fn main
  :#foo(1 2 3)

Stdout: "3\n2\n1\n"

Comments

Multi-line

/* this is a comment */

/* this is a nested comment
  /* yeah, nested comment */
*/

Yes, the space before and after /* is neccesary

One-line

// this is the one-line comment
// // nested
// //nested even w/o space
// /* multi-line in one-line */
// /*w/o space again*/

Yes, the space before and after // is neccesary

Pointers

Dereference (->)

"abc"

Stack: [3]

It is pointing to the last data array element (where the length of the string is located. It is equal to the length of the string now because there is only one string in the program for now)

"abc" ->

Stack: [3]

Bc they are equal

"abc" -1 + ->

Stack: [97] because 'a' is 97

Dereference a string (->vec)

Located in std.tsplh.
Places a whole string to the stack.

include std.tsplh

"abc" ->vec

Stack: [95, 96, 97, 3]

Arrays (not implemented yet)

One-dimensional (1D) (not implemented yet)

['a 'b 'c]

Stack: [3]

You can dereference it by [->vec](#Dereference a string)`

include std.tsplh

['a 'b 'c] ->vec

About

it is remake of ./fplusold repo, but I rewrited all the code in Rust programming language.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published