Citrine programming language
this planet deserves a better class of language



The Citrine Project aims to produce a simple, general purpose scripting language for UNIX®-like operating systems. I created Citrine because I want a programming language that anybody can understand, read, write and love. Citrine mimicks natural language through message-based object orientation, can be translated easily (you can code in French or Chinese), has very few grammar rules and strong built-in security. It's an attempt to create better software starting at the language level.

Citrine is a product of Gabor Software (Dutch) founded by Gabor de Mooij. Citrine is open source and licensed BSD, you can also buy a commercial license (these include support and access to closed source plugins when available).

Latest Citrine News

3 june 2017 - New release 0.7.2
1 june 2017 - New release 0.7.1 for 64bit OpenBSD and 64bit Linux

Citrine Downloads

Citrine 0.7.2 for OpenBSD AMD64 (TARGZ) (Changelog)
Citrine 0.7.2 for Linux 64 bit (TARGZ)
Plugin: JSON (by Jake Russo)
Plugin: CURL (by Jake Russo)

Citrine Website Menu

Citrine Latest Source Code (github)
Travis-CI Test Report
Discuss on Google Groups
Citrine Online Playrground (try Citrine online)?
Reference Manual
Roadmap

What does it look like?

A slightly over-engineered 'hello world' program:

Butler := Object new.
Butler on: 'greet:' do: { :n
 Pen write: 'Welcome ' + n.
}.
james := Butler new.
james greet: 'visitor'.

Run the program:

$ ctr hello.ctr

This program will generate the following output:

Welcome visitor

Obviously this code could have been written much simpler, but that would reveal less of the language.

Features

Citrine combines lots of ideas from various programming languages like Ruby, Smalltalk, JavaScript and C.


Why another programming language?

The purpose of the Citrine Project is to build a 'next generation' programming language. Software is eating the world and as a society we depend on that software a lot. It does not make sense to rely on a very tiny community of developers to cater all our code needs. Instead, Citrine aims to pave the way for everyone to learn to code, thus keeping our technology sustainable. Citrine aims to bring programming to everyone by:

Citrine is a simple, tiny, dynamic scripting language with classless, prototypal Smalltalk-style messaging for everyday scripting, for everyone.

The basics

In Citrine, everything is an object. You write a program by making these objects talk to each other, by sending messages.

There are 5 literal objects:

