CORN Programming language

Language definition



1. Structure of language
2. Structure of program
3. Class names and namespaces
4. Structure of class
5. Initialization of objects, inheritance
6. Returned objects
7. Blocks, vectors, arrays
8. Using variables
9. Build messages
10. Evaluation
11. Special objects
12. Strings, chars, integers
13. Arithmetic instructions
14. Monitor class
15. Selector class
16. Conditional instructions and loops
17. Exceptions
18. Shortcuts
19. Language preprocessor



1. Structure of language

Source code is written in a plain text files, usually with extension *.corn , in free style, it means, that there are no general rules about location of every syntax item.
One file can contain definitions of many classes. Source files contains only definitions of classes.
Syntax items of corn language are case-sensitive.

While source code is being compiled, it is first preprocessed , then many specific rules are applied (both to the source code and compiler behaviour), most of them can be chosen as a parameters of compiler. Next, structure of read source code are being checked, and then compiled.

To compile sources, run the compiler:

$ corncmp [options] -o outputFile.pack inputFile1.corn inputFile2.corn ...


2. Structure of program

Program written in corn language is a set of files, which contains compiled packages of classes, they are usually files with extension *.pack .

To execute a program, packs must be loaded into the corn machine, and one message needs to be send to the object of selected class. Corn machine can be a separate, standalone process executed for one single program, or can be a server, which launch number of programs at time.

For example, if program is contained in two packages: mylibs.pack , myprog.pack and there is a class named myprog.starter which accept message startMyProgram , then to run that program by corn machine, type:

$ corn -pack mylibs.pack -pack myprog.pack -exec myprog.starter startMyProgram

Any extra parameters passed to the corn script will be given to that function as a string parameters (but it may depends on destination platform specifics).

Notice, that a corn compiler is also written in corn language and it is executed in the same manner.



3. Class names and namespaces

Classes are organized in packages. Typical class name is a sequence of packages (subpackages) names and a final name separated by dots, ie:

yellow.struct.stack
yellow.sync.mutex
corn.error.uncatched

To cut declaration, and usage of classname by first packages prefix use namespace , syntax:

namespace prefixname {
    // region of code
}

There is also possible to "dig a hole" in that namespace stack, with namespacehole instruction.

In example, this two declaration are the same:

class a.b.c.d.e
    when get => new a.b.c.x()
end

namespace a {
    namespace b.c.d {
	class .e
	    namespacehole {
		when get => new .x()
	    }
	end
    }
}


4. Structure of class

Programs in corn language are organized in objects. Each object have it's own definition of behaviour, named class . There can be many object of one class, but only one class per object.
A class can be either named, to be accessed externally, or unnamed, to be created internally.
There are three types of classes: class (basic class), monitor and selector .

Syntax:

[class|monitor|selector] [name] [ on parentClass... ]
    [var ... ]
    [uniq ... ]
    [condition ... ]
    [init|start ... ]
    [when ... ]
    [private ... ]
    [other ... ]
end
  • var - list of internal class' variables, separated by comma. Each value points at the beginning to the null object.

  • uniq - list of variables, which will be set with unique values (those values depends on implementation).

  • condition - list of class conditions (only for monitor class) separated by comma.

  • init - an entry executed when object is created, there can be many init entries in class. In named class, only one init entry can expect parameters, which became from creation instruction new... .

  • start - start entry for selector class, there can be only one.

  • when - public entry definition for catching message.

  • private - private (visible only inside class) entry definition for catching message sended to the object.

  • other - an entry, executed when there are no matched entry for incoming message. If not exists, then message will be passed down to inherit chain.

To create new object of named class, write:

x = new package.classname( ... params to constructor ...);

To create object of unnamed class, write:

x = new class ... end ;

One function entry ( when or private ) can catch few different messages, syntax:

when[(ae)] msg1|msg2|msg3 ...param variables... => result object

Entry can be declared with (a) flag, (e) flag or both: (ae) . Meanings of that flags are:

  • (a) - the last params of that messages will contain array of rest params, In this situation, if you call a message with different number of params from the declaration, then rest of them will be passed as an array of object; otherwise you will not be able to access them.

  • (e) - params to that function needs to be evaluated before function call, if not, then corn kernel will evaluate them before executing that function.

