Interfacing with Other Languages

This is the final part of the quick guide.

To wrap things up, let's look at how you can run programs written in other languages from VCSSL.

Note: In the case of Vnano, the approach to interfacing with external programs -- particularly with the host Java application it's embedded in -- is quite different from VCSSL. For more details, refer to the [guides available on the official Vnano website.
- Table of Contents -

Running Executables
(Compiled C/C++ Programs, OS Commands, Shell Scripts, etc.)

To invoke a program written in a compiled language like C or C++, the general approach is to call the compiled executable directly (e.g. .exe on Windows, .out or similar on Linux).

Creating a Sample C Program

To make things easier to follow, let's create a simple C program as an example.


# include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Hello C and VCSSL!\n");
    return 0;
}
example.c

Compile this using your preferred compiler, and generate an executable named:

example.exe (or 'example.out' on Linux and similar systems)

We won't go into the compilation steps here, since plenty of C tutorials already cover that in detail.

Once you've compiled it, running the executable should print the following to standard output:

Hello C and VCSSL!

If that works, you're good to move on.

Running It from VCSSL

The simplest way to run that executable from VCSSL is to use the system or exec functions. These run the program in a few steps:


import File;

// Execute example.exe
// ("argA/B/C" are arguments passed to the program)
string programPath = getFilePath("example.exe");
system(programPath, "argA", "argB", "argC");
Exec.vcssl

If all you want is to call it, it doesn't get easier than this. But let's be honest --

When calling external programs from a script, isn't it usually debugging -- not the execution -- that eats up most of your time?

Things like:

...all of that takes more effort than just writing the call itself.

So instead of starting with the simplest case, let's jump straight to the practical one: A slightly longer setup, but far more useful.

We'll use the Process library for this:


import File;
import Process;

// Get absolute path to example.exe
// (Using a relative path or just the filename may fail depending on the environment)
string programPath = getFilePath("example.exe");

// Define arguments (program path + command-line args)
string processArgs[] = { programPath, "argA", "argB", "argC" };

// Create process
int processID = newProcess(processArgs);

// Start execution
startProcess(processID);

// Optional: send input if needed
// (Be aware: input may not be accepted unless it ends with a newline)
setProcessInput(processID, "Hello!" + EOL);

// Wait for process to finish
waitForProcess(processID);

// Get standard output and error
// (The entire output during execution is buffered)
string processOutput = getProcessOutput(processID);
string processError = getProcessError(processID);

// Output to VCSSL console
println("Standard Output: " + processOutput);
println("Standard Error: " + processError);
SerialExecution.vcssl

When you run this, you'll get:

Standard Output: Hello C and VCSSL!

Standard Error:

Here, the external program is launched using newProcess(...), which:

Using that process ID, you can:

If You See Garbled Characters, Specify the Encoding

If the external program prints non-ASCII output, like Japanese text, you may encounter character encoding issues.

This happens when the output encoding of the external program doesn't match what VCSSL expects.

For example, on Windows, many old C programs output Japanese text in CP932 encoding (roughly equivalent to Shift_JIS).
In such cases, add these lines before starting the process:


// Create process
string processArgs[] = { programPath, "argA", "argB", "argC" };
int processID = newProcess(processArgs);

// Set I/O encoding
setProcessInputEncoding(processID, "CP932");
setProcessOutputEncoding(processID, "CP932");
setProcessErrorEncoding(processID, "CP932");

// Start execution
startProcess(processID);
...
Encoding.vcssl

Without this, you may see garbled output for Japanese program. Note that VCSSL uses UTF-8 by default if no encoding is specified.

Receiving Standard Output/Error in Event Handlers

Previously, we saw how to retrieve the standard output and standard error from an external executable after it finishes running.

Alternatively, you can also capture them in real time using event handlers like this:


import File;
import Process;

// Get the absolute path of example.exe
//(Relative paths or just the filename might fail depending on the environment)
string programPath = getFilePath("example.exe");

// Create and run the process for example.exe
// ("argA/B/C" are arguments passed to the program)
string processArgs[] = { programPath, "argA", "argB", "argC" };
int processID = newProcess(processArgs);
startProcess(processID);


// Event handler for standard output
void onProcessOutput(int sourceProcessId, string text) {
    print(text);
}

// Event handler for standard error
void onProcessError(int sourceProcessId, string text) {
    print(text);
}
EventExecution.vcssl

With this setup, whenever the running program outputs something to standard output or standard error, the onProcessOutput or onProcessError function is triggered accordingly.

The arguments passed to these handlers are:

Note, however, that the handler is not triggered every single time a byte is output. Instead, the output is buffered to a certain extent before being passed to the event.

Usually, the output is buffered per line, so in most cases, the argument "text" will contain a full line of output.

That said, if a very long line is output without line breaks (exceeding the buffer size), the output may be split across multiple event calls. So while line-by-line handling is typical, it's not guaranteed.

When Should You Use This?

You might be wondering, "Why bother with event handlers for this?" The answer is: when interactive input/output is required.

Many command-line programs behave like this:

