Skip to content

Latest commit

 

History

History
547 lines (395 loc) · 12.9 KB

DOCUMENTATION.md

File metadata and controls

547 lines (395 loc) · 12.9 KB

Documentation

The following instructions assume that you have a working installation of the crab interpreter.

Table of Contents

Introduction

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.

Hello World

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!

Explanation

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.

Variables

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
}

Type conversion

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()

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()

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()

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
}

Control flow

crab supports the 3 most common control flow constructs:

If-statement

if (condition) {
	// do something
} else if (condition2) {
	// do something else
} else {
	// do something completely different
}

While-loop

while (condition) {
	// do something as long as 'condition' is true
}

For-loop

for (var i = 0; i < 10; i++) {
	println(i);
}

Break and continue

crab the break and continue statements in loops.

  • break: exit the loop
  • continue: skip the rest of the current iteration and start over again

Operators

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

Functions

In its most simple form a function can be declared and called as follows:

func test() {
	println("test");
}

func main() {
	test();
}

Return

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.

Parameters

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
}

Nested functions

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!
}

Anonymous functions

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!
}

Strings and lists

Lists

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]

Strings

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!

Supported escape sequences

  • \n: new line
  • \r: carriage return
  • \t: horizontal tab
  • \": double quotation mark
  • \\: backslash
  • \e: ASCII escape character

Utility functions

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"

Exceptions

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.

try...catch

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
	}
}

User input/output

Output

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

Input

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

File operations

Most file operations can throw exceptions.

Check if a file exists

Returns true if the specified file exists.

if (fileExist("filepath")) {
	// exists!
}

Read

Returns the full content of the specified file.

var content = readFileText("filepath");

Write

Creates parent directories, if they do not already exists and overrides the file with the provided content.

writeFileText("filepath", "content");

Append to an existing file

Appends the provided content to the specified file.

appendFileText("filepath", "content");

Delete

Deletes the specified file/empty directory.

deleteFile("filepath");

List all files in a directory

Returns a list of all file names in the specified directory.

var files = listFiles("directory");

Math

Min/max

min(2.5, 3.3); // 2.5
max(2.5, 3.3); // 3.3

Floor/ceil/round

floor(2.5); // 2
ceil(2.5); // 2

round(2.5); // 3
round(2.4); // 2

Square root

sqrt(9); // 3

Random number

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);

Measuring time

You can get the current unix time in milliseconds with the millis() function:

var now = millis();