The other entry defines behaviour of object when no entry matches called function name. This entry needs to have 2 params, at the first params message name will be placed, at the second param will be placed array of params of that called message. Syntax is similar:

other uncatchedMessage, messageParams => result object

Params to the function are simply list of named variables, or references to variable in context (marked with char & ), which will be seted to the value of that param, ie:

    var x
    when set y,&x,z => {}


5. Initialization of objects, inheritance

A class can inherit from the other one, it means, that every uncatched messages will be automatically passed to that other object. It is allow to inherit from named or declared class. Syntax:

class [name] on a.b.c
class [name] on class ...

Inheritance is not strongly defined, and it can be changed (switching parent of prepared object) in the time of execution. Parent objects are not created with object, but only when they are needed (in lazy way).
Therefore, inheritance relation groups objects in chains. A chain can be infinite or ended by cycle. Every chain contains at least 2 objects. Each object in chain can access another with special variables: self , current and super .

  • self - the pointer to the first object in chain

  • current - the pointer to the current (caller) object

  • super - the pointer to the next one object in chain (parent).

New objects are created by:

new classname(vector of params)

An array of params from that instruction in passed to the object's constructor init or start entry start for selector.
It is possible to switch created object (inside constructor) by another one, calling:

pass objectForReplace ;

The next one object is inherit chain (parent) will be created when it is needed. To set array of params for constructor, of not yet created parent, call:

initsuper(vector of params)

The result of that instruction is true when object is not yet created, therefore params are correctly saved, or false if parent object is already created, and it is too late to set params for creation.



6. Returned objects

Each of sematic instruction returns an object (except three instructions), and therefore can be put in each place expected object.

Every function and message entry must return an object for response. There are three special instructions, which breaks evaluation for current function, they are: return , break and pass .

  • return object

    - stop evaluation for current function, and return selected object in response.

  • break

    - is the same as: return self;

  • pass object

    - stop initialization for current object, and pass an object, as a new created one.



7. Blocks, vectors, arrays

Linear block is a list of instructions to be made sequent before giving a result. The result object of a linear block is a result object from the last instruction, or self object if there was none. Syntax:

{ i1 ; i2 ; i3 ; ... }

Concurrent block is a list of instruction to be made concurrently. The result of concurrent block is a result from the last ended instruction. Each of that instruction allow to use return and break instructions inside, which will breaks current thread, but not global thread. Syntax:

