Main Syntax and Features of VCSSL
In the previous section, we covered how to run VCSSL programs and perform basic input/output.
This time, let's take a look at the main syntax and features of VCSSL.
Since this is a quick-start guide, I'll keep it brief and to the point, assuming you're already familiar with a C-style language.
Note: The code examples here are written in VCSSL, but unless otherwise stated, they apply equally to Vnano, which is a subset of VCSSL.
Sponsored Link
Variables, Types, and Operators
Declaring Variables
Here's how you declare variables:
int a; // Declaration only
int b = 1; // Declaration with initialization
const int N = 100; // Constant (immutable variable)
VariableDeclear.vcssl
Variables can be declared in both the global and local scope.
Global variables allow duplicate declarations (as long as the type is the same), but local variables do not.
Also, global variables can be accessed even before their declaration line, whereas local variables must be declared before use.
Global constants declared with "const" are initialized before any other statements are executed.
In Vnano, the special treatment for global variables is not supported -- all variables behave like local ones.
Primitive Types
VCSSL has five basic primitive types: int, float, complex (not supported in Vnano), string, and bool. There are also other extended types like arbitrary-precision integers/numbers, but these five are enough for general use:
int a = 1; // Integer
float f = 2.3; // Floating-point
complex c = 4.0 + 5.0*I; // Complex number (I is the imaginary unit) - not available in Vnano
string s = "Hello"; // String
bool b = true; // Boolean
print(a, f, c, s, b);
VariableType.vcssl
Output:
These types are familiar if you've used other languages, but here are a few VCSSL-specific notes:
- The precision of "float" and "int" depends on the implementation, but float is typically double-precision, and int is 64-bit signed.
- You can use "double" and "long", but they are treated as synonyms for float and int, respectively.
- The real and imaginary parts of "complex" values (accessed via re() and im()) are of type float.
- "string" is a value type, but it's nullable -- meaning it can be compared to or assigned NULL. (In Vnano, it is not nullable.)
Operators on Primitive Types
Here's a quick overview of the operator behavior for each primitive type.
For int, float, and complex, you can use all basic arithmetic and comparison operators. The result type for arithmetic operations is as follows:
- int + int → int
- float + float, or int + float, or float + int → float
- Any operation involving complex → complex
Note: complex supports arithmetic, but not comparison operators (like <, excluding == and !=).
For string, it behaves as a value type (not a reference). In VCSSL, you can add strings together:
- string + string → concatenated string
- string + other types → all values are converted to strings and concatenated → result is string
bool is a boolean type used for logical operations. Comparison operations return a bool, and conditionals (like if) must evaluate to a bool.
Arrays
Let's talk about arrays -- but first, one important thing:
Unlike most C-style languages, arrays in VCSSL behave as value types, not reference types. (VCSSL avoids reference types entirely to eliminate the need for garbage collection.)
So when you assign one array to another, it copies all elements -- not just a reference. The arrays do not share memory.
Here's how to use them:
int a[3]; // You can also write: int[3] a;
a[0] = 0;
a[1] = 1;
a[2] = 2;
print(a);
Array.vcssl
Output:
Multidimensional arrays can be declared like this:
int a[2][3];
Array2D.vcssl
You can also initialize arrays upon declaration (not supported in Vnano):
int a[ ] = {0, 1, 2};
ArrayInit.vcssl
If you omit the element count without initializing, it creates an array of size 0.
To get the length of an array, use the length(...) function:
int a[3];
int n = length(a, 0); // Get the number of elements in dimension 0
print(a);
ArrayLength.vcssl
Output:
For multidimensional arrays, specify the dimension index:
int a[2][3];
int n0 = length(a, 0);
int n1 = length(a, 1);
print(n0, n1);
ArrayLength2D.vcssl
This will output:
You can also change the size of an array dynamically using alloc(...):
int a[2]; // Initial size: 2
a[0] = 0;
a[1] = 1;
alloc(a, 3); // Resize to 3 elements
a[2] = 2;
print(a);
ArrayAllocFunction.vcssl
Output:
Values from the original elements are preserved after resizing. So it's more like realloc() in C than malloc().
Structures (Not Available in Vnano)
To wrap up the topic of types, let's look at structures:
// Structure declaration
struct Box {
int width;
int height;
}
// Declaring and using a structure variable
Box box; // No need for "struct" -- in fact, you must not include it here!
box.width = 100;
box.height = 200;
println(box.width, box.height);
StructBox.vcssl
This will output:
One important thing: When declaring a structure variable, you must not write "struct" like in C. The "struct" keyword is only used when defining the structure type itself.
Also, structures are value types in VCSSL -- there's no way to make them reference types. So when you assign one structure to another, all member values are copied.
Structures are also nullable, meaning they can be compared to and assigned NULL. As of now, there are no restrictions on that.
That said, in the current implementation of the VCSSL runtime, structures are relatively heavy in terms of memory usage and processing speed. If you're working with large arrays or tight performance -- critical loops, it's best not to rely on them too much for now. They're expected to be improved in the next generation of the runtime.
Control Structures
if Statements (and if-else)
VCSSL has only three control structures: "if" (including "if-else"), "for", and "while".
Here's an example of an if statement:
int a = input("Please enter an integer", 100);
if(a >= 10) {
print("10 or greater");
} else if(a >= 5) {
print("5 or greater");
} else {
print("4 or less");
}
If.vcssl
The message changes based on the input. Note that condition expressions **must return** a bool.
Also, VCSSL does not allow "dangling" control structures, so block braces { } are required for all if, else, for, and while blocks. If you omit them like this:
if(a >= 10) print("10 or greater"); // This will cause an error
IfError.vcssl
...it won't run.
Now, if you're sharp on C grammar, you might wonder:
You're absolutely right. But in VCSSL, "else if" is treated as a single composite keyword, not as an "else" with an attached "if".
- We didn't want to break away from the familiar "else if" idiom that's everywhere in C-style languages.
- But we still wanted to forbid brace-less blocks.
for Loops
Here's an example using for:
int a = input("Please enter an integer", 10);
for(int i=1; i<=a; i++) {
println(i);
}
For.vcssl
This prints all positive integers up to the input value.
Just keep in mind that printing to the console is slower than calculations, so if you increase the loop count too much, the output may take some time to complete.
while Loops
Here's how to do the same thing with a while loop:
int a = input("Please enter an integer", 10);
int i=1;
while(i<=a) {
println(i);
i++;
}
While.vcssl
Functions
Declaring Functions
Function declarations in VCSSL follow the typical format you'd expect from a C-style language:
int add(int a, int b) {
return a+b;
}
int c = add(1, 2);
print(c);
Function.vcssl
This will output:
If you want to pass or return arrays, you can write it like this:
int[ ] add(int a[ ], int b[ ]) {
...
FunctionArray.vcssl
Function Overloading
Functions in VCSSL support overloading. That means you can declare multiple functions with the same name, as long as their parameter types or counts differ.
If two functions have exactly the same signature, the one declared later takes precedence.
Passing Arguments by Reference
You can also pass arguments by reference. Just prefix the parameter name with & like so:
- int &a
- int &a[]
That's all there is to it.
Just note that in the second form (int &a[]), the syntax differs slightly from C++. In C++, you'd write something like int (&a)[], so be careful not to get tripped up.
That Weird-Looking "const" in Parameters
You might sometimes see function parameters that look like this:
- const int &a
- const int &a[]
This means the parameter is passed by reference, but the function promises not to modify it.
At first glance, it might seem pointless to pass something by reference if you're not going to change it. But depending on the context, this can actually help with performance or optimization -- though in other cases it might hurt performance.
You might spot this kind of thing occasionally in the standard library (like here), but unless you're working at a low level, you don't really need to worry about it too much.
Dynamic Evaluation (eval) (Not Available in Vnano)
As with many interpreter-based languages, VCSSL lets you evaluate code dynamically using the eval(...) function.
string expr = "1+2";
int a = eval(expr); // Evaluate the string as an expression
print(a);
EvalExpr.vcssl
This will output:
It supports lexical scoping, so you can assign variables dynamically as well:
string expr = "a=1+2";
int a;
eval(expr);
print(a);
EvalStat.vcssl
The return value of eval(...) is a string[], so if you assign it to a non-array variable like int or float, there's a conversion cost involved. In tight loops or performance-sensitive code, using assignments (like in the second example above) is usually faster.
As for the overhead of parsing the expression string inside eval(...), don't worry -- the interpreter caches a lot of it behind the scenes, so performance is very close to writing the expression directly in code.
Note: As you may already know, using eval() comes with serious security considerations. Never pass user input to eval(...) from untrusted sources (like the internet, or unchecked local files). If someone with malicious intent gets through, the consequences could be severe.
Realistically, the only safe use cases for eval() are:
- When you, the programmer, are intentionally calling known functions via string names (like callbacks).
- When the user of your program is knowingly entering part of a process (e.g., entering a formula they intend to compute), and is accepting that risk.
Even then, VCSSL restricts eval(...) from calling potentially dangerous functions, to protect against mischief or careless input.
File Input and Output
File I/O is essential for tasks like numerical computation, so here's a quick overview of how it works in VCSSL.
Simple File Input/Output
Let's start with the simplest approach -- quick and easy file I/O.
It doesn't support anything too fancy, but it gets the job done:
save("test.txt", "Hello World"); // Write
string s = load("test.txt"); // Read
println(s);
SimpleIO.vcssl
This creates a file named "test.txt", writes "Hello World" into it, reads it back, and displays it in the console.
If you want to insert line breaks, just include the "EOL" constant in the content:
save("test.txt", "Hello" + EOL + "World"); // Write
SaveWithEOL.vcssl
EOL is a system-dependent newline constant. If you specifically want to write LF (0x0A), use the LF constant. For CR (0x0D), use CR constant.
Basic File Input/Output
If you need a bit more control, here's a more traditional approach:
int file = open("test.txt", "w"); // Open file in write mode
writeln(file, "Hello"); // Write "Hello" with a newline
writeln(file, "World"); // Write "World" with a newline
close(file); // Close the file
Writeln.vcssl
This creates "test.txt" with "Hello" and "World" on separate lines.
The open(...) function opens the file and returns an integer file ID.
The second argument "w" means "write mode." If you want append mode instead, use "a". You can also use the constants WRITE and APPEND instead of the string modes.
writeln(...) writes to the file and adds a newline. There's also a write(...) function for writing without a newline. In both cases, the first argument is the file ID, and the rest are the values to write.
Now let's look at reading files:
int file = open("test.txt", "r"); // Open in read mode
int n = countln(file); // Get the number of lines
for(int i=0; i<n; i++){
string line = readln(file); // Read one line from the file
println(line); // Print it to the console
}
close(file); // Close the file
Readln.vcssl
If you run this with a "test.txt" file in the same directory, it will output its contents.
The "r" mode means gread mode,h and you can also use the constant READ instead.
readln(...) reads one line at a time. Technically, it returns an array, but in "r" mode, it returns an array with just one element (the full line), so you can store it directly in a non-array variable like line.
Numeric CSV and TSV Input/Output
The open(...) function also supports numeric CSV and TSV formats. Let's start with writing CSV:
int file = open("test.csv", "wcsv"); // Open in CSV write mode
writeln(file, 0, 1, 2); // Write numbers, comma-separated, with a newline
writeln(file, 3, 4, 5);
close(file); // Close the file
WritelnCSV.vcssl
This writes the lines "0,1,2" and "3,4,5" to "test.csv". You can also use the constant WRITE_CSV instead of "wcsv".
Now for reading:
int file = open("test.csv", "rcsv"); // Open in CSV read mode
int n = countln(file); // Get the number of lines
for(int i=0; i<n; i++){
string line[ ] = readln(file); // Read a line, split by commas
println(line); // Print it to the console
}
close(file); // Close the file
ReadlnCSV.vcssl
This reads back the contents of test.csv, splits each line by commas, and prints:
3 4 5
As before, you can use the constant READ_CSV instead of "rcsv".
If you'd prefer TSV instead of CSV, just use "wtsv" and "rtsv" (or WRITE_TSV / READ_TSV) instead. These modes handle numeric TSV files in the same way.
If you need to handle string values in CSV/TSV files, use the file.TextFile library instead: file.TextFile - Library Specification
Loading Libraries
(For Vnano, please refer to the documentation for the specific application you are using.)
Now that we've covered most of the language features, let's wrap up this section with a quick overview of how to work with libraries in VCSSL.
First, Create a Library
Let's start by creating a simple library file.
- ./testdir/TestLib.vcssl -
void fun( ) {
println("Hello");
}
TestLib.vcssl
include (Not Available in Vnano)
To use the library we just created, you can use the "include" directive -- just like in C or C++. If your program is in the same directory as "testdir", you can write:
include "./testdir/TestLib.vcssl" ;
fun( );
IncludeLib.vcssl
This will output:
With "include", you specify the relative path to the library file (from the current file's location) in quotation marks.
Like in C/C++, the included code is inserted directly at the location of the include statement. This means you might run into issues like duplicate identifiers or multiple includes of the same file.
Because of this, VCSSL doesn't encourage heavy use of include -- use it only when really necessary.
import (Not Available in Vnano)
To avoid the pitfalls of "include", VCSSL also supports a more robust alternative: "import". In most cases, "import" is the preferred way to bring in libraries.
import testdir.TestLib ;
fun( );
ImportLib.vcssl
This also outputs:
Here, the path is written using dot (.) separators instead of slashes (/), and it's relative to the main (executed) script's location, not the library's.
Modules and Namespaces (Not Available in Vnano)
Libraries loaded via "import" are brought in as independent modules -- they're not inlined into the code. Each such unit is called a module, and even if a module is imported multiple times, it only gets loaded once.
All modules can access each other's global scope, but they also have implicit namespaces. If multiple modules have identifiers (like variables or function names) with the same name, VCSSL will prioritize the one from the current module.
To access an identifier from a different module, you can specify its namespace:
import testdir.TestLib ;
void fun( ) {
println("World");
}
TestLib.fun( ); // Calls fun() from the TestLib module
fun( ); // Calls fun() defined in the current module
ModuleNamespace.vcssl
This will output:
World
Standard Library
Just like many other languages, VCSSL comes with a standard library containing many useful functions and features.
You can browse the full list and specifications here:
https://www.vcssl.org/en-us/lib/
In fact, we've already been using part of it throughout this guide. For example, functions like print(...) and println(..) are provided by the System library.
The "System" library is loaded automatically before any other modules, so you can use its functions without having to import it manually.
-
And with that, we've completed the first half of this guide -- the basics.
In the next section, we'll dive into some more advanced features, starting with GUI and 2D/3D graphics using the standard libraries.