The following instructions assume that you have a working installation of the crab interpreter.
- Introduction
- Hello World
- Variables
- Type conversion
- Control flow
- Operators
- Functions
- Strings and lists
- Exceptions
- User input/output
- File operations
- Math
- Measuring time
crab is an interpreted toy programming language, which makes it quiet slow but still usable for simple tasks.
It has a C-like syntax but a dynamic type system, which allows variables to hold values of different types at different times.
Despite its dynamic nature crab tries to catch errors like undefined identifiers, wrong argument or return value counts or break
statements
outside of loops at parse time.
To beginn writing code in crab you first need to create a new text file.
The extension doesn't really matter, but the convention is to give your source code file the extension .cb
.
For a simple 'Hello World' program write the following code in a file called helloworld.cb
:
func main() {
println("Hello, World!");
}
To execute your code simply call the crab
interpreter and give it your source file as an argument:
crab helloworld.cb
You should see the following output:
Hello, World!
The entry point to all crab programs is the main()
function. It never returns anything and takes no arguments.
The only thing that can be different for its signature is the optional throws keyword.
The main()
function calls the builtin println()
function, an alternative to the print()
function,
which prints its arguments to stdout
and appends a newline character.
In general all statements in crab have to end with a ;
similar to other C-like languages.
To define a variable in crab simply use the var
keyword:
var x = 5;
var y; // equivalent to 'var y = null;'
Omit the var
keyword when you want to assign a value to an existing variable:
var x = 5;
// because 'crab' has dynamic typing, the following works:
x = "Hello World!";
crab has global and local scope and supports variable shadowing:
// variable x in global scope
var x = 5;
func main() {
println(x); // 5
// variable x in local scope
var x = 7;
println(x); // 7
// nested scope
{
x = 9;
var x = 10;
println(x); // 10
}
println(x); // 9
}
Sometimes you need to convert between types, for example when you want to receive numbers from the user.
There are 3 functions for exactly this purpose: toString()
, toNumber()
and toBoolean()
.
toString()
takes a value and converts it to a string. This action will always succeed.
Example:
var x = 5;
x = toString(x); // x is now a string
toNumber()
takes a string value and tries to convert it to a number. If this action doesn't succeed, toNumber()
will throw an exception.
toNumber()
accepts string representations of integers and floating point numbers.
Example:
var x = "5";
try {
x = toNumber(x); // x is now a number
} catch {
// couldn't convert x to a number
}
toBoolean()
takes a string value and tries to convert it to a boolean. If this action doesn't succeed, toBoolean()
will throw an exception.
toBoolean()
accepts "1", "t", "T", "TRUE", "true", "True", "0", "f", "F", "FALSE", "false" and "False".
Example:
var x = "true";
try {
x = toBoolean(x); // x is now a boolean
} catch {
// couldn't convert x to a boolean
}
crab supports the 3 most common control flow constructs:
if (condition) {
// do something
} else if (condition2) {
// do something else
} else {
// do something completely different
}
while (condition) {
// do something as long as 'condition' is true
}
for (var i = 0; i < 10; i++) {
println(i);
}
crab the break
and continue
statements in loops.
break
: exit the loopcontinue
: skip the rest of the current iteration and start over again
symbol | name | description |
---|---|---|
() | parentheses | grouping or function call |
[] | brackets | list subscript |
++ | increment | increments the variable by 1 |
-- | decrement | decrements the variable by 1 |
! | bang | negates the logical value after it |
- | unary minus | multiplies the value after it with -1 |
** | exponentiation | returns the result of raising the first operand to the power of the second operand |
+ | addition | adds two values together |
- | subtraction | subtracts two values from another |
* | multiplication | multiplies two values with each other |
/ | division | divides a value by another value |
% | modulus | take the modulus of two values |
< | less | returns true if the left operand is less than the right one |
> | greater | returns true if the left operand is greater than the right one |
<= | less or equal | returns true if the left operand is less than or equal to the right one |
>= | greater or equal | returns true if the left operand is greater than or equal to the right one |
== | equal | returns true if both operands are equal |
!= | not equal | returns true if both operands are not equal |
&& | logical AND | returns true if both operands are true |
^^ | logical XOR | returns true if exactly one of the operands is true |
?: | ternary conditional | returns either the left or right result depending on the condition |
= | assignment | assigns the right value to the left operand |
+= | assignment | adds and assigns the right value to the left operand |
-= | assignment | subtracts and assigns the right value from the left operand |
*= | assignment | multiplies and assigns the right value with the left operand |
**= | assignment | assigns the the left operand raised to the power of the right operand to the left operand |
/= | assignment | divides and assigns the right value from the left operand |
%= | assignment | takes the modulus and assigns the right value to the left operand |
In its most simple form a function can be declared and called as follows:
func test() {
println("test");
}
func main() {
test();
}
If you want to return values from your function, you will need to tell crab about the number of values you want to return:
// 0 return values
func test() {
return;
}
// 1 return value
func test2() 1 {
return "some string";
}
// 2 return values
func test3() 2 {
return "some string", 42;
}
func main() {
test();
var a = test2(); // a = "some string"
var b, c = test3(); // b = "some string", c = 42
}
crab supports at most 4 return values.
Functions can take arguments by specifying a parameter list between the parantheses:
func add(a, b) 1 {
return a + b;
}
func main() {
println(add(1, 2)); // 3
}
Functions can be declared inside of other functions with closure support:
func returnAFunction(text) 1 {
func function() {
println(text);
}
return function;
}
func main() {
var fn = returnAFunction("Hello, World!");
fn(); // Hello, World!
}
crab also allows defining anonymous functions. With anonymous functions the previous example could be rewritten as:
func returnAFunction(text) 1 {
return func(){
println(text);
};
}
func main() {
var fn = returnAFunction("Hello, World!");
fn(); // Hello, World!
}
Lists in crab can hold values of different types and can be dynamically resized:
var emptyList = [];
var list = [1, 2, "Hello, World!", [true, false]];
var bigList = createList(1000); // Create a list with 1000 null entries
println(list[3]); // Hello, World!
append(list, "new value");
println(list); // [1,2,Hello, World!,[true,false],new value]
remove(list, 3);
println(list); // [1,2,[true,false],new value]
println(len(list)); // 4
concat(list, [3, 4, 5]);
println(list); // [1,2,[true,false],new value,3,4,5]
You can work with strings similarly as with lists. The only difference is assignment. Due to the immutable nature of strings, you cannot assign a new character at a specified index.
var helloworld = "Hello, World!";
println(helloworld[4]); // o
helloworld[4] = "y"; // error!
\n
: new line\r
: carriage return\t
: horizontal tab\"
: double quotation mark\\
: backslash\e
: ASCII escape character
There are a number of builtin utility functions that make working with lists and strings a lot easier:
// will convert all arguments to strings prior to processing
toLower("HelLo, WoRLd!"); // "hello, world!"
toUpper("HelLo, WoRLd!"); // "HELLO, WORLD!"
trim(" \t Hello, World! \t "); // "Hello, World!"
// work with both strings and lists
contains("Hello, World!", "!"); // true
contains([1,2,4,5], [2]); // true
indexOf("Hello, World!", "W"); // 7
indexOf([1,2,3,4], [5]); // -1
replace("Hello, World!", "l", "n"); // "Henno, Wornd!"
replace([1,2,3,4], 2, 1); // [1,1,3,4]
split("Hello, World!", ","); // ["Hello", " World!"]
split([1,2,3,4,5], 3); // [[1,2],[4,5]]
// only work with lists
join([1,"hello",false], "-"); // "1-hello-false"
Error handling in crab is done through exceptions.
An exception can be thrown using the throw
keyword:
func testfunction() throws {
throw "some value";
}
IMPORTANT: All exceptions must be explicitly handled by either adding throws
to the function signature (after the return value count)
or by wrapping the problematic code in a try...catch
block.
func testfunction() throws {
throw "some value";
}
func main() {
try {
testfunction();
} catch (e) { // the '(e)' can be omitted, if you don't need the value of the exception
println(e); // some value
}
}
There are two functions for printing text to stdout
: print()
and println()
.
They only differ in the way that println()
appends a newline character while print()
does not.
You can supply multiple arguments to the print
functions in order to print them separated by a space.
Example:
println("Hello", "World", 5, true); // Hello World 5 true
You can request input from the user via stdin
with the input()
function:
var name = input("What is your name? "); // input always returns a string
Most file operations can throw exceptions.
Returns true if the specified file exists.
if (fileExist("filepath")) {
// exists!
}
Returns the full content of the specified file.
var content = readFileText("filepath");
Creates parent directories, if they do not already exists and overrides the file with the provided content.
writeFileText("filepath", "content");
Appends the provided content to the specified file.
appendFileText("filepath", "content");
Deletes the specified file/empty directory.
deleteFile("filepath");
Returns a list of all file names in the specified directory.
var files = listFiles("directory");
min(2.5, 3.3); // 2.5
max(2.5, 3.3); // 3.3
floor(2.5); // 2
ceil(2.5); // 2
round(2.5); // 3
round(2.4); // 2
sqrt(9); // 3
Generate a random floating point number between a (inclusive) and b (exclusive):
var a = 0;
var b = 100;
var num = random(a, b);
Generate a random integer between a (inclusive) and b (exclusive):
var a = 0;
var b = 100;
var num = randomInt(a, b);
You can get the current unix time in milliseconds with the millis()
function:
var now = millis();