{# i1 ; i2 ; i3 ; ... }

Separate block is a list of instructions to be made independly. Global control will not wait for its execution (be careful to use enough semicolons around, because compiler may threads chars < , > as an arithmetic instructions). Syntax:

< i1 ; i2 ; i3 ; ... ; >

To start a number of separate instruction use syntax:

<# i1 ; i2 ; i3 ; ... ; >

Vector is a list of objects, which are given to evaluate function or create object. A vector can be either linear or concurrent , which differs methods to determine objects. Syntax:

( o1 , o2 , o3 , ... )
(# o1 , o2 , o3 , ... )

Array is an object which contains finite ordered collection of objects. An array, like vector, can be created linearly or concurrently. Syntax:

a = [ o1 , o2 , o3 , ... ]
a = [# o1 , o2 , o3 , ... ]

An array can be also splited to separate objects in reverse syntax:

[ o1 , o2 , o3 , ... ] = a


8. Using variables

Variable is a named handle to some object.
To locally declare a variable for use inside some syntax instruction, use let structure:

let variableName [ = initial value ] in instruction

To declare variable for use inside class, use var structure:

class
    var variableName [ = initial value ]
end

To declare a number of variables in let or var structure, separate them by a commas.

One variable can be handled from name in any context of evaluation, for which that variable can be "visible". Meaning of that variable will be used in the point of evaluation, not in the point of creation, ie:

let x,y
in {
    fun a,b => {
	new class
		var c,d
		when send e,f => {
		    let z
		    in
		    return x;
		}
	    end
    };
}

To declarate variables as items of some array or collection use splited syntax, ie:

let a, b = true, [c,d,e] = someArray in  ...

Corn compiler can shuffle variables (and do that often) while it makes binary representation. It can be not always wanted, so, to keep some variables in their positions put volatile label before declaration of their names. It will inform the compiler, to not move that variables (and therefore, they will not be optimized):

let a,volatile b,[c,,volatile d] = e in ...


9. Build messages

These are basic instructions to prepare and build messages:

  • object . messageName

    result is a function entry in some object in inherit chain, that can catch that message

  • object1 & object2

    result is a function entry (in object1), not given from name, but from string object (object2)

  • object ( vector )

    selecting vector of params to the function

  • object1 ~ object2

    selecting params to the function, not from vector, but from array object

  • object \ ( vector )

    append a vector of params to the not yet evaluated structure

  • object1 \~ object2

    append an array of params to the not yet evaluated structure

The next one instructions can be used as shortcuts for above instructions, with exception, that each syntax structure will be evaluated in order of its declaration:

  • object : messageName p1,p2,...
    it equal to: object = object.messageName(p1,p2,...)
  • a = object :: property 
    is equal to: a = object.property()
  • object :: property = a
    is equal to: object = object.setproperty(a)
  • a = b[c]
    is equal to: a = b.get(c)
  • a[c] = b
    is equal to: a = a.put(c,b)
  • ++a
    is equal to: a:++ , which is equal to: a = a.++()
  • --a
    is equal to: a:-- , which is equal to: a = a.--()


10. Evaluation

To evaluate prepared function in variable x call:

    * x

and the result of calling will be placed back to that variable.

A result of calling function can also be unevaluated function, which result can be another unevaluated function, and so on. To evaluate that kind of results, and finally got evaluated object in response, call:

    ** x

Every object can be evaluated only once, even if two processes will try to evaluate the same object concurrently, ie:

    let x,y,z, z1,z2,z3
    in {
	x = user; y = x.println; z = y("text");
	z1 = z2 = z3 = z;
	{# **z1; **z2; **z3 };
    }

and the output will be only one "text" string.


11. Special objects

This named objects are accessible everywhere inside code.

  • system - this object represents corn runtime machine and provides many resources, which can't be handled in common libraries. There is only one system object per runtime machine.

  • user - this object represents an abstract user of current evaluation. The idea is, that there are many users, and everyone makes own code concurrently. It easy to internally create a new one user object, and evaluate some code on its side.

  • self - first object in inherit chain

  • super - pointer to the next one object in chain

  • current - pointer to the current object

  • same - pointer to the current function (the same function)

  • null - this object represents good old "null" :)

  • true , false - this objects represent states of logic

 


12. Strings, chars, integers

Strings, chars, integers are undividable objects, which can be handled by variable or special inline syntax inside code.
Inline syntax of those objects:

  • string - a region between chars: ( " ) contains chars of its content. A string declaration must end before end of line.

  • char - a single character between chars: ( ' ). Some characters needs to be predeceases by backslash, these are: ( \ " ' cr tab )

  • integer - typed integer value from -(2^32-1) to +(2^32-1). It can be also typed in hex form, ie: 0x0E, -0x2F0C. Integers are unlimited while program works, but inside code, it is possible to create integers only from that range above. Larger integers (out of that range) exists, but become dividable and not unique objects.

Undividable means, that they can be compared each other as unique instance of objects, ie:

    ifeq animal,"Elephant" ...
    if!eq 616,value ...

which compares objects with no content checking.



13. Arithmetic instructions

These are arithmetic instruction, which are switched to the message passed to the first one argument, with the second one as parameter.

 + - ++ -- * / %  == != <> <= >= < > not and or nor nand xor

The priority of groups of those instructions runs from right to the left.

In example: a + b are switched to: a.+(b) by compiler.



14. Monitor class

Monitor is a type of class, which guaranties exclusive access to its public functions.

A monitor condition is a construction, which allows to manipulate monitor state, by defining internal conditions. There are only three allowed instructions works on condition:

  • wait condName

    going to wait on selected condition for a signal. If called thread own current monitor lock, then it is released for waiting time.

  • signal condName

    signal to wake up one waiter on condition (if anyone waits).

  • signalall condName

    signal to wake up all waiters on condition.

Instructions to release and lock monitor inside code are: leave and enter , ie:

monitor ex.ex8
    when message => {
	// ... have monitor lock
	leave {
	    // ... don't have monitor lock
	    enter {
		// ... have monitor lock
	    };
	    // ... don't have monitor lock
	};
	// ... have monitor lock
    }
end


15. Selector class

Selector is a class, which receives messages inside own code, using special instructions. It can decide which message it will receive, and also decide in which order.
Syntax of selector class:


selector [ name ] [on ...]
    [ var - internal variables ]
    [ start - selector startup entry ]
end

There are only one startup entry, which will be executed when object will be created, with params passed to the constructor.
Instructions for internally receive messages are accept and acceptany . Syntax:

accept msg1|msg2|msg3 msg,ap => returned object
acceptany msg,ap => returned object

First one instruction accepts incoming message with chosen name, the second one accepts any new incoming message. Parameter msg will be set to the name of incoming message, and parameter ap will be set to the array of message params.
When this instruction is done, a returned object will be passed as a response to the caller.



16. Conditional instructions and loops

Postfixes part of a name of conditional instruction is a part of conditions. Possible postfixes are:

true !true false !false null !null zero !zero one !one eval !eval eq !eq

Character: ( ! ) switch condition to the opposite.
Conditions true, false, null, zero, one compare selected object with given one.
Condition: eval checks, if given object is evaluated.
Condition: eq compare two given objects, separated by comma.

  • ifPOST instruction-then [ else instruction-else ]
  • whilePOST instruction
  • do instruction whilePOST
  • forPOST ( instr1-initial ; instr2-for-condition ; instr3-step ) instr4-body 
  • for ( instr1-for-increment ; instr2-times ) instr3-body 
  • times instr1-count , instr2-body 
  • loop instruction 
  • switch inst1-selected in
        instr2-1 [, instr2-2, instr2-3, ... ] then instr2-then ;
        instr3-1 [, instr3-2, instr3-3, ... ] then instr3-then ;
        ...
        [ else instr-else ]
    end
    

Meaning of that instructions shouldn't be hard to guess :).



17. Exceptions

Exception can be thrown only due to evaluation or creation new objects.
To throw exceptions from code write:

throw exception-object

To catch, from region of code, exception of selected classes into new variable write:

try
    instruction
catch
    name-of-class variableName => instruction ;

It is possible to catch exceptions of different classes from the same fragment of code, by expanding catch declaration:

try
    instruction
catch
    name-of-class-1,name-of-class-2 variableName => instruction
catch
    name-of-class-3,name-of-class-4 variableName => instruction ;

Exceptions from threads of concurrent block {# ... } will be passed to the parent thread. In that case, rest of that threads won't be breaken, and parent thread will receive only one - last throwed exception from all child threads.



18. Shortcuts

There are some shortcuts in syntax of corn language. It is sometimes easy to use them (and also code looks better).

  • show name
    var name
    when name => name
  • hide name
    var name
    when setname x => { name = x; {}}
  • swap name
    var name
    when name => name
    when setname x => { name = x; {}}
  • a = object::propname
    a = object.propname()
  • object::propname = a
    object = object.setpropname(a)
  • a ?!true b [ else c ]
    if!true a b [ else c ]
  • a = b[c]
    a = b.get(c)
  • a[c] = b
    a = a.put(c,b)
  • @a
    a.clone()


19. Language preprocessor

Preprocessing source code is the first step in compilation prepared by compiler. It removes comments and analysis preprocessor directives.

Comments are the same as in C/C++ languages. That is, a comment is a region of code starting before signature: /* and ends after signature: */ . A comment starts also before signature: // to the end of current line. Comments are not searched inside string declaration: "..." nor any special regions.

A line ended by char: \ is treated as prefix of the next one line.

Current preprocessor directives are:

  • #define DefinedLabel [...code to the end of line...]
    #define DefinedLabel ( params ) [...code to the end of line...]
    defines new label by name, which will be switched in code by its declarations.

  • #undef DefinedLabel
    undefines label.

  • #ifdef DefinedLabel
    #if!def DefinedLabel
    start or deny to analyze region of code, depends on that, if selected label are defined or not. This preprocessor directives must be ended by:
    #endif
    or can be switched on analyze state by directive:
    #else .

  • #if Arithmetic condition
    start on deny to analyze code, depends on condition. Simple arithmetic operations are allowed in the condition: + - * / % == < > () , etc.

  • #include " filename "
    includes text from another file in the place of that directive.

  • #include < filename >
    the same, but file is searched in standard include directories.

  • #error Error string
    create user error in the place of declaration.



  Corn version: 2.8.2