An elegant and embeddable programming language
Visit the language spec »
Website »
Report Bug
•
Request Feature
Vyse is a dynamically typed, interpreted and fast scriptling language inspired by Lua for rapid prototyping of applications like video games. Programmers familiar with Lua/Javascript can pick up the language within an hour. Vyse also comes with a C++ API for frictionless embedding in projects.
Vyse aims to pick up after the Lua programming language, by adding popularly desired features on top of it, whilst providing similar speed and minimalism as Lua. There exist some quirks in Lua that Vyse aims to change.
-
Variables are global by default
-
Long keywords like
local
and the use ofdo .. end
to designate blocks instead of{}
makes it unlikeable to programmers coming from C. -
The concept of metatables and metamethods is somewhat unintuitive to new programmers.
-
Tables being used as both arrays and maps can have unwanted side effects.
-
Lacks many features that are common in other languages such as :
- Compound assignment operators (
+=
,-=
,*=
) and decrement and increment operators (++
,--
) . continue
statement for skipping over some loop iterations.switch
statements.
- Compound assignment operators (
-
Arrays starting at 1 is a a minor issue to some.
Vyse aims to keep most of what Lua provides, but address the aforementioned issues and offer the following QoL feautures:
- A Familiar syntax for programmers migrating from Javascript and C
- An easy to integrate C++ API for easy embedding in applications
- Syntactic sugar for JS like OOP that boils down to the very same metatable and metamethod heirarchy.
Here is a rough overview of the language's syntax and features:
-- importing modules is done with the builtin 'import' function
const math = import("math")
-- variable declaration and assignment
const PI = math.pi -- variables declared with const are immutable
let radius = 10
-- Function declaration
fn calculate_area(r) {
return PI * r ** 2 -- '**' operator is used for exponentiation.
}
let area = calculate_area(radius) -- function calling
-- constructing objects is very similar to Javascript.
let goblin = {
name: "Bobo",
health: 12,
damage: 4,
attack(target) {
target.health -= this.damage
print("Goblin attacks!")
},
};
-- methods is done with the `:` operator.
let wolf = { health: 20 }
goblin:attack(wolf)
-- Arrays are intuitive too
let numbers = [1, 3, 5, 7, 9]
-- the '#' operator returns length of lists or strings.
print('I have ' .. #numbers .. 'numbers!') // '..' is the concat operator
-- the `<<<` operator can be used to push to arrays
numbers <<< 10 <<< 11
-- looping is very similar to lua.
for i = 1, #numbers {
print(numbers[i])
}
-- conditionals
math.randseed(time.now());
const number = math.random(0, 100);
let guess = input():to_number(); -- read user input and parse as a number
while true {
if guess < number {
print("Too low! Try higher")
} else if guess > number {
print("Too hight! Guess lower")
} else {
print("You guessed it! The number was " .. number);
break
}
}
For a more complete spec of the language, and all it's features visit manual.md. Alternatively, read the documentation on this page.
Currently, vyse supports basic control flow, variable declaration, usage and basic collection data structures. To move towards a more complete implementation, the following tasks have to be attended to:
- Implement parent tables (metatables in Lua). (DONE)
- Add a complete list of collection types (Arrays and tables). (DONE)
- Implement proper error reporting in all the passes. (DONE)
- Full support for Lambdas and closures. (DONE)
- Optimze value representation to optionally NaN boxed values.
- Optimize the garbage collector for incremental collection.
- Optimize the VM's loop-dispatch to computed jumps.
- Add more compiler passes for better optimization.
Vyse runs on the Vyse Virtual Machine (svm for short). The VM has a stack based architechture that operates on 1 byte long opcodes. To keep the implementation concise and simple, vyse uses a single pass compiler that directly consumes tokens and emits bytecode.
The stages involved are :
The vyse lexer resides in the include/scanner.hpp
file, A simple hand written lexer that accepts a string returns a
token whenever the method next_token()
is called.
The Lexer is called from within the Compiler, but can also be instantiated and used stand-alone for testing purposes.
The Compiler compiles tokens to Bytecode following the Vyse Bytecode Instruction format (include/x_opcode.hpp
).
Every instruction is 1 byte long. The compiler returns a function containing all the bytecode from the script, which is
then loaded into the VM and called.
The design of the VyVM is very similar to the Lua Virtual Machine despite the fact that it's Stack based as opposed to LuaVM's register based design (after version 5.1). It consists of a value stack and an accumulator register. It has full support for lambdas and closures following Lua's upvalue design.
To build vyse from source, it is recommended that you use CMake (version 3.13 or higher).
The build tool used here is Ninja, but you can use any other build tool of your preference (eg- 'Unix Makefiles'
).
After downloading/cloning vyse into a directory, cd
into it and run the following commands to run the tests:
mkdir build
cd build
cmake .. -G Ninja -DBUILD_TESTS=true -DCMAKE_BUILD_TYPE=Debug -DSTRESS_GC=true -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
ninja
./vy <filename>
The CMake script accepts some options, namely:
Option | Values | Description |
---|---|---|
-DCMAKE_BUILD_TYPE |
Release /Debug |
Compilation mode for the C++ compiler. Also enables runtime assertions. |
-DBUILD_TESTS |
true /false |
Whether to build the test suite. |
-DSTRESS_GC |
true /false |
When true , runs the garbage collector whenever possible. Useful for catching GC bugs. |
-DLOG_GC |
true /false |
When true , logs the GC status on every cycle. |
-DLOG_DISASM |
true /false |
When true , dumps the bytecode disassembly of every program after the compiler pass, before running it on the VM |
Note that -CMAKE_C_COMPILER=clang -CMAKE_CXX_COMPILER=clang++
are optional, and you can use any C++ compiler toolchain of your liking.
The aforementioned snippet will build the project in debug mode, which is preferred for development but is much, much slower.
For an optimized release build, use:
cmake .. -G Ninja -DBUILD_TESTS=true -DCMAKE_BUILD_TYPE=Release -DSTRESS_GC=false -DLOG_GC=false -DLOG_DISASM=false
The benchmarks for vyuse are present in the benchmark
directory.
The benchmark/run.py
script can be used to compare Vyse against other languages.
You can either run all benchmarks, or run specific benchmarks.
First, create and activate a virtual environment:
cd benchmarks
python3 -m venv env
source env/bin/activate
Then, install all the dependencies inside the virtual environment:
python3 -m pip install -r requirements.txt
To run all benchmarks, enter the following into your shell:
cd benchmarks
python3 run.py
To run, a specific benchmark, use the --bench
option:
python3 run.py --bench=fib
For testing, the ctest
utility is used along with some hand written helper headers.
To run existing tests, run ctest
inside the build directory (bin
or out
).
Tests of all kinds can be found under the test
directory.
(Note: time is in seconds)
If your terminal does not have ASNI support, or you want to pipe the benchmarks to a
file for reporting, then you can use the --nocolor
flag.
Currently, syntax highlighting and code completion snippets are supported on the following code editors:
- VS Code
- Sublime text [In Progress]
- Vim [Coming soon]
- Atom [Coming soon]
If you're looking to contribute to vyse, It is recommended to have clang-format for formatting and clangd language server for your text editor.
On VSCode, the C/C++
extension can be used to debug the executable.
Alternatively, you can use GDC/LLDB or other debuggers to your liking.
The sources are in src/
while the public headers are in include/
.
A basic CLI module is present under cli/
.