Strings begin and end with a single quote (').

This is what a comment looks like:

# this a comment
# it starts with #.

Messages

There are three kinds of messages. A unary message is a message without any arguments:

3 factorial.

Here we send the message 'factorial' to 3, this expression will return 6 ( 3 * 2 * 1 ). Note that each line of code should end with a period (.) . You can chain messages like this:

3 factorial factorial.

This line will first send the message 'factorial' to the number 3. Then, it will send the message 'factorial' to the result of the previous operation (6), the final result will be 720.

A binary message uses infix notation:

3 + 7.

This code sends the message '+ 7' to 3, the result will be 10. Every message that's only one unicode character long is considered to be a binary message. A binary message always takes one argument: the object that follows after the message.

So, for instance: + - / * < > and = are all binary messages.

Note that spaces matter, you have to write 3 + 7 not 3+7.

Finally, there are keyword messages taking one or more arguments:

3 between: 1 and: 5.

Here we send the message 'between: 1 and: 5' to number 3. This expression will return True. Here is another example:

Pen write: 'Hello world!'.

This will print 'Hello World!' on the screen. We send the message 'write: hello world!' to the Pen object and that object will print the message on the screen.

You can chain multiple keyword messages using a comma:

Pen write: 'A', write: 'B'.

String Interpolation

Citrine supports basic string interpolation like this:

var myName := 'Gabor Software'.
Pen write: 'Created by $$myname '.

The reason why Citrine uses the double dollar sign is to allow developers to quickly use variables in strings (just double tap the dollar) without requiring every regular dollar to be escaped or to require a more complicated syntax. The dollar sign has been chosen because it's a familiar string interpolation symbol also used in PHP and Java.

Control Flow

Citrine does not need a special syntax for loops or conditions. To create a loop, send 'times' to a number:

3 times: {
 Pen write: 'Ho!'.
}.

Likewise, to create a condition, send 'ifTrue' (or 'ifFalse') to a Boolean:

( money < price ) ifTrue: {
 Pen write: 'dream on!'.
}.

Break and Continue

To break out of a loop send the 'break' message to a boolean, like this:

5 times: { :i
 Pen write: i.
 (i > 2) break. #after 2
}.

To skip the remainder of a block in a loop and proceed to the next iteration use 'continue'.

(i > 2) continue.

Exceptions

To catch an exception, you need to associate a catch block with your code block:

{ #throw an exception:
 thisBlock error: 'oops!'.
} catch: { :err 
 Pen write: err.
}, run.

Don't forget the comma right after the catch block! You want to say 'run' to the first code block, not the catch block!

Creating your own objects

You can create your own objects by sending the 'new' message to Object.

myObject := Object new.

Once you've created a new object, you can make it respond to messages (adding methods) like this:

myObject on: 'getBeverage' do: {
	^ 'Coffee'.
}.

As you can see, the second argument of this message is a literal block of code returning a string (^ means return, it's easy to remember because it takes the form of a little arrow pointing upwards!). If you do not return anything within a block of code, a reference to the object itself will be returned (so you can chain messages).

myObject on: 'getBeverageFor:' do: { :forPerson 
	var b := 'coffee'.
	(forPerson = 'Picard') ifTrue: { 
		b := 'Earl Grey'. 
	}.
	^ b.
}.

Note that each parameter of a block starts with a colon (:).

Properties (Me and My)

All properties of an object are private, you can access them using the my keyword.

animal := Object new.
animal on: 'name:' do: { :n
 my name := n.
}.

Since properties are private, other objects can't access it. If you want to make a property (like name) available, you have to add a method that returns it, like this:

animal on: 'name' do: {
 ^ my name.
}.

If you want to send a message to the current instance you use the me keyword. So, for instance, say we have a method called 'sleep' and we wish to make an alias for it called takeNap, we could write something like:

animal on: 'takeNap' do: {
 me sleep.
}.

Here, on receiving the message takeNap, the animal will call its internal method sleep (sending a message to itself using the me keyword).

The keywords me and my always refer to the objects to which they belong. If you invoke a block of code by sending the run or applyTo: message, me and my will refer to the block itself.

Prototypes

To reuse code, Citrine uses prototypical inheritance. Let's create a cat from the animal object:

cat := animal new.
cat name: 'Diva'. 

The new message will create a new object, setting the prototype link to the original one. To invoke an overridden method, prefix the message with a backtick (`).

cat on: 'name' do: {
 ^ 'your royal highness '
  + (me `name).
}.

If you now ask the cat for its name, it will point out you should address her with 'your royal highness Diva.' instead of just 'Diva'.

Malleable Objects

You can extend existing objects, for instance, to make numbers respond to × :

#let's extend the number object with a unicode times equivalent!
Number on: '×' do: { :b
 me times: b. 
}.
7 × { :i  Pen write: i. }.

Generic responses

By default, if an object does not know how to respond to a certain message it just does nothing and returns a reference to itself. This applies even to Nil, so in Citrine you don't have to worry about NullPointerException issues like in Java.

You can make objects respond to arbitrary messages if you like, in some languages this is known as 'magic methods'.

echo := Object new.
echo on: 'respondTo:' do: { 
 :sound 
 2 times: { Pen write: sound. }. 
}.
echo ho!. #ho!ho!

You can also craft dynamic messages using a string and an array like this:

str := 'between:and:'.
Pen write: (3 message: str arguments: (Array < 2 ; 8)), brk.

Functions & Scoping

You can treat a block of code like a function, just send the message run to a block and it will execute the code within. If you need parameters use: applyTo instead.

{ ^9. } run. #yields 9
{ :a  ^ a * a. } applyTo: 6. #yields 36
{ :a :b  ^ (a + b). } applyTo: 3 and: 4. #yields 7
{ :a :b :c  ^ (a + b + c). } applyTo: 3 and: 4 and: 1. #yields 8

Citrine uses dynamic scoping. To declare a new variable use:

var a := 1.

Here, we assign the value 1 to variable a in the current scope, it will exist as long as the current block of code runs, and it will be visible to all blocks of code called during that time.

{ var q := 1. { Pen write: q. } run. } run. #prints 1.
{ var q := 1. { q := 2. Pen write: q. } run. } run. #prints 2.
{ var q := 1. { x := 2. Pen write: x. } run. } run. #Not allowed x is not defined
f := { q := 2. }. { var q := 1. f run. Pen write: q. } run. #prints 2

As you might have noticed, only in global scope you're allowed to omit the var keyword.

To create a closure, bind the in-function variables explicitly to the block object, like this:

multiplier := { :m 
 ^ { :x ^(my f * x).
} set: 'f' value: m. }.

double := multiplier applyTo: 2.
q := double applyTo: 9. #yields 18

Here we create a higher order function mapper that takes an array and a function and applies the function to every element in the array:

addition := { :x 

 var f := { :y
  ^ (my x + y).
 } 
 set: 'x' value: x.
 
 ^ f.

}.
mapper := { :array :func
 
 var mapping := { :i 
  my q put: (my f applyTo: (my q at: i)) at: i.
 }
 set: 'q' value: array,
 set: 'f' value: func.
 (array count) times: { :i
   mapping applyTo: i.
 }.
	
}.
a := Array < 1; 2; 3.
mapper applyTo: a and: (addition applyTo: 100). #101 102 103

Spaces matter

In Citrine, spaces matter, for instance:

priority + 3

should mean something different (send binary message + with argument 3 to object priority) than:

priority +3

The == symbol

Because Citrine uses := for assignment, it makes sense that = is used for comparisons. The == symbol has no meaning in Citrine.

Template Syntax

Citrine supports PHP-like template syntax using <? and ?>. To use Template Syntax, begin your document with ?> start a code section with: '<?.'. Mind the dot, it ends the 'template' string. Without the dot you can chain additional Pen commands like 'write'.

?><html>...
	<?. Pen write: 'Citrine starts here!'. ?>	

Plugins

To keep Citrine clean and small we use plugins (thus adhering to the UNIX philosophy of doing only one thing and doing it well). To load a plugin just begin to send messages to the object provided by the plugin. As soon as you start 'talking' to the object, Citrine will attempt to load it from your plugin folder. Take a look at the HTTP plugin below for an example.

HTTP Plugin

Citrine ships with a CCGILIB based HTTP plugin that allows you to deal with GET and POST requests. There's no need to explicitly load the plugin, just start talking to an object and it gets loaded automatically. Plugin path format:

mods/{plugin}/libctr{plugin}.so

for instance the HTTP Request plugin provides a Request object, so the path is:

mods/request/libctrrequest.so

you can use the Request object like this:

#blog.ctr?search=eastereggs
s := Request get: 'search'. #autoloads Request plugin
Pen write: ('searching for: '+s). #searching for eastereggs

More examples:

list := Request getArray: 'orders[]'. #GET an array of items
post := Request post: 'message'. #get a POST var
list := Request postArray: 'orders[]'. #array from POST
file := Request file: 'avatar'. #returns Array (0 = temp file, 1 = name)

Storm Server

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:

#Citrine Storm Server Demo storm.ctr
#Put your Citrine scripts in /var/www/htdocs/...
#To kill this server
#kill $(cat /var/run/storm.pid)
Pen write: 'Start Citrine Storm Server v1', brk.
Request host:'localhost' listen:4000 pid:'/var/run/storm.pid' callback: {
	#Now set the content type and any other headers
        Pen write: 'Content-type: text/html\n\n'.
	#Load the script file
	var fname := Command env: 'DOCUMENT_URI'.
        var script := File new: '/var/www/htdocs' + fname.
        #Run it!
	script include.
}.

NGINX Configuration

So, to begin with we have to bring up the Citrine SCGI Storm Server like this:

ctr storm.ctr

You'll now see a welcome message like this:

Start Citrine Storm Server v1

Now it's time to configure NGINX to use Storm Server, open nginx.conf:

vi /etc/nginx/nginx.conf

(note: the path of the nginx configuration file can be different depending on your OS)

and add:

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

Now restart NGINX and you're done! You can now add your Citrine pages (using .ctr extension) in /var/www/htdocs/ or whatever folder you specified in storm.ctr. Have fun!



Written by Gabor de Mooij,
📪 mail at gabosoftware ​dot ​nl



Served by Citrine

BACK TO TOP