Home

Jacob Torrey edited this page May 15, 2015 · 1 revision
Clone this wiki locally

Getting Started

New to the Crema language? Start here! This wiki contains code samples, reference material, and general Crema topics to help you get started writing your own Crema programs.

Hello, Crema!

Lets start by writing a simple program with Crema. Before proceeding, make sure that you have built the Crema compiler using the instructions on the Crema repo page.

First, create a directory named "programs" to house your Crema code. This directory can be located anywhere, but we will place it inside the Crema base directory for this example. Inside this directory, create a new file named hello.crema. Your directory structure should appear as follows:

  • crema
    • docs
    • programs
      • hello.crema
    • src
      • cremacc
      • stdlib
      • (etc.)
    • tests
    • vagrant

Next, open hello.crema with your favorite text editor, type the following, and then save the file:

str_println("Hello, Crema!")

Next, compile your program by navigating to crema/src, and running the command:

./cremacc -f ../programs/hello.crema -o hello

Finally, run your program using the command ./hello. You should see the message "Hello, Crema!" printed to your command prompt.

Using Crema from the Command Line

You can also pass Crema code directly to cremacc from the command line by omitting the -f option. Try recreating the hello program using the command line only:

  1. Run cremacc without the -f option: ./cremacc -o hello

  2. Type the following directly into the command prompt: str_println("Hello, Crema!")

  3. Hit ENTER

  4. Finish passing code to cremacc by typing Ctrl+d

  5. Run the generated program: ./hello

Next Steps

After you have successfully run the "Hello, Crema" programs, you should become aquainted with Crema's features by reading on to the section Crema Syntax. Once you are ready to start writing your own Crema programs, check out the section Crema Standard Library to learn about all of the built-in functions that Crema has to offer. If you are more inclined to learn Crema by example, check out the code samples near the end of this wiki.

Crema Syntax

Crema is designed in the popular procedural paradigm, following a number of C styles while adopting some newer features from more modern languages like Python and Ruby. Crema code is written without the use of semicolons or end-of-line characters. White space is completely ignored by the compiler.

Comments

You can make comments in a Crema program using the # symbol:

# This comment will be ignored by the compiler

Variables and Data types

Crema uses the following data types for variables and/or return values

Type Description
int A 64-bit signed integer value
double A 64-bit signed floating-point decimal value
char A single character
string A string of characters
bool A boolean value, "true" or "false"
struct A C-like structure
void Return type for functions that do not return a value

Variables can be declared anywhere in a Crema file like this:

int x

Or defined with a value like this:

bool b = true

Crema is a statically typed language, meaning that you are not permitted to change the data type of a variable at run time:

int number = 10

number = 12
number = "String"	# <-- Error

Numbers

There are two data types in Crema that represent numbers: int and double

int x = 2
double d = 5.0

Crema automatically casts values from an int to a double for you:

double d = 2 	# <-- d == 2.0

Strings and Characters

Crema strings can be defined using double-quotes around a string literal:

string str = "Hello, World"

You can print strings using the standard lib str_print() and str_println() functions:

string str1 = "Hello, "
string str2 = "World"
str_print(str1)
str_println(str2)

# Outputs: "Hello, World" (str_print() doesn't trigger a new line)

Characters can be defined with single quotes

char c = 'a'

Currently, Crema does not support escape characters or formatted printing. For example, the following will print the given string literal exactly as it is written:

str_println("escape \n characters \t\t not supported")	# <-- This will print literally

Boolean Values

Crema uses the lower-case keywords true and false to define boolean values

boolean b1 = true
boolean b2 = false

Structures

Structures can be defined to build more complex data types:

# Declare a new person structure
struct person{
	string name,
	int age
}

# Create a person structure
struct person p1
p1.name = John
p1.age = 26

p1.age = p1.age + 10

Arrays

Crema arrays can be defined for the following data types...

int array1[] = [1, 2, 3, 4, 5]
char array2[] = ['a', 'l', 'l']
double array3[] = [1.0, 1.2, 1.3]
string array4[] = ["bob", "alice"]
bool array5[] = [true, false]

# ...

... and you can access elements of an array like this:

# ...

int x = array1[0]	# <-- x == 1
array2[2] = 'e'		# <-- array2 == ['a', 'l', 'e']

Operators

Crema supports the following mathematical operators for numerical data types:

int x = 2
int y = 3
int result

result = x + y 		# result == 5
result = x - y  	# result == -1
result = x * y 		# result == 6
result = y / x 		# result == 1
result = y % x 		# result == 1
result = x | y 		# result == 3
result = x & y  	# result == 2
result = x ^ y 		# result == 1

Crema supports the following logical operators:

bool b1 = true
bool b2 = false
int x = 2
int y = 3
bool result

result = b1 && b2	# result == false
result = b1 || b2	# result == true
result = x == y 	# result == false
result = x != y 	# result == true
result = x > y 		# result == false
result = x < y 		# result == true
result = x >= y 	# result == false
result = x <= y 	# result == true

The following common operators are currently NOT supported by Crema:

x++
x--
~x
x >> 1
x << 1
(x == 5) ? 1 : 10

Conditional Statements

Crema supports standard if, else if, and else statements for conditional processing

int a = 1

if(a > 0){
	str_println("a > 0")
}else if(a == 0){
	str_println("a == 0")
}else{
	str_println("a < 0")
}

The common switch expression is currently NOT supported

Loops

The only type of loop supported by Crema is the foreach loop. This loop is used to sequentially iterate over each element of a list. Crema specifically does not support other common loops (such as while or for loops) so that Crema programs are forced to operate in sub-Turing Complete space. In other words, foreach loops always have a defined upper bound on the number of iterations possible during the loop's execution. This makes it impossible to write a loop that could execute for an arbitrary length of time.

char letters[] = ['C', 'r', 'e', 'm', 'a', ' ', 'R', 'o', 'c', 'k', 's', '!']

# print the letters (Crema Rocks!)
foreach(letters as i){
	str_print(i)
}

Functions

Functions in Crema can be defined using the syntax: def (){}

All functions must have either a return type, or specify void if no value is returned. For example:

def int foo(){
	return 0;
}

def void bar(){
	
}

You can pass parameters to functions like this:

def int add(int a, int b){
	return a + b
}

def void printString(string str){
	str_println(str)
}

# ...

... And call a function like this:

# ...

int x = add(3, 2)

printString("Hello, World")

Including Crema Files

You can include code from external .crema files into your program with the include statement. This statement will take the contents of a specified file and insert it directly into a Crema program at the same line. For example, suppose you have two Crema files as follows:

foo.crema

str_print("Foo ")

bar.crema

str_println("Bar")

Then, the following Crema programs are identical, and will both print "Foo Bar":

include foo.crema
include bar.crema
str_print("Foo ")
str_println("Bar")

Variable Scope

Variables in Crema follow C-like lexical scope rules. Variables can be defined globally (accessible by all Crema functions) or locally within a function. Variables defined locally within a function take precedence over global variables that have the same name.

int x = 5		# <-- Global Variable
int y

def int foo(){
	int x = 10	# <-- Local Variable
	return x	# <-- Local variable x equals 10, x in global scope is unchanged
}

def int bar(){
	return x	# <-- Inherits x from global scope
}

y = foo()		# <-- y == 10
y = bar()		# <-- y == 5

Functions in Crema may only be defined in global scope. Nested functions definitions are not permitted:

def void foo(){			# <-- Good
	str_println("This is how to define a function in Crema")
}

def void bar(){
	def void foo2(){	# <-- Bad. This is not valid in Crema
		str_println("This nested function will cause a complier error")
	}
}

Crema Standard Library

Crema supports a small standard library of functions that cover a variety of basic programming tasks. You can call any of the functions from this library in your Crema programs.

I/O

Function Return Type Description
str_print(string s) void Prints a string to stdout
str_println(string s) void Prints a string to stdout terminated with a new-line character
int_print(int i) void Prints an int to stdout
int_println(int i) void Prints an int to stdout terminated with a new-line character
double_print(double d) void Prints a double to stdout
double_println(double val) void Prints a double to stdout terminated with a new-line character
prog_arg_count() int Returns the number of command line arguments passed to the running Crema program
prog_argument(int i) string Returns the string representation of a command line argument at a given postion i from the command line arguments list passed to the running Crema program

Type Conversion

Function Return Type Description
double_to_int(double d) int Casts the given double value as an int
int_to_double(int i) double Casts the given int value as an double
string_to_int(string str) int Parses given string to an int value, or returns 0 if the string cannot be parsed as an int
string_to_double(string str) double Parses given string to a double value, or returns 0 if the string cannot be parsed as a double

Math

Function Return Type Description
double_floor(double d) double Returns the nearest integral value less than the given number
double_ceiling(double d) double Returns the nearest integral value greater than the given number
double_truncate(double d) double Truncates the given double value
double_round(double d) double Rounds a double to the nearest integer value
double_square(double d) double Returns the square of the given double value
int_square(int i) int Returns the square of the given int value
double_pow(double base, double power) double Returns the double value of base raised to the power power
int_pow(int base, int power) int Returns the integer value of base raised to the power power
double_sqrt(double val) double Returns the square root of the given double value
double_abs(double val) double Returns the absolute value of the given double value
int_abs(int val) int Returns the absolute value of the given int value
double_sin(double d) double Returns sin of the given double value
double_cos(double d) double Return cos of the given double value
double_tan(double d) double Returns tan of the given double value

Crema Sample Programs

FizBuz

The following demonstrates the classic "FizBuz" printing function. This program defines a single function called fizBuz that accepts one int argument passed in from the command line. If the argument is divisible by 3, "Fiz" gets printed. If the argument is divisible by 5, "Buz" gets printed. If the argument is divisible by both 3 and 5, "FizBuz" gets printed.

def void fizBuz(int a){
	if(a%3 == 0){
		str_print("Fiz")
	}
	if(a%5 == 0){
		str_print("Buz")
	}
	str_println("")
}

int argc = prog_arg_count()
if(argc < 2){
	str_println("Usage: Type a number to be fiz'ed and/or buz'ed")
}else{
	int input = string_to_int(prog_argument(1))
	if(input <= 0){
		str_println("Please type a valid number greater than 0")
	}else{
		fizBuz(input)
	}
}

Bubble Sort

The following demonstrates a Crema implementation of the classic bubble sort algorithm. The program creates an unsorted list of integers, and then prints the list after each iteration of the sorting algorithm completes. Since Crema only allows the use of a foreach loop, note the use of iterator variables in the global scope of this program. With Crema, you must manually track the position of an element in an array during execution of a foreach loop. Also notice the use of extra variables to access elements in an array by an offset. With Crema, you must explicitly access elements of an array with a fixed value, and not with an expression (i.e. array[2] is legal, array[2+1] is not).

NOTE: This program will not work correctly until an open bug related to nested loops is fixed.

int a = 0
int b = 0
int i = 0
int j = 0
int values[] = [6, 3, 8, 7, 2, 1, 4, 9, 0, 5]
int upperBound = 9 # <-- length of values - 1

foreach(values as itteration)
{
	str_println("itteration started")
	foreach(values as element)
	{
		if(i < upperBound)
		{
			j = i + 1
			a = values[i]
			b = values[j]
			if(a > b)
			{
				values[i] = b
				values[j] = a
			}
		}
		int_print(values[i])
		i = i + 1
	}
	str_println("")
	upperBound = upperBound - 1
	i = 0
}

Binary Search

The following demonstrates a Crema implementation of a binary search algorithm.

def int binarySearch(int values[], int searchTarget){
	int upperBound = list_length(values) - 1	# Upper index of seach region
	int lowerBound = 0						 # Lower index of seach region
	int delta = list_length(values)		# Distance between upperBound and lowerBound
	int middleValueIndex = 0	# Mid-point index between upper and lower bounds
	int middleValue = 0			# Value at the mid-point index
	int foundIndex = -1			# The index of the target number after finding

	foreach(values as value){
		# Check middle value to see if it matches target number
		middleValueIndex = ((upperBound + lowerBound) / 2)
		middleValue = values[middleValueIndex]
		if(middleValue == searchTarget){
			foundIndex = middleValueIndex
			break
		}

		#Re-adjust the lower and upper bounds for next itteration
		if(middleValue >= searchTarget){
			upperBound = middleValueIndex - 1
		}else{
			lowerBound = middleValueIndex + 1
		}
		delta = upperBound - lowerBound
	}
	return foundIndex
}

*This research was developed with funding from the Defense Advanced Research Projects Agency (DARPA). Distribution Statement "A" (Approved for Public Release, Distribution Unlimited)