A general-purpose programming language coming to life through a custom one-pass compiler and a stack-based virtual machine.
👉️ The language is currently being developed, but the implemented features can be seen in Milestones. This README is also in development.
Try out Thusly in the WASM-based interactive playground directly in the browser.
- General-purpose
- Interpreted
- Dynamically typed
- Imperative
- Garbage collected
The development of both the language and the surrounding documentation is currently on-going, but the following is a subset of the language:
Whitespace:
Whitespace is semantically insignificant except for newline characters on non-blank lines.
To read more about the architectural design, implementation details, and follow along walk-throughs of examples of tokenization, parsing, compilation, evaluation, and execution, see design/implementation.
- The terminals in the initial grammar can be identified from user input via a multi-line file or single-line REPL input and then tokenized.
- Arithmetic expressions using
number(double) can be evaluated.- Addition (
+) - Subtraction (
-) - Multiplication (
*) - Division (
/) - Modulo (
mod) - Unary negation (
-) - Precedence altering (
())
- Addition (
- Comparison expressions using
numbercan be evaluated.- Greater than (
>) - Greater than or equal to (
>=) - Less than (
<) - Less than or equal to (
<=)
- Greater than (
- Equality and logical negation expressions using
number,boolean,text(string), andnonecan be evaluated.- Equal to (
=) - Not equal to (
!=) - Logical not (
not)
- Equal to (
- Concatenation of
textliterals using+can be evaluated.
- Temporary
@outstatement can be executed. - Global and local variables can be defined and used.
- Declaration and initialization (
var <name> : <expression>) - Assignment expression (
<name> : <expression>)
- Declaration and initialization (
- Variables adhere to lexical scope.
- Standalone block
block <statements> end
- Standalone block
- Logical operators can be evaluated.
- Conjunction (
and) - Disjunction (
or)
- Conjunction (
- Control flow statements can be executed.
-
Selection
-
if -
elseif -
else
if <condition> <statements> elseif <condition> <statements> else <statements> end -
-
Loops
- Bounded (
foreach)
foreach <name> in <start>..<end> step <change> <statements> endExample 1
// `<change>` expression is implicitly 1 foreach value in 0..10 @out value endExample 2
var a: 0 var b: 10 var c: 2 foreach value in a..b step c @out value end- Unbounded (
while)
while <condition> { <change> } <statements> endExample
// `{ <change> }` expression is optional var i: 0 while i < 10 {i +: 1} @out i end - Bounded (
-
- Augmented assignment expressions can be evaluated.
- Addition and assignment (
+:) - Subtraction and assignment (
-:) - Multiplication and assignment (
*:) - Division and assignment (
/:) - Modulo and assignment (
mod:)
- Addition and assignment (
- Range comparison expression can be evaluated (
<value> in <min>..<max>) - TODO (more milestones will be added here)
- A Thusly VM can run and execute code in the browser via an interactive playground.
- Functions can be defined and invoked.
- Closures are supported.
- Functions are first-class citizens.
- TODO (more milestones will be added here)
This section is for briefly demonstrating implemented functionality thus far and expected behavior when running your code.
By inputting code from either a file or via the REPL, the VM will interpret it and output the result if an @out statement is used.
Table 1: Valid user input (expressions)
| Example input | Expected output | Expected precedence parsing |
|---|---|---|
1 + 2 * 3 / 4 |
2.5 | 1 + ((2 * 3) / 4) |
(1 + 2) * 3 / 4 |
2.25 | ((1 + 2) * 3) / 4 |
1 + -2 - -3 |
2 | (1 + (-2)) - (-3) |
1 > 2 = 3 > 4 |
true | (1 > 2) = (3 > 4) |
false != not(1 + 2 >= 3) |
false | false != (not((1 + 2) >= 3)) |
"he" + "llo" = "hello" |
true | ("he" + "llo") = "hello" |
"keep " + "on " + "coding" |
keep on coding | ("keep " + "on ") + "coding" |
false and false or true |
true | (false and false) or true |
true or true and false |
true | true or (true and false) |
Table 2: Valid user input (statements)
| Example input | Expected output |
|---|---|
var first: "Jane"var last: "Doe"var full: first + " " + last@out full |
Jane Doe |
var x: 1var y: 2var z: x: y@out x@out z |
2 2 |
var x: "global"@out xblock x: "changed global" var x: "local" @out xend@out x |
global local changed global |
var x: 0if x < 5 @out "in if"else @out "in else"end |
in if |
foreach value in 0..2 @out valueend |
0 1 2 |
var a: 0var b: 2var c: 0.5foreach value in a..b step c @out valueend |
0 0.5 1 1.5 2 |
var x: 0while x < 4 {x +: 1} @out xend |
0 1 2 3 |
Table 3: Invalid user input
| Example input | Error type | Expected error reason |
|---|---|---|
"one" + 2 |
Runtime | + operates on number only or text only |
"one" < 2 |
Runtime | < operates on number only |
!true |
Comptime | ! is only allowed in != (use not) |
x: 1 |
Comptime | x has not been declared |
var x: 11 + x: 2 |
Comptime | 1 + x is an invalid assignment target( + has higher precedence than :) |
- A C compiler (e.g. Clang or GCC)
- CMake version 3.20 or later
Run the below command to make Thusly come to life. It will create a top-level bin directory where the configured and compiled project will be located along with the executable binary cthusly.
./build.shIf permission is denied, first add executable permission to the build script by running:
chmod +x build.sh.
Once you have built the project you can go ahead and feed it some code to interpret thusly (..get it?):
Usage example:
$ ./bin/cthusly --help
Usage: ./bin/cthusly [options] [path]
The REPL (interactive prompt) starts if no [path] is provided
-h, --help Show usage
-d, --debug Enable all debug flags below
-dcomp, --debug-comp Show compiler output (bytecode)
-dexec, --debug-exec Show VM execution trace
Flags:
Currently, only 1 flag may be provided.
Interpret code from a file:
./bin/cthusly path/to/your/fileStart the REPL (interactive prompt):
./bin/cthuslyExit REPL:
Press
Cmd + D(Mac) orCtrl + D(Windows).
Example:
$ ./bin/cthusly
> @out "he" + "llo" = "hello"
true
> @out (1 + 2) * 3 / 4
2.25
>
Enable/disable debug:
For the debug flags to have an effect, the following macro in src/common.h need to be defined.
- DEBUG_MODE
You may comment or uncomment it to disable or enable support for the flags (then rebuild the project).
This software is licensed under the terms of the MIT license.