In such cases, you must respond at the correct moment based on the output, or the process will hang. Using event handlers lets you detect output in real time and respond dynamically based on its content.

Running OS Commands: Use a Shell Script

Until now, we've assumed you're calling a C-language program. But what if you want to run OS-dependent commands?

In such cases, the most reliable approach is:

Technically, you can pass command strings directly to the shell (as a one-liner), depending on the shell you're using.

But honestly, invoking external programs is inherently difficult to debug, so it's wise to expect trouble (e.g. relative paths that work in the shell but fail when called from a one-liner in your program due to different working directories, or argument quoting issues where "C:\Program Files\something" gets split).

All of these are much easier to troubleshoot when written as a shell script. In fact, it's common to end up saying, "I should've just made a script from the start."

Running Other Scripting Languages via Interpreters?

Along similar lines, you might try calling the interpreter of another scripting language directly to run a script.

This might work, in principle, since the interpreter is just an executable too.

However, whether it works reliably depends entirely on the implementation of the interpreter being called.

Scripting language interpreters tend to be more complex than typical executables, and even small mismatches in environment or arguments can prevent them from running properly.

So it's best to treat this approach with a "fingers crossed" mindset -- if it runs, great! If not, you may need to dig into interpreter -- specific docs and behavior

Calling Code Written in Java

Now for the second half of this guide. This time, we'll look at how to call code written in the Java programming language from VCSSL.

Why cover Java separately from other languages? Because Java code can be called in a different way. You see, the VCSSL runtime itself is written in Java, and many of the built-in functions are actually implemented in Java.

That means there's already an interface available to implement your own "built-in functions" in Java, which can then be called from VCSSL just like any other function. Let's take a look at how to use that.

GPCI2 Interface

There are several kinds of interfaces available for use, but most of the publicly documented ones (like those listed here: VCSSL/Vnano Plugin Interface GitHub Repo) are designed primarily for Vnano, not VCSSL.

Note: This is because the current VCSSL runtime is a generation older than Vnano. In the next major version, broader support for these interfaces is planned.

Still, there's one interface that's been supported since the early days -- while it's not ideal for ultra-high-frequency use (due to its call overhead), it's simple to use and works well in many situations:

Let's try using this interface.

Compiling the Interface

You could grab the interface declaration file from the link above, but setting up the package path and classpath can be a bit of a hassle.

So instead, just copy and paste the following code into a file named "GeneralProcessConnectionInterface2.java" and save it wherever you like:


public interface GeneralProcessConnectionInterface2 {

    // Initialization logic before the script is executed
    public void init();

    // Cleanup logic after the script is done
    public void dispose();

    // Return true only for the function names you want to process
    public boolean isProcessable(String functionName);

    // Implement the logic to be executed when VCSSL calls the function
    public String[] process(String functionName, String[] args);
}
GeneralProcessConnectionInterface2.java

Since this will be loaded via reflection, as long as the method names and signatures match, you're good to go. Just compile it like this:

javac GeneralProcessConnectionInterface2.java

This interface declares four methods, and each one has a specific role -- refer to the inline comments in the code above for a quick overview.

Implementing the Desired Functionality According to the Interface (i.e., Writing a Plugin)

Now let's create a separate Java class that implements the interface we defined earlier, and write the processing logic we want to use.

In VCSSL and Vnano, Java-based components that provide built-in functions or other runtime features are called plugins.

So what we're doing here is writing a plugin. Let's call the class ExamplePlugin:


public class ExamplePlugin implements GeneralProcessConnectionInterface2 {

    // Initialization logic before the script is executed
    @Override
    public void init() {
        System.out.println("Initializing plugin...");
    }

    // Cleanup logic after the script finishes
    @Override
    public void dispose() {
        System.out.println("Disposing plugin...");
    }

    // Return true only for the function(s) you want to handle
    @Override
    public boolean isProcessable(String functionName) {
        if (functionName.equals("exampleFunction")) {
            return true;
        }
        return false;
    }

    // Main logic to execute when the function is called from VCSSL
    @Override
    public String[] process(String functionName, String[] args) {
        if (functionName.equals("exampleFunction")) {
            return new String[]{ "exampleFunction was called: arg0=" + args[0] + ", arg1=" + args[1] };
        }

        System.err.println("!!! If this gets printed, the function name was likely mistyped.");
        return null;
    }
}
ExamplePlugin.java

So what does this plugin do?

Now compile the plugin in the same folder where you saved the compiled GPCI2 interface GeneralProcessConnectionInterface2.class:

javac ExamplePlugin.java

If ExamplePlugin.class is generated successfully, your plugin is ready.

Calling It from VCSSL

Let's now try calling the plugin from VCSSL.

In the same folder where you have ExamplePlugin.class, create a VCSSL script file and write the following:


// Load the plugin ExamplePlugin.class
connect ExamplePlugin;

// Call the function "exampleFunction" provided by the plugin
string ret[] = exampleFunction("Hello", "World!");

// Output the result
println(ret);
ExamplePluginCall.vcssl

If everything goes well, you should see the following output:

exampleFunction was called: arg0=Hello, arg1=World!

