SubScript v0.2 - Language definition
About
SubScript is a dynamic scripting language that aims at quick
and flexible scripting.
The name is based upon the fact that (sub)scripts can be defined
within scripts and stored in variables for later execution.
It is also possible to compound scripts on the fly.
The current interpreter is stack based and executes scripts directly.
Language characteristics
The list below shows a few (unordered) characteristics of the SubScript language:
* Functions do not have a fixed argument count.
* No return keyword, inserting a value is enough.
* Scripts can be build dynamically during execution.
* No garbage collection, objects have to be freed manually.
* The last statement does not require a statement separator.
Basic elements
Comment
SubScript only knows single line comments by using the comment sign #.
# This is comment
Calculation operators
SubScript can be used as a calculator as follows.
54 + 2 * (3 + 6);
This will result in:
72
Variables
Variables are automatically declared at first use. To declare a variable the assignment operator ''':''' has to be used. Subscript is a weak-typed scripting language so types are automatically set. Please note that a value has to be assigned directly when declaring a variable.
# Define Text variable
name: "John";
# Define Number variable
age: 44;
# Define Boolean variable
bald: true;
# Define Array variable
hobbies: ["golf", "tennis", "boxing"];
# Alter variable
age: 45;
age: + 5; # Adds 5 to age (becomes 50)
# Use of(print) variables
printl(name);
printl(age);
This will output:
John
50
Defines
Defines work the same way as variables, but its value can not change after declaration. Trying to change the value results in an exception.
# Define a value
define PI: 3.14159265;
# Use the definition
printl(2 * PI * 5);
This will output:
31.4159265
Types
Working with Numbers
# Real numbers
number: 3.14;
number: * 2 * 5;
# Integer numbers
number: 125;
number: 0x7D; # 125 in hexadecimal
number: 0b01111101; # 125 in binary
a: 0b1010;
b: 0b0101;
c: a | b; # a OR b, results in 0b1111
c = 0b1111;
This will result in:
true
Working with Text
# Define a piece of text
text: "Hello";
printl(text); # Prints "Hello"
# Since text is a list, we can do the following
printl(text[2]); # Prints: 'l'
# And even this...
foreach(char: text)
{
# prints every character on a separate line
printl(char);
}
# Concatenation is easy
text[]: " World!";
printl(text); # Prints: "Hello World!"
# This works as well...
days: 356;
printl(text + " A year has " + days + " days");
This will output:
Hello
l
H
e
l
l
o
Hello World!
Hello World! A year has 356 days
Work with Arrays
# Define an array
even: [2, 4, 6];
# Show second item
printl(even[1]);
# Add an item
even[]: 9;
# Alter the fourth item
even[3]: 8;
# Show last item
# Negative indexes start at the end of the array
printl(even[-1]);
# Define new array
uneven: [1, 3, 5, 7, 9];
# Merge them into a new array (just add the arrays)
numbers: even + uneven;
# remove the first and last item (just substract the arrays)
numbers: numbers - [numbers[0], numbers[-1]];
# Show all items
foreach(item: numbers)
{
printl(item);
}
# Multiple dimensions
array: [12, "pigs", [true, 42], numbers];
# Print the second value of the array on the third position
printl(array[2, 1]); # Prints 42
# Rember that we removed the last value from numbers
printl(array[-1, -2]); # Prints 5
This will output:
4
8
4
6
8
1
3
5
7
42
5
Working with Booleans
# Single boolean values
printl(true);
printl(false);
# And now with the NOT operator
printl( !true );
# Composed boolean statements
printl(true & true);
printl(true | false);
printl(false & true);
printl(false & false);
# A total picture
printl( !(false | (true & false)) );
This will output:
true
false
false
true
true
false
false
true
Working with Scrips
# Create a simple script and store it into a variable
script: { 8 * 8 };
# And its possible to execute them
result: execute(script);
printl(result);
newScript: script + { * 8 }; # This creates the script { 8 * 8 * 8 }
printl(execute(newScript));
This will output:
64
512
Flow control
Selection
If, else if, else
a: 3;
b: 3;
if(a < b)
{
printl("A is smaller then B");
}
else if(a > b)
{
printl("A is bigger then B");
}
else
{
printl("A is equal to B");
}
This will output:
A is equal to B
Switch
drink: "jillz";
switch(drink)
{
"wiskey", "beer"
{
printl("Are you a man?");
}
"rose",
"wine",
"jillz"
{
printl("Are you a woman?");
}
other
{
printl("What are you?");
}
}
This will output:
Are you a woman?
Iteration
For loop (depricated!)
# Please note that 'i' is the index variable
# format arguments (start point, end point, step size)
for i: (0, 5, 1)
{
printl(i);
}
# Reverse loop
# Please not that the step size is always positive
for i: (6, 0, 2)
{
printl(i);
}
Foreach loop
array: ["Hello", "World"];
# Print all items from an array
foreach(item: array)
{
printl(item);
}
text: "Hello World";
# Print all characters from a text
foreach(char: text)
{
printl(char);
}
# Print range of numbers (replaces for-loop!)
foreach(number: range(3, 5))
{
printl(number);
}
This will output:
Hello
World
H
e
l
l
o
W
o
r
l
d
3
4
5
While loop
index: 0;
while(index < 5)
{
printl(index);
inc(index);
}
This will output:
0
1
2
3
4
Do loop
index: 0;
do
{
printl(index);
inc(index);
} (index < 5);
This will output:
0
1
2
3
4
Breaking out
numbers: [32, 79, 34, 93];
find: 34;
index: 0;
foreach(i: range(0, length(numbers) - 1))
{
if(numbers[i] = find)
{
index: i;
break;
}
}
printl(index);
This will output:
2
Functions
Creation and usage
# Define a simple function
function hello()
{
# Values that are not used as input or stored into
# a variable will be returned
"Hello World";
}
# Use the function (it will print the text "Hello World")
printl( hello() );
# Define a function with arguments
function random(min, max)
{
# Make new number
min + round(rand() * (max - min));
}
# Call the function
number: random(1, 10);
printl(number);
Flexible arguments
function bar()
{
# This function has no argument restriction
printl("Argument count: " + length(args));
foreach(arg: args)
{
printl(arg);
}
}
bar("Hello", "World");
function foo(a, b)
{
# This function requires at least two arguments
printl("a = " + args[0]);
printl("b = " + args[1]);
}
foo(12, 15);
Recursion
function factorial(n)
{
if(n > 1)
{
n: * factorial(n - 1);
}
n;
}
printl(factorial(10));
Classes and objects
Creation and usage
# Define a class
class Animal
{
# Define attributes (always public)
type: "unknown";
name: "unknown";
# Constructor
function init(type, name)
{
this.type: type;
this.name: name;
}
function getName()
{
name;
}
}
# Array of pet objects
pets:
[
# Create objects
Animal("Bird", "Tweety"),
Animal("Fish", "Nemo"),
Animal("Dog", "Lassie")
];
# Print all pets
foreach(pet: pets)
{
printl(pet.type + ": " + pet.getName());
}
Object relationship
class Item
{
# Textual name of the item
name: "";
# Constructor
function init(name)
{
this.name: name;
}
# Return the name of the item
function getName()
{
name;
}
}
class Storage
{
# Array of item objects (starts empty)
items: [];
# Adds an item to the array
function addItem(item)
{
items[]: item;
}
# Returns the name of the last item
function getLastItemName()
{
# Retrieve the last item object and return its name
items[-1].getName();
}
}
# Create storage object
storage: Storage();
# Add items to the storage
storage.addItem(Item("Box"));
storage.addItem(Item("Shoe"));
storage.addItem(Item("Banana"));
# Show the name of the last item added
storage.getLastItemName();
Single Inheritance
class A
{
foo: "bar";
}
class B (A)
{
bar: "foo";
}
b: B();
b.foo + " " + b.bar;
Multiple Inheritance
class A
{
a: "AAA";
}
class B
{
b: "BBB";
}
class C (A, B)
{
c: "CCC";
}
c: C();
printl(c.a + c.b + c.c);
Overriding
class Item
{
name: "";
weight: 0;
# Constructor
function init(name, weight)
{
this.name: name;
this.weight: weight;
}
}
class Product (Item)
{
quantity: 0;
# Override the parents constructor
function init(name, weight, quantity)
{
this.name: name;
this.weight: weight;
this.quantity: quantity;
}
}
item: Item("Shoe", 12);
product: Product("Drill", 45, 3);
Namespaces
Creation and usage
# Define a namespace
namespace math
{
define PI: 3.14159265;
function random(min, max)
{
min + round(rand() * (max - min));
}
}
# Call function
number: math.random(2, 8);
diameter: 8;
perimeter: round(diameter * math.PI);
printl(number);
printl(perimeter);
Root elements
namespace space
{
count: 15;
}
count: 12;
printl(count + space.count);
Global
namespace space
{
global define COUNT: 14;
global opened: false;
global function done()
{
printl("Done");
}
}
printl(COUNT);
done();
Exception handling
Catch exceptions
try
{
printl(unknownVariable);
}
catch(e)
{
printl("Message: " + e.message);
printl("Line: " + e.line);
printl("Position: " + e.position);
}
Throw exceptions
try
{
exception("Nothing to do!");
}
catch(e)
{
printl("Oops... " + e.message);
}
Including scripts
include("external_script.subs");
externalFunction();
Extending scopes
# Define class
class Storage
{
items: [];
function addItem(item)
{
items[]: item;
}
}
# Create and test object
storage: Storage();
storage.addItem(12);
# Open the scope of the storage object
use(storage)
{
# Call internal functions
addItem(13);
addItem(14);
# Add a function
function printItems()
{
foreach(item: items)
{
printl(item);
}
}
}
# Call the added fuction
storage.printItems();
Script weaving
Simple weaving
include("logger.subs");
# Define script for logging exceptions
# Scripts can be inserted (weaved) on multiple points for optimal reuse
# This is more efficient then calling a function within a separate catch script
logException:
{
Logger.log("Error: " + e.message + " on line " + e.line + ", position " + e.position);
};
try
{
12 + integer("14");
}
catch(e) logException; # Weave script
try
{
12 + integer(true);
}
catch(e) logException; # Weave script again
Weaving extended scripts
include("logger.subs");
# Define script for logging exceptions
logException:
{
exceptionMessage: "Error: " + e.message + " on line " + e.line + ", position " + e.position;
Logger.log(exceptionMessage);
};
# Extend the log script with a print function
# Extended scripts form a single scope, so the exceptionMessage variable is available
logAndPrintException: logException +
{
printl(exceptionMessage);
}
try
{
12 + integer("14");
}
catch(e) logException; # Weave log only script
try
{
12 + integer(true);
}
catch(e) logAndPrintException; # Weave log and print script
List comprehension
Since list comprehension is not more than syntactic sugar, it is not (yet) implemented in the language.
In case of need it is easy to simulate this functionality by using the power of sub scripts.
function listComp(output, input, predicate)
{
# Create list to store the result
result: [];
# Write the correct code that is parsable
# Please note that the operation is nested into
# the comprehension (instead of concatenated)
operation: { result[]: } + output;
comprehension: { if( } + predicate + { ) operation; };
# Create new list
foreach(x: input) comprehension;
# Return the result
result;
}
# Lets test it with the following comprehension:
# S = { x * x | [1..100], x rem 3 = 0 }
list: listComp({x * x}, range(1, 100), {x % 3 = 0});
# Show results
foreach(item: list)
{
printl(item);
}
Please note that the listComp function rewrites the comprehension (in this specific case) as follows.
foreach(x: input)
{
if(x % 3 = 0)
{
result[]: x * x;
}
}
The same function can be used with arrays that have more then one dimension.
define NAME: 0;
define GRADE: 1;
grades:
[
["Jim", 1.6],
["John", 7.8],
["Mary", 5.2],
["Jeff", 5.5],
["Ann", 6.7],
["Wendy", 3.8]
];
passed: listComp({ [x[NAME], round(x[GRADE])] }, grades, { x[GRADE] > 5.4 });
printl("" + length(passed) + " students passed:");
foreach(result: passed)
{
printl(" - " + result[NAME] + ": " + result[GRADE]);
}