How to translate code?

To translate a Citrine program from one language into another:

ctr -t en2nl.dict myprogram.ctr

Dictionaries have the following format:

t "SOURCE" "TARGET"
s "SOURCE" "TARGET"
d "SOURCE" "TARGET"
x "SOURCE" "TARGET"

Use t for tokens and s for strings. There are 2 special translation pairs: the decimal separator (d) and the thousands separator (x). To generate a dictionary from 2 versions of Citrine in different languages:

ctr -g /nl/dictionary.h /en/dictionary.h

How to use the icons?

Install 'Citrine.ttf' and configure your editor (I use Geany and Geany Macros, a macro package for Geany is included ):

cp ~/.config/geany/plugins/Geany_Macros/settings.conf ~/.config/geany/plugins/Geany_Macros/settings_backup.conf

cp macros.ini ~/.config/geany/plugins/Geany_Macros/settings.conf

>_

Use macros_single_quotes.ini for single quotes, and macros_double_quotes for double quotes. You can download the font and macro package from the Download Page (always select the latest version). The fonts and macros are also included in the source.

List of symbols:

icon code keys
2054ALT+
26CFALT.
26BFALTk
261EALT=
2190ALT+
21B2ALT,
270EALT'
2264ALT<
2265ALT>
2260ALTu
2022ALT-
×00D7ALT*
÷00F7ALT/
2009ALT; (thin space)
↵ 21B5ALTn
⇿ 21FFALTT
‘’ 2018+2019ALT "
“” 201C+201DALT "
«» 00AB+00BBALT "

How to write my own plugin?

To write a plugin for Citrine you have to generate a shared library in the /mod folder that can be dynamically loaded. For a complete example see the Percolator plugin.

First, create a .c file that contains a begin() function. In this function you can add objects to the world of Citrine, i.e. add them as public properties to the World object (CtrStdWorld):

void begin(){
ctr_internal_object_add_property(
	CtrStdWorld,
	ctr_build_string_from_cstring( "Thing" ),
	thingObject,
	CTR_CATEGORY_PUBLIC_PROPERTY);
}

Note that Citrine does not have classes, so you can only add objects to the world. Property names are also objects (Text Objects), to wrap a char* into a Text object you can use ctr_build_from_cstring( char* ... ), the inner value can be accessed through o->value.svalue->value (char*) and o->value.svalue->vlen (ctr_size). All strings in Citrine must have a length (vlen), they are NOT NUL-terminated. Create your object like this:

ctr_object* thingObject = ctr_thing_new(CtrStdObject, NULL);

always override the prototype link (necessary to find parent methods), otherwise you get an infinite loop:

thingObject->link = CtrStdObject;

Your constructor function (ctr_thing_new) may look like this:

ctr_object* ctr_thing_new(
	ctr_object* myself, ctr_argument* argumentList) {

	ctr_object* instance = 
		ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT);
	instance->link = myself;
	return instance;
}

To add a method to your object use:

ctr_internal_create_func(
	thingObject,
	ctr_build_string_from_cstring( "new" ),
	&ctr_thing_new
);

All method functions are invoked with a reference to the object itself (myself) and an argumentList object. To obtain the first object in the list use:

argumentList->object

To obtain the second object:

argumentList->next->object

and so on... To add a property to your object:

ctr_internal_object_property(
	myself, 
	ctr_build_string_from_cstring( "size" ),
	ctr_build_number_from_float(123)
);

To get a property from an object:

ctr_internal_object_property(
	myself, 
	ctr_build_string_from_cstring( "size" ),
	NULL
);

As you can see you can use ctr_build_number_from_float() to wrap a double in a Number object (access the inner value with: o->value.nvalue). Booleans are always references, so you don't need to wrap them, simply assign CtrStdBoolTrue or CtrStdBoolFalse. The same applies to Nil: CtrStdNil. Custom resources like file handles can be wrapped in objects of type OTEX (ctr_internal_create_object(CTR_OBJECT_TYPE_OTEX)), put the custom data in o->value.rvalue.

To trigger an exception use ctr_error( message, code ); where code can be errno or 0 for custom errors. To cast an object use: ctr_internal_cast2bool( ... ); ctr_internal_cast2number( ... ); ctr_internal_cast2string( ... ); For strings and numbers, to enforce the return of a copy (otherwise if the type already matches you get the same object back): ctr_internal_copy2string( ctr_object* o ); ctr_internal_copy2number( ctr_object* o );

To allocate memory use: ctr_heap_allocate(size), to free: ctr_heap_free(). All memory need to be freed upon the end of a Citrine program, otherwise you will get a warning message about a memory leak. Use ctr_heap_allocate_cstring( object ) to copy the contents of a text object to char* memory (automatically allocated). To send a message to another object:

ctr_argument* argumentList = 
	ctr_heap_allocate( sizeof( ctr_argument ) );
argumentList->object = someObject;
ctr_object* result = 
ctr_send_message(
	myObject,
	"work:",
	strlen("work:"),
	argumentList
);

To run user code (Task object), use ctr_block_run(task, argumentList, me). The last parameter is the object that acts as "myself". It is important to make sure that the garbage collector does not remove any objects that are not reachable from within the world object, if you created some temporary objects with ctr_heap_allocate, and they are not part of the world yet, set the sticky flag: object->info.sticky = 1; until the user code is done. During execution of user code the GC becomes active.

All functions you need are defined in the citrine.h header file (which you need to include). Compile and link your plugin with (before copying to /mod):

cc -I . -c thing.c -Wall -Werror -fPIC -o thing.o
cc -o libctrthing.so thing.o

To activate a plugin named "thing", just send a message to it:

☞ t ≔ Thing new.

If the reference does not exist, Citrine will automatically try to load it from the mods folder.


Test-o-Mat

The Citrine code base is tested using the Test-o-Mat framework which ships with Citrine. The Test-o-Mat system is very simple to understand and work with and can also be used to test other software. The basic idea is that you have test definitions and expectation files (.exp). After loading the test framework, override the next-message to generate a new test specification object and override the process-message of the Test Suite to describe how the test should be processed. For a detailed example you can look at test no. 365 in the test folder (tests/test0365.ctr). The Test-o-Mat framework is only available in the English language at the moment. In the example below (based on that test) we simply compare the contents of the test file with itself using a the UNIX cat-command:

Program use: Path Test-o-Mat Package.
☞ number of tests ≔ 1.

Tests path: ‘’.
☞ test suite ≔ Tests new.

test suite on: ‘next’ do: {
	☞ test ≔ Nil.
	(⚿ counter < number of tests) true: {
		test ≔ Test new
			number: ⚿ counter copy,
			name: ‘test0365.ctr’,
			file: ‘test0365.ctr’.
	}.
	⚿ counter add: 1.
	↲ test.
}.

test suite on: ‘process:’ do: { :test
	☞ actions ≔ Tests actions.
	☞ action ≔ 
			Tests action
			command: Path cat,
			target: ‘../test0365.ctr’,
			output: Path tmp result,
			log: Path tmp log.
	actions 
		append: action,
		expect: ‘../test0365.ctr’,
		run or fail: ‘OK↵’.
}.
test suite run.

How to write webapps?

Citrine ships with a CCGILIB-based HTTP plugin (mods/request/libctrrequest.so) that allows you to deal with GET and POST requests. you can use the Request object like this:

get  ≔ Request get: ‘ search’ .
list ≔ Request getArray: ‘ orders[]’ .
post ≔ Request post: ‘ message’ .
list ≔ Request postArray: ‘ orders[]’ .
file ≔ Request file: ‘ avatar’ .

Storm Server is a CCGILib based SCGI server for Citrine for hi-traffic web applications, to start an instance of Storm we put the following code in storm.ctr:

Request 
host: ‘ localhost’  
listen: 4000
pid: ‘/var/run/storm.pid’  callback: {
	✎ write: ‘Content-type: text/html\n\n’ .
	☞ fname ≔ Program setting: ‘ DOCUMENT_URI’ .
	☞ script ≔ File new: ‘ /var/www/htdocs’  + fname.
	Program use: script.
}.

NGINX example configuration /etc/nginx/nginx.conf:

location ~ \.ctr$ {
        try_files $uri $uri/ =404;
        scgi_pass 127.0.0.1:4000;
        include   scgi_params;
}

How to start the core unit tests?

As of july, Citrine will feature a new unit test system to test the core of the language. Over the next few years, we expect to extend this test suite with all sorts of useful tests to improve the quality of the Citrine code. The unit test runner can be invoked simply from the language itself, using:

Program test.

This will start the internal unit test runner. After running all the unit tests the process will be stopped automatically. There is no way to continue a Citrine program after invoking the test runner. If a single test fails, the test suite will report the error and exit. The output from the unit test runner might look like this (although this will change from release to release):


Running Internal Tests
[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][22][23][24][25][26][27]...

The unit test system is included in the regular test suite and currently resides in test file 0356. The unit test system is used to test parts of Citrine that are difficult to test from the outside like the inner workings of the Lexer, Parser, Walker, as well of specific system utilities for memory management and encoding.


Environment variables

You can use the following environment variables:

CITRINE_MEMORY_LIMIT_MB
CITRINE_MEMORY_MODE
CITRINE_MEMORY_POOL_SHARE

CITRINE_MEMORY_LIMIT_MB sets the memory limit in megabytes upfront. CITRINE_MEMORY_MODE selects the garbage collector setting (see Program tidiness). CITRINE_MEMORY_POOL_SHARE sets the share of the pool (2 means that you want 50% of all allocated memory to be devoted to the pool).


AST-Export

AST Citrine allows you to export the AST representation of any Citrine program so you can transpile or compile it to any other target language, including for example: machine code, Assembler, C, C++, Java, Lua, SQL, PHP, Perl or JavaScript.


To generate an AST-export:

ctrXX -x program.ctr

XX = your language edition

The output looks like:

52;0;0;;[57;0;3;✎;;55;0;6;write:;[56;0;5;Hello;;];];52;0;0;;[57;0;3;✎;;55;0;6;write:;[56;0;6;World!;;];];79;0;0;;;

The original program:

✎ write: ‘Hello’.
✎ write: ‘World!’.

The format for the AST-export is as follows, each node in the tree is represented as:

{TYPE} ; {MODIFIER} ; {LENGTH OF VALUE BUFFER} ; {VALUE BUFFER} ; {NESTED NODES}

Nested nodes are between [ and ].

Opcodes for nodes:

CodeType of AST-node
51Assignment
52Message expression
53Unary Message
54Binary message
55Keyword Message
56String Literal
57Object Reference
58Numeric Literal
59Code Block Literal { }
60Return Statement ↲
76Parameters
77Instructions in a Code Block
78Parameter (in the parameters)
79End of Program
80Nested Expression between (...)

Modifiers:

CodeMeaning
0Variable: x
1Property: ⚿ x
2Declaration: ☞ x ≔

To test the AST-export you can pipe it through the example program 'info' (available in the source tree in misc/tools):

ctrXX -x program.ctr | info Message Object Reference(✎) Keyword Message(write:) String Literal(Hello) Message Object Reference(✎) Keyword Message(write:) String Literal(World!) End