Just like that, the message generated on the plugin side has been successfully returned to VCSSL. Success!

Understanding and Working Around GPCI2's Quirks

As we've seen above, calling Java methods via the GPCI2 interface is quite simple -- and for many use cases, it's already more than sufficient. That's why GPCI2 is likely to remain supported for the foreseeable future.

However, since this interface was designed in the early days of VCSSL, it comes with a few legacy quirks. Some of them result in extra overhead, others make things feel a little -- too "loose," and a few are just plain awkward. They're not necessarily flaws, but more like charming oddities rooted in the history of the platform.

Let's go over what to watch out for, and how to work around these quirks when needed.

Arbitrary Argument Types and Counts: Wrap with a VCSSL Function

GPCI2 has very loose type constraints. For instance, even though "exampleFunction" was intended to take strings, you can still call it like this:


string ret[] = exampleFunction(123, 4.56, true);
QuirksAnyType.vcssl

All arguments passed from VCSSL are automatically converted to strings before being passed to the plugin's "args" array. This is incredibly flexible -- perhaps too flexible for a statically typed language.

This can be problematic when, for example, your plugin assumes the arguments are integers but VCSSL users pass in "ABCDE" or some other non-numeric string. In statically typed code, you'd normally expect the compiler to catch this mismatch for you.

So how do we avoid this? The common workaround is to wrap your plugin function inside a VCSSL function, like this:


// Define a wrapper function in VCSSL that enforces strict typing
string wrapperFunction(int arg0, int arg1) {
    return exampleFunction(arg0, arg1);
}
QuirksAnyTypeWrap.vcssl

If you define a wrapper like this in a separate VCSSL library file and import it into your main script, VCSSL will enforce type checking before calling the plugin.

Zero Arguments? You Still Get One (Empty) in the Plugin: Use Overloaded Wrappers

Here's another gotcha:

Logically, you might expect args.length == 0, but in practice, you get:

There's no real reason for this. Itfs just a legacy quirk from the very first VCSSL engine:

In the early engine design, it was inconvenient to represent the absence of argument data, so the engine always passed a placeholder argument (an empty string), similar to how C uses void to represent no arguments.

The real problem is that your plugin can't distinguish between these two cases:

If this distinction matters for your logic, the only safe workaround is again to use a VCSSL wrapper function and create separate overloads for the two use cases.

In the future, GPCI3 (the successor to GPCI2) is expected to offer a compatibility mode switch that will allow more precise handling of this situation, but it's not available yet.

Only One Array Argument Allowed

In GPCI2, you can call a plugin function with a single array argument like this:


string args[] = {"123", "4.56", "true"};
string ret[] = exampleFunction(args);
QuirksArrayArg.vcssl

However, you can't pass multiple arrays. Only one array can be passed as a single argument.

If you must pass multiple arrays, you'll need to flatten and serialize them into a single array, embedding any structure/length info manually. This is a limitation of GPCI2 that more modern interfaces don't have.

High Overhead

As you may have noticed, GPCI2 is quite "free-form" when it comes to types. Internally, the engine performs type conversions to string arrays for every call, and that alone introduces significant overhead.

On top of that, every plugin call involves a runtime check like this:


// Main logic to execute when the function is called from VCSSL
@Override
public String[] process(String functionName, String[] args) {
    if (functionName.equals("exampleFunction")) {
        ...
QuirksOverhead.java

So every time the function is called from VCSSL, this string comparison is executed. That adds up quickly, especially in tight loops or high-frequency scenarios like numerical simulations.

Therefore, it's not a good idea to implement performance-critical logic using GPCI2.

Unfortunately, the faster plugin interfaces that avoid this overhead aren't currently supported in the VCSSL engine (they are supported in Vnano, though). We'd like to support those in VCSSL as well in the future -- especially if there's demand -- but as of now, there's no real workaround.

Conclusion

And with that, this quick-start guide comes to an end!

Over the course of five sessions, we've taken a fast-paced tour through various parts of VCSSL to help you get a taste of the language's overall capabilities. How did you find it?

If this guide has helped you get a rough sense of "what kinds of features VCSSL offers" and "how to approach typical use cases," then it has achieved its goal -- and I'm glad it could be of help.

If you'd like to dive deeper into specific topics, there are more detailed guides available for free on the official VCSSL website:

Syntax Guide
A reference-style guide covering the syntax rules of VCSSL in a straightforward format.
GUI Development Guide
A guide to developing applications using VCSSLfs GUI features.
2D Graphics Development Guide
Covers how to work with 2D graphics in VCSSL.
3D Graphics Development Guide
Covers how to use VCSSLfs 3D graphics functionality.
Plotting 2D Graphs
An official guide (under RINEARN Graph 2D) for plotting 2D graphs using VCSSL.
Plotting 3D Graphs
An official guide (under RINEARN Graph 3D) for plotting 3D graphs using VCSSL.
Standard Library Specification
Full specifications of all functions and variables provided in the standard libraries.
VCSSL Code Archive
A large collection of actual VCSSL programs with explanatory articles, covering a wide variety of use cases.

If you're interested in exploring more deeply, feel free to make full use of the resources above!