Browse Source

merged makefile

tags/v1.3.0
max 5 years ago
parent
commit
8b2b43a941
7 changed files with 533 additions and 182 deletions
  1. +12
    -1
      Makefile
  2. +27
    -2
      README.md
  3. +118
    -0
      examples/rainbow.pd
  4. +80
    -0
      examples/synth.vult
  5. +45
    -0
      examples/vco.vult
  6. +97
    -179
      src/LuaJITEngine.cpp
  7. +154
    -0
      src/VultEngine.cpp

+ 12
- 1
Makefile View File

@@ -31,7 +31,7 @@ $(efsw):
cd efsw && cp lib/libefsw-static-release.a $(DEP_PATH)/lib/
cd efsw && cp -R include/efsw $(DEP_PATH)/include/

# LibPD
# libpd
ifeq ($(LIBPD), 1)
libpd := dep/lib/libpd.a
SOURCES += src/LibPDEngine.cpp
@@ -206,5 +206,16 @@ endif
# cd dep/llvm-8.0.1.src/build && $(MAKE)
# cd dep/llvm-8.0.1.src/build && $(MAKE) install

# Vult
ifeq ($(VULT), 1)
SOURCES += src/VultEngine.cpp
vult := dep/vult/vultc.h
$(vult):
cd dep && mkdir -p vult
cd dep/vult && $(WGET) "https://github.com/modlfo/vult/releases/download/v0.4.9/vultc.h"
$(SHA256) $(vult) 91f575afd2913d0879df90ee666021065ad726372f0bd306198024dc771cce55
FLAGS += -Idep/vult
DEPS += $(vult)
endif

include $(RACK_DIR)/plugin.mk

+ 27
- 2
README.md View File

@@ -8,7 +8,10 @@ Scripting language host for [VCV Rack](https://vcvrack.com/) containing:
- 6 switches with RGB LEDs

Supported scripting languages:
- JavaScript (.js)
- JavaScript (ES2020) (.js)
- [Lua](https://www.lua.org/) (.lua)
- [Vult](https://github.com/modlfo/vult) (.vult)
- [Pure Data](https://puredata.info) (.pd)
- [Add your own below](#adding-a-script-engine)

[Discussion thread](https://community.vcvrack.com/t/vcv-prototype/3271)
@@ -89,6 +92,9 @@ function process(block) {
}
```

*The Vult API is slightly different than Prototype's scripting API.
See `examples/template.vult` for a reference of the Vult API.*

## Build dependencies

### Windows
@@ -111,6 +117,23 @@ sudo apt install premake4
sudo pacman -S premake
```

## Build
### Add path to Rack-SDK
```bash
export RACK_DIR=/set/path/to/Rack-SDK/
```

### load submodules
```bash
git submodule update --init --recursive
```

### Make
```bash
make dep
make
```

## Adding a script engine

- Add your scripting language library to the build system so it builds with `make dep`, following the Duktape example in `Makefile`.
@@ -123,6 +146,8 @@ sudo pacman -S premake
## Contributors

- [Wes Milholen](https://grayscale.info/): panel design
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript, disabled), LuaJIT (Lua), Python (in development)
- [Andrew Belt](https://github.com/AndrewBelt): host code, Duktape (JavaScript, not used), LuaJIT (Lua), Python (in development)
- [Jerry Sievert](https://github.com/JerrySievert): QuickJS (JavaScript)
- [Leonardo Laguna Ruiz](https://github.com/modlfo): Vult
- [CHAIR](https://chair.audio) [Clemens Wegener (libpd), Max Neupert (patches)] : libpd
- add your name here

+ 118
- 0
examples/rainbow.pd View File

@@ -1,4 +1,8 @@
<<<<<<< HEAD
#N canvas 547 398 828 731 12;
=======
#N canvas 698 144 821 731 12;
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#N canvas 250 355 563 749 hsv2rgb 0;
#X obj 20 64 route 0;
#X msg 20 36 \$2 \$3 \$1;
@@ -912,8 +916,11 @@
#X msg 317 400 S4 \$1 \$2 \$3;
#X msg 413 400 S5 \$1 \$2 \$3;
#X msg 509 400 S6 \$1 \$2 \$3;
<<<<<<< HEAD
#X obj 294 17 loadbang;
#X obj 294 71 print toVCV;
=======
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X obj 30 92 metro 2000;
#X msg 62 426 L1 \$1 \$2 \$3;
#X msg 158 426 L2 \$1 \$2 \$3;
@@ -922,7 +929,10 @@
#X msg 447 426 L5 \$1 \$2 \$3;
#X msg 542 426 L6 \$1 \$2 \$3;
#X obj 29 592 dac~ 1 2 3 4 5 6, f 69;
<<<<<<< HEAD
#X msg 294 43 display Hello world!;
=======
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X obj 29 369 t l l;
#X obj 125 369 t l l;
#X obj 221 369 t l l;
@@ -930,7 +940,10 @@
#X obj 413 369 t l l;
#X obj 509 369 t l l;
#X msg 125 316 \$1 1 1;
<<<<<<< HEAD
#X msg 30 118 0 \, 100 2000;
=======
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X obj 125 201 + 16.6667;
#X obj 125 227 % 100;
#X obj 29 249 / 100;
@@ -976,11 +989,34 @@
#X obj 316 559 +~ 1;
#X obj 413 560 +~ 1;
#X obj 508 560 +~ 1;
<<<<<<< HEAD
#X connect 0 0 29 0;
=======
#N canvas 0 68 624 300 display 0;
#X obj 29 10 loadbang;
#X msg 29 34 44;
#X obj 29 140 print toVCV;
#X obj 29 90 list prepend display;
#X obj 29 115 list trim;
#X obj 29 62 makefilename Hello%c\ world!;
#X text 264 22 A somewhat ugly workaround just to print a comma. In
Pd \, commas separate messages., f 32;
#X connect 0 0 1 0;
#X connect 1 0 5 0;
#X connect 3 0 4 0;
#X connect 4 0 2 0;
#X connect 5 0 3 0;
#X restore 229 19 pd display;
#X text 608 326 This should be an abstraction \, but to keep the example
directory tidy it here is duplicated code., f 28;
#X msg 30 118 100 \, 0 2000;
#X connect 0 0 26 0;
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X connect 2 0 1 0;
#X connect 3 0 4 0;
#X connect 4 0 6 0;
#X connect 5 0 0 0;
<<<<<<< HEAD
#X connect 6 0 20 0;
#X connect 7 0 56 0;
#X connect 8 0 30 0;
@@ -988,17 +1024,33 @@
#X connect 10 0 32 0;
#X connect 11 0 33 0;
#X connect 12 0 34 0;
=======
#X connect 6 0 18 0;
#X connect 7 0 52 0;
#X connect 8 0 27 0;
#X connect 9 0 28 0;
#X connect 10 0 29 0;
#X connect 11 0 30 0;
#X connect 12 0 31 0;
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X connect 13 0 1 0;
#X connect 14 0 1 0;
#X connect 15 0 1 0;
#X connect 16 0 1 0;
#X connect 17 0 1 0;
<<<<<<< HEAD
#X connect 18 0 28 0;
#X connect 20 0 36 0;
=======
#X connect 18 0 80 0;
#X connect 19 0 1 0;
#X connect 20 0 1 0;
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11
#X connect 21 0 1 0;
#X connect 22 0 1 0;
#X connect 23 0 1 0;
#X connect 24 0 1 0;
<<<<<<< HEAD
#X connect 25 0 1 0;
#X connect 26 0 1 0;
#X connect 28 0 19 0;
@@ -1066,3 +1118,69 @@
#X connect 79 0 27 3;
#X connect 80 0 27 4;
#X connect 81 0 27 5;
=======
#X connect 26 0 2 0;
#X connect 26 1 19 0;
#X connect 27 0 13 0;
#X connect 27 1 20 0;
#X connect 28 0 14 0;
#X connect 28 1 21 0;
#X connect 29 0 15 0;
#X connect 29 1 22 0;
#X connect 30 0 16 0;
#X connect 30 1 23 0;
#X connect 31 0 17 0;
#X connect 31 1 24 0;
#X connect 32 0 8 0;
#X connect 33 0 34 0;
#X connect 34 0 36 0;
#X connect 35 0 5 0;
#X connect 35 0 54 0;
#X connect 36 0 32 0;
#X connect 36 0 61 0;
#X connect 37 0 38 0;
#X connect 38 0 39 0;
#X connect 38 0 62 0;
#X connect 39 0 9 0;
#X connect 40 0 41 0;
#X connect 41 0 42 0;
#X connect 41 0 63 0;
#X connect 42 0 10 0;
#X connect 43 0 44 0;
#X connect 44 0 45 0;
#X connect 44 0 64 0;
#X connect 45 0 11 0;
#X connect 46 0 47 0;
#X connect 47 0 48 0;
#X connect 47 0 65 0;
#X connect 48 0 12 0;
#X connect 49 0 37 0;
#X connect 50 0 43 0;
#X connect 51 0 46 0;
#X connect 52 0 35 0;
#X connect 52 1 33 0;
#X connect 52 2 49 0;
#X connect 52 3 53 0;
#X connect 52 4 50 0;
#X connect 52 5 51 0;
#X connect 53 0 40 0;
#X connect 55 0 72 0;
#X connect 56 0 66 0;
#X connect 57 0 68 0;
#X connect 58 0 67 0;
#X connect 59 0 69 0;
#X connect 60 0 70 0;
#X connect 66 0 73 0;
#X connect 67 0 74 0;
#X connect 68 0 75 0;
#X connect 69 0 76 0;
#X connect 70 0 77 0;
#X connect 71 0 25 0;
#X connect 72 0 71 0;
#X connect 73 0 25 1;
#X connect 74 0 25 2;
#X connect 75 0 25 3;
#X connect 76 0 25 4;
#X connect 77 0 25 5;
#X connect 80 0 7 0;
>>>>>>> 2d8748675208b3c97908663eb33499dbf4213d11

+ 80
- 0
examples/synth.vult View File

@@ -0,0 +1,80 @@
/*

Simple synthesizer with one oscillator, LFO and envelope.

Author: Leonardo Laguna Ruiz - leonardo@vult-dsp.com

Check the API documentation in the basic.vult example

*/

fun env(gate) {
mem x;
val k = if gate > x then 0.05 else 0.0002;
x = x + (gate - x) * k;
return x;
}

fun edge(x):bool {
mem pre_x;
val v:bool = (pre_x <> x) && (pre_x == false);
pre_x = x;
return v;
}

fun pitchToFreq(cv) {
return 261.6256 * exp(cv * 0.69314718056);
}

fun phasor(pitch, reset){
mem phase;
val rate = pitchToFreq(pitch) * sampletime();
phase = phase + rate;
if(phase > 1.0)
phase = phase - 1.0;
if(reset)
phase = 0.0;
return phase;
}

fun oscillator(pitch, mod) {
mem pre_phase1;
// Implements the resonant filter simulation as shown in
// http://en.wikipedia.org/wiki/Phase_distortion_synthesis
val phase1 = phasor(pitch, false);
val comp = 1.0 - phase1;
val reset = edge((pre_phase1 - phase1) > 0.5);
pre_phase1 = phase1;
val phase2 = phasor(pitch + mod, reset);
val sine = sin(2.0 * pi() * phase2);
return sine * comp;
}

fun lfo(f, gate){
mem phase;
val rate = f * 10.0 * sampletime();
if(edge(gate > 0.0))
phase = 0.0;
phase = phase + rate;
if(phase > 1.0)
phase = phase - 1.0;
return sin(phase * 2.0 * pi()) - 0.5;
}

// Main processing function
fun process(cv, gate){
// LFO
val lfo_rate = getKnob(3);
val lfo_amt = getKnob(4);
val lfo_val = lfo(lfo_rate, gate) * lfo_amt;
// Oscillator
val pitch = getKnob(1) + 10.0 * cv - 2.0;
val mod = getKnob(2) * 2.0 + lfo_val;
val o = oscillator(pitch, mod);
// Envelope
val e = env(gate);
return o * e;
}
and update() {
_ = display("IN1: CV, IN2: GATE");
}

+ 45
- 0
examples/vco.vult View File

@@ -0,0 +1,45 @@

/*

Simple wave table VCO.

Author: Leonardo Laguna Ruiz - leonardo@vult-dsp.com

Check the API documentation in the basic.vult example

*/

fun pitchToFreq(cv) @[table(size=128, min=-10.0, max=10.0)] {
return 261.6256 * exp(cv * 0.69314718056);
}

// Generates (at compile time) a wave table based on the provided harmonics
// To change the wave table, change the 'harmonics' array
fun wavetable(phase) @[table(size=128, min=0.0, max=1.0)] {
val harmonics = [0.0, 1.0, 0.7, 0.5, 0.3];
val n = size(harmonics);
val acc = 0.0;
val i = 0;
while(i < n) {
acc = acc + harmonics[i] * sin(2.0 * pi() * real(i) * phase);
i = i + 1;
}
return acc / sqrt(real(n));
}

fun process(input_cv:real) {
mem phase;
val knob_cv = getKnob(1) - 0.5;
val cv = 10.0 * (input_cv + knob_cv);
val freq = pitchToFreq(cv);
val delta = sampletime() * freq;
phase = phase + delta;
if(phase > 1.0) {
phase = phase - 1.0;
}

return wavetable(phase);
}
and update() {

}

+ 97
- 179
src/LuaJITEngine.cpp View File

@@ -5,11 +5,22 @@
struct LuaJITEngine : ScriptEngine {
lua_State* L = NULL;

struct SafeArray {
void* p;
size_t len;
// This is a mirror of ProcessBlock that we are going to use
// to provide 1-based indices within the Lua VM
struct LuaProcessBlock {
float sampleRate;
float sampleTime;
int bufferSize;
float* inputs[NUM_ROWS + 1];
float* outputs[NUM_ROWS + 1];
float* knobs;
bool* switches;
float* lights[NUM_ROWS + 1];
float* switchLights[NUM_ROWS + 1];
};

LuaProcessBlock luaBlock;

~LuaJITEngine() {
if (L)
lua_close(L);
@@ -22,6 +33,20 @@ struct LuaJITEngine : ScriptEngine {
int run(const std::string& path, const std::string& script) override {
ProcessBlock* block = getProcessBlock();

// Initialize all the pointers with an offset of -1
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
luaBlock.knobs = &block->knobs[-1];
luaBlock.switches = &block->switches[-1];

for (int i = 0; i < NUM_ROWS; i++) {
luaBlock.inputs[i + 1] = &block->inputs[i][-1];
luaBlock.outputs[i + 1] = &block->outputs[i][-1];
luaBlock.lights[i + 1] = &block->lights[i][-1];
luaBlock.switchLights[i + 1] = &block->switchLights[i][-1];
}
#pragma GCC diagnostic pop

L = luaL_newstate();
if (!L) {
display("Could not create LuaJIT context");
@@ -29,16 +54,22 @@ struct LuaJITEngine : ScriptEngine {
}

// Import a subset of the standard library
luaopen_base(L);
luaopen_string(L);
luaopen_table(L);
luaopen_math(L);
luaopen_bit(L);
// Loads the JIT package otherwise it will be off
luaopen_jit(L);
// Disables access to the JIT package
lua_pushnil(L);
lua_setglobal(L,"jit");
static const luaL_Reg lj_lib_load[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_BITLIBNAME, luaopen_bit},
{LUA_JITLIBNAME, luaopen_jit},
{LUA_FFILIBNAME, luaopen_ffi},
{NULL, NULL}
};
for (const luaL_Reg* lib = lj_lib_load; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}

// Set user pointer
lua_pushlightuserdata(L, this);
@@ -63,7 +94,46 @@ struct LuaJITEngine : ScriptEngine {
}
lua_setglobal(L, "config");

// Compile script
// Load the FFI auxiliary functions.
std::stringstream ffi_stream;
ffi_stream
<< "local ffi = require('ffi')" << std::endl
// Describe the struct `LuaProcessBlock` so that LuaJIT knows how to access the data
<< "ffi.cdef[[" << std::endl
<< "struct LuaProcessBlock {" << std::endl
<< "float sampleRate;" << std::endl
<< "float sampleTime;" << std::endl
<< "int bufferSize;" << std::endl
<< "float *inputs[" << NUM_ROWS + 1 << "];" << std::endl
<< "float *outputs[" << NUM_ROWS + 1 << "];" << std::endl
<< "float *knobs;" << std::endl
<< "bool *switches;" << std::endl
<< "float *lights[" << NUM_ROWS + 1 << "];" << std::endl
<< "float *switchLights[" << NUM_ROWS + 1 << "];" << std::endl
<< "};]]" << std::endl
// Declare the function `_castBlock` used to transform `luaBlock` pointer into a LuaJIT cdata
<< "function _castBlock(b) return ffi.cast('struct LuaProcessBlock*', b) end";
std::string ffi_script = ffi_stream.str();

// Compile the ffi script
if (luaL_loadbuffer(L, ffi_script.c_str(), ffi_script.size(), "ffi_script.lua")) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
display(s);
lua_pop(L, 1);
return -1;
}

// Run the ffi script
if (lua_pcall(L, 0, 0, 0)) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
display(s);
lua_pop(L, 1);
return -1;
}

// Compile user script
if (luaL_loadbuffer(L, script.c_str(), script.size(), path.c_str())) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: %s", s);
@@ -104,102 +174,15 @@ struct LuaJITEngine : ScriptEngine {
return -1;
}

// FloatArray metatable
lua_newtable(L);
{
// __index
lua_pushcfunction(L, native_FloatArray_index);
lua_setfield(L, -2, "__index");
// __newindex
lua_pushcfunction(L, native_FloatArray_newindex);
lua_setfield(L, -2, "__newindex");
// __len
lua_pushcfunction(L, native_FloatArray_len);
lua_setfield(L, -2, "__len");
}
lua_setglobal(L, "FloatArray");

// BoolArray metatable
lua_newtable(L);
{
// __index
lua_pushcfunction(L, native_BoolArray_index);
lua_setfield(L, -2, "__index");
// __newindex
lua_pushcfunction(L, native_BoolArray_newindex);
lua_setfield(L, -2, "__newindex");
// __len
lua_pushcfunction(L, native_BoolArray_len);
lua_setfield(L, -2, "__len");
}
lua_setglobal(L, "BoolArray");

// Create block object
lua_newtable(L);
{
// inputs
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* input = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
input->p = &block->inputs[i];
input->len = block->bufferSize;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "inputs");

// outputs
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* output = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
output->p = &block->outputs[i];
output->len = block->bufferSize;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "outputs");

// knobs
SafeArray* knobs = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
knobs->p = &block->knobs;
knobs->len = 6;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "knobs");

// switches
SafeArray* switches = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
switches->p = &block->switches;
switches->len = 6;
lua_getglobal(L, "BoolArray");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "switches");

// lights
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* light = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
light->p = &block->lights[i];
light->len = 3;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "lights");

// switchLights
lua_newtable(L);
for (int i = 0; i < NUM_ROWS; i++) {
SafeArray* switchLight = (SafeArray*) lua_newuserdata(L, sizeof(SafeArray));
switchLight->p = &block->switchLights[i];
switchLight->len = 3;
lua_getglobal(L, "FloatArray");
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "switchLights");
lua_getglobal(L, "_castBlock");
lua_pushlightuserdata(L, (void*) &luaBlock);
if (lua_pcall(L, 1, 1, 0)) {
const char* s = lua_tostring(L, -1);
WARN("LuaJIT: Error casting block: %s", s);
display(s);
lua_pop(L, 1);
return -1;
}

return 0;
@@ -208,20 +191,11 @@ struct LuaJITEngine : ScriptEngine {
int process() override {
ProcessBlock* block = getProcessBlock();

// Set block
{
// sampleRate
lua_pushnumber(L, block->sampleRate);
lua_setfield(L, -2, "sampleRate");

// sampleTime
lua_pushnumber(L, block->sampleTime);
lua_setfield(L, -2, "sampleTime");

// bufferSize
lua_pushinteger(L, block->bufferSize);
lua_setfield(L, -2, "bufferSize");
}
// Update the values of the block.
// The pointer values do not change.
luaBlock.sampleRate = block->sampleRate;
luaBlock.sampleTime = block->sampleTime;
luaBlock.bufferSize = block->bufferSize;

// Duplicate process function
lua_pushvalue(L, -2);
@@ -264,62 +238,6 @@ struct LuaJITEngine : ScriptEngine {
getEngine(L)->display(s);
return 0;
}

static int native_FloatArray_index(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
float* data = (float*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
lua_pushnumber(L, data[index]);
return 1;
}
static int native_FloatArray_newindex(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
float* data = (float*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
data[index] = lua_tonumber(L, 3);
return 0;
}
static int native_FloatArray_len(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
lua_pushinteger(L, a->len);
return 1;
}

static int native_BoolArray_index(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
bool* data = (bool*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
lua_pushboolean(L, data[index]);
return 1;
}
static int native_BoolArray_newindex(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
bool* data = (bool*) a->p;
size_t index = lua_tointeger(L, 2) - 1;
if (index >= a->len) {
lua_pushstring(L, "Array out of bounds");
lua_error(L);
}
data[index] = lua_toboolean(L, 3);
return 0;
}
static int native_BoolArray_len(lua_State* L) {
SafeArray* a = (SafeArray*) lua_touserdata(L, 1);
lua_pushinteger(L, a->len);
return 1;
}
};




+ 154
- 0
src/VultEngine.cpp View File

@@ -0,0 +1,154 @@
#include "ScriptEngine.hpp"
#include "vultc.h"
#include <quickjs/quickjs.h>

/* The Vult engine relies on both QuickJS and LuaJIT.
*
* The compiler is written in OCaml but converted to JavaScript. The JavaScript
* code is embedded as a string and executed by the QuickJs engine. The Vult
* compiler generates Lua code that is executed by the LuaJIT engine.
*/

// Special version of createScriptEngine that only creates Lua engines
ScriptEngine* createLuaEngine() {
auto it = scriptEngineFactories.find("lua");
if (it == scriptEngineFactories.end())
return NULL;
return it->second->createScriptEngine();
}

struct VultEngine : ScriptEngine {

// used to run the lua generated code
ScriptEngine* luaEngine;

// used to run the Vult compiler
JSRuntime* rt = NULL;
JSContext* ctx = NULL;

VultEngine() {
rt = JS_NewRuntime();
// Create QuickJS context
ctx = JS_NewContext(rt);
if (!ctx) {
display("Could not create QuickJS context");
return;
}

JSValue global_obj = JS_GetGlobalObject(ctx);

// Load the Vult compiler code
JSValue val =
JS_Eval(ctx, (const char*)vultc_h, vultc_h_size, "vultc.js", 0);
if (JS_IsException(val)) {
display("Error loading the Vult compiler");
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, global_obj);
return;
}
}

~VultEngine() {
if (ctx) {
JS_FreeContext(ctx);
}
if (rt) {
JS_FreeRuntime(rt);
}
}

std::string getEngineName() override {
return "Vult";
}

int run(const std::string& path, const std::string& script) override {
display("Loading...");

JSValue global_obj = JS_GetGlobalObject(ctx);

// Put the script text in the 'code' variable
JSValue code = JS_NewString(ctx, script.c_str());
JS_SetPropertyStr(ctx, global_obj, "code", code);

// Put the script path in 'file' variable
JSValue file = JS_NewString(ctx, path.c_str());
JS_SetPropertyStr(ctx, global_obj, "file", file);

display("Compiling...");
// Call the Vult compiler to generate Lua code
static const std::string testVult = R"(
var result = vult.generateLua([{ file:file, code:code}],{ output:'Engine', template:'vcv-prototype'});)";

JSValue compile =
JS_Eval(ctx, testVult.c_str(), testVult.size(), "Compile", 0);

JS_FreeValue(ctx, code);
JS_FreeValue(ctx, file);

// If there are any internal errors, the execution could fail
if (JS_IsException(compile)) {
display("Fatal error in the Vult compiler");
JS_FreeValue(ctx, global_obj);
return -1;
}

// Retrive the variable 'result'
JSValue result = JS_GetPropertyStr(ctx, global_obj, "result");
// Get the first element of the 'result' array
JSValue first = JS_GetPropertyUint32(ctx, result, 0);
// Try to get the 'msg' field which is only present in error messages
JSValue msg = JS_GetPropertyStr(ctx, first, "msg");
// Display the error if any
if (!JS_IsUndefined(msg)) {
const char* text = JS_ToCString(ctx, msg);
const char* row =
JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "line"));
const char* col = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "col"));
// Compose the error message
std::stringstream error;
error << "line:" << row << ":" << col << ": " << text;
WARN("Vult Error: %s", error.str().c_str());
display(error.str().c_str());

JS_FreeValue(ctx, result);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
return -1;
}
// In case of no error, retrieve the generated code
JSValue luacode = JS_GetPropertyStr(ctx, first, "code");
std::string luacode_str(JS_ToCString(ctx, luacode));

//WARN("Generated Code: %s", luacode_str.c_str());

luaEngine = createLuaEngine();

if (!luaEngine) {
WARN("Could not create a Lua script engine");
return -1;
}

luaEngine->module = this->module;

display("Running...");

JS_FreeValue(ctx, luacode);
JS_FreeValue(ctx, first);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, msg);
JS_FreeValue(ctx, global_obj);

return luaEngine->run(path, luacode_str);
}

int process() override {
if (!luaEngine)
return -1;
return luaEngine->process();
}
};

__attribute__((constructor(1000)))
static void constructor() {
addScriptEngine<VultEngine>("vult");
}

Loading…
Cancel
Save