Template File Structure
Include (Template) and Code (Unit)
Trickonos knows 2 types of Template Files.
- Includes (in general called Template) which is mixed Code and Output much like PHP
- Code Units (in general called Code/Unit) which is only Code, introduced for template internal declarations, functions, operations and classes
Example Template
Output Head
{? foreach x in [1,2,3] do ?}
Inner Output {? Write x; ?}
{? end; ?}
Output Tailgenerates
Output Head
Inner Output 1
Inner Output 2
Inner Output 3
Output Tail
Example Code Unit
function write_class(name,funclist);
WriteLn 'class '+name+' {';
foreach x in funclist do
WriteLn(' ',makefunction(funclist),';');
WriteLn '};';
end;This does not generate code when included with USE, but whenever the function write_class is called with corresponding arguments.
NOTE: Write and WriteLn are macros, see Core Macros
Include (Template) Markers
In Templates, Code and Output is separated by Markers. The default Markers are:
- {?: start of code
- ?}: end of code
Templates always start in non-code Mode, which means everything is Output until the start of code Marker (also called Code Intro Sequence) is hit. Everything from the Code Intro Sequence to the next end of code Marker (Code Outro Sequence) is considered code. Code and Output separation is
not arbitrarily possible. Output is part of the Language and interpreted as statement, those only possible wherever a statement is allowed. see
Statements NOTE: Template markers may be changed using either the static marker macro or the dynamic System.Marker call
Template/Unit Processing Order
Trickonos processes files in file order (file by file), not order of statements:
(thisfile.tpl)
Do Tricks
{? INCLUDE 'otherfile.tpl'; ?}
Do other TricksThe above File (thisfile.tpl) is loaded and compiled before 'otherfile.tpl'. This creates compile time (static) and execution time (dynamic) behavior, which must be considered f.e. when changing markers. It allows dynamic on-demand load f.e. loading files mixed with control flow:
(somefile.tpl)
{?
if booleanvar then
INCLUDE 'thisfile.tpl';
else
INCLUDE 'otherfile.tpl';
end;
?}
- somefile.tpl is loaded and compiled, interpreter starts
- thisfile.tpl is not loaded when booleanvar<>true (in fact, this file may not exist), but otherfile.tpl will be compiled, loaded and executed in place
- otherfile.tpl same procedure with booleanvar=true
NOTE: booleanvar may not be boolean since trickonos uses an object centric logic, see if/then/else for more
Lexical Elements (and Comments)
NOTE: Except for the Withespace/Markers/Code/Non-Code information, this considers only Code Elements
(Non Code Elements) Whitespace, Markers, Code/Non-Code
NOTE: This part is for information only
Semi-Formal:
whitespace : 0x13 | 0x10 | 0x32 | 0x9 . # (Linefeed, Newline, Space, Tab)
EOS : $ . # End Of Stream/File
CODE_INTRO : '{?' . # Default Code Intro Sequence
CODE_OUTRO : '?}' . # Default Code Outro Sequence
PUT (non-code) : ... . # Everything In Non-Code Sections
NEWLINE : ... . # System/Settings Dependent Newline SequenceInformal:
- Whitespaces are either Line Feed, New Line, Space or Tab ASCII Symbols
- Whitespaces are ignore in Code
- EOS marks the end of input (Stream or File)
- CODE_INTRO marks code start
- CODE_OUTRO marks code end
- NEWLINE is a system dependent line ending sequence (Trickonos detects mixed Unix, Windows and Mac style line endings per default)
Comments
Semi-Formal:
LINE_COMMENT : '//' ... NEWLINE . # single line comment
ML_COMMENT : '/*' ... '*/' . # multiline comment
Informal:
- LINE_COMMENT is a single line comment, starting with a comment marker (defaults to //)
- ML_COMMENT is a multi line comment, starting with a ml comment start marker (defaults to /*) and ending with a ml comment end marker (defaults to */)
- Multiline Comments (ML_COMMENT) can not be nested
- Comments are (ignored) lexical elements and those can appear before,behind and between any valid code lexical elements
NOTE: Comment markers can be changed using the static LNMarker, ECCMarker or dynamic System.LNMarker and System.ECCMarker calls
REMARK: LN is short fore line, ECC for encapsulated comment (thought its non nestable, may change in future)
Identifiers, Value Constructors (boolean, integer, string ..)
Semi-Formal:
# WSN like notation for letters/digits
letter : 'a' | 'b' | ... | 'z' .
digit : '0' | '1' | ... | '9' .
hdigit : '0' | '1' | ... | '9' | 'a' | 'b' | .. | 'f' .
odigit : '0' | '1' | ... | '7' .
bdigit : '0' | '1' .
# Language Elements
NIL* : 'nil' .
BOOL : TRUE | FALSE .
TRUE* : 'true' .
FALSE* : 'false' .
IDENT* : ( letter | '_' ) { letter | digit | '_' } .
INTEGER : NUMBER | INTHEX | INTOCT | INTBIN .
NUMBER : digit { digit } .
INTHEX* : '$' hdigit { hdigit } . # freepascal-ish hexadecimal notation
INTOCT : '&' odigit { odigit } . # freepascal-ish octal notation
INTBIN : '%' bdigit { bdigit } . # freepascal-ish binary notation
STRING : STRING_Q | STRING_E .
STRING_Q : "'"-quoted-sequence . # double-quote escaped (pascal like) string
STRING_E : '"'-quoted-sequence . # escaped (c like) stringInformal:
- Identifiers and Keywords (nil,true,false...) are case insensitive, also some special elements (marked with *) like hexadecimal number notation
- Identifiers (IDENT) start with a letter or underscore optionally followed by one or more (combinations of) letter, digit and underscore
- Numbers (NUMBER) are sequences of digits
- Hexadecimal, Octal and Binary Notation is supported using the prefixes $, &, %
- Numbers in either Decimal (NUMBER), Hexadecimal, Octal or Binary Notation are treated as Integer (NOTE: minus is an operator, those negative integers are not lexical elements but operations )
- Strings (STRING) are either double-quote (STRING_Q) or backslash (STRING_E) escaped strings
- STRING_Q is a pascal-ish multiline string, a double-quote sequence will be changed into a single quote sequence: 'a''b' is the same as "a'b"
- STRING_E is a c-ish multiline string, escape sequences will be changed into according characters: "\\\"\\" is the same as '\"\'
Escape Sequence
The following escape sequences are allowed
| Escape Sequence | Changed into |
| \\ | \ |
| \n | Newline Character (0x10) |
| \t | Tab Character (0x9) |
| \l | Linefeed Character (0x13) |
| \" | " |
NOTE: may be extended in future
Core Language
NOTE: Trickonos Language is dynamic typed and object centric. Expressions are very abstract and in general operate on, or produce an object. Statements use object centric logic. Except for value constructors, types are unknown for the compiler and operations simply "try to do something on an object" which results in an error or a valid object. The following Documentation is not intended for beginners instead, it should be simpler to first have a look at some examples (f.e. the test cases) and/or the Scripting Objects Documentation.
Expressions
Expression Syntax
See Expressions
Operator Priorities/Associativity
Operator Table
| Op | Op Level (ascending) | Op Assoc | Short Description/General Meaning |
| := | AEXPR | right | assignment |
| = | RELOP | left | comparision |
| <> | RELOP | left | comparision |
| <= | RELOP | left | comparision |
| >= | RELOP | left | comparision |
| is | RELOP | left | typecheck |
| + | ADDOP | left | add |
| - | ADDOP | left | substract |
| or | ADDOP | left | logic/binary or |
| xor | ADDOP | left | logic/binary exclusive or |
| * | MULOP | left | multiply |
| div | MULOP | left | divide |
| mod | MULOP | left | modulo (remainder) |
| and | MULOP | left | logic/binary and |
| shl | MULOP | left | (arithmetic) shift left |
| << | MULOP | left | (shl alias) |
| shr | MULOP | left | shift right |
| >> | MULOP | left | (shr alias) |
| rol | MULOP | left | rotate left |
| <<< | MULOP | left | (rol alias) |
| ror | MULOP | left | rotate right |
| >>> | MULOP | left | (ror alias) |
| + | UNOP | - (unary) | absolute |
| - | UNOP | - (unary) | negative |
| not | UNOP | - (unary) | logic/binary not |
| . | SELOP | left | member selector |
| (() | SELOP | left | call |
| ([) | SELOP | left | index selector |
Value Table (for completeness)
| Value Type | Short Description/General Meaning |
| nil | nil object |
| true | true object |
| false | false object |
| <INTEGER> | integer constructor |
| <STRING> | string constructor |
| <LIST> | list constructor |
| <DICT> | dict constructor |
| <IDENT> | name access |
| (<EXPR>) | sub-expression |
Example
nil; // nil object
true; // true object
false; // false object
[]; // empty list
{}; // empty dictionary
ident; // variable/name access
(true); // sub-expression yielding true
x := +1.dostuff()() rol 1 + 5 = 2; // <=> x := ((((((+1).dostuff())()) rol 1)+5)=2)
x := y := z := 1; // <=> x := ( y := ( z := 1 ) )Statements
Trickonos language statements are object driven and can be separated into 2 groups.
- Core Statements which create a certain control flow using core checks. Core checks means compare and typechecks which are part of the core and those always exist, which implies that these core statements won't fail. As an example, have a look at if/then/else, which relies only on object checks that must be present. Interface dependent statements differ.
- Interface Dependent Statements which rely on a certain interface (existing methods). As example, have a look at ForEach, which assumes a certain interface and calls methods that may not be present.
In difference to expressions, statements can contain Non-Code passages, which are also interpreted as statements. Trickonos statements are very pascal oriented in sense of look&feel but in difference to pascal, are in general compound statements terminated with end; whereas pascal in general allows a single statement or a compound statement. Without further digging into that topic, there are 2 major reasons for this. First, it resolves problems with the non-code mixing and shortens the language while maintaining the readability of pascal, 2 the language is simpler and contains strict rules about compound/statement terminators and interpretation (f.e. there is no such thing like the dangling else problem and its conform to the strict expression syntax).
Thought, trickonos has Pascal Compat syntax for statements, which allows to write the keyword "begin" in several places, where compound statements start.
Semi-Formal statements:
STAT: IFTHEN | FOREACH | REPEAT | WHILE | PUT | FUNC .
PUT: non-code lines .
STATS: ( STAT ) .
Informal:
- STATS is a sequence of statements.
- STATS may be the empty sequence.
- PUT consists of non-code.
- PUT is implementation dependent and may be one ore multiple statements (currently implemented as multiple statements, where each line is a statement)
Semantics:
- Statements are executed / interpreted in order, from top to bottom (from first to last statement) with exceptions (control flow modifying statements and expressions)
If Then ElseIf Else
Semi-Formal:
IFTHEN: 'if' <EXPR> 'then' <STATS> (ELSEIF) [ELSE] 'end' ';' .
ELSEIF: 'elseif' <EXPR> 'then' <STATS> .
ELSE: 'else' <STATS> .
Informal:
- IFTHEN starts with the keyword 'if', followed by an expression, the keyword 'then' and a sequence of statements, optionally ELSEIF statements, an optional ELSE statement and is finally terminated with the keywords 'end' and ';'
- ELSEIF starts with the keyword 'elseif', followed by an expression, the keyword 'then' and a sequence of statements
- ELSE starts with the keyword 'else', followed by a sequence of statements
- Expressions, also referred to as Conditions, are not limited to Boolean Expressions
Semantics:
- IFTHEN (and respective ELSEIF) checks - evaluates and tests the resulting object - whether the resulting object (called result) of its Expression is false (Boolean) or nil.
- If the result is false or nil, the corresponding statements are skipped and the next ELSEIF is tested.
- If the result is neither false nor nil, the corresponding statements are executed and control flow returns after the 'end' ';'
- If neither IFTHEN or ELSEIF was executed, and there exists an ELSE, the ELSE statements are executed and control flow returns after the 'end' ';'
- If neither IFTHEN or ELSEIF was executed and there exists no ELSE then control flow returns after the 'end' ';'
Example if then:
if true then
Halt(1); // this will be executed (always)
end;
if true then // this is allowed
end;
if false then
Halt(1); // this is never executed
end;
if nil then
Halt(1); // this is never executed
end;
if SomeName then
Halt(1); // this depends on the value of SomeName
end;
if (SomeName <> nil) and
(SomeName <> false) then // semantically equivalent to the above
Halt(1);
end;
if x := nil then // allowed
Halt(1); // never executed since expression = nil
end;
Example if then elseif:
if false then
Halt(1); // not executed
elseif true then
Halt(2); // always executed
end;
x := 2;
if (y := x) < 1 then // assigned, but if is skipped
elseif (z := x) = 2 then // assigned, elseif is executed, control flow returns on end
elseif (a := x) = 3 then // not assigned, since this part was skipped
end;
Example if then elseif else:
if true then // control flow returns on end, both elseif/else are skipped
elseif false then
Halt(1);
else
Halt(1);
end;
if false then
Halt(1);
else
Halt(0); // always executed, program stops with 0
end;
Repeat Until
Semi-Formal:
REPEAT: 'repeat' <STATS> 'until' <EXPR> ';' .
Informal:
- REPEAT starts with the 'repeat' keyword, followed by statements, the 'until' keyword, an expression and terminates with ';' .
- Typical foot controlled loop, where Expression is tested after execution of statements, those statements are executed at least once
Semantics:
- REPEAT tests the expression result for nil and false.
- If the expression result is nil or false, the loop will be reentered on the first statement of STATS
- If the expression result is neither nil nor false, the loop is terminated and control flow returns after ';'
- REPEAT may result in an endless loop
Example:
x := 1;
repeat
x := x + 1; // increment x by 1
until x > 10; // terminate loop, when x > 10
y := nil;
repeat
x := x + 1; // increment x by 1
if x > 10 then // assign x to y when x > 10
y := x;
end;
until y; // terminates loop, when y is assigned
y := nil;
repeat
until y; // endless loop, since y is always nilWhile Do
Semi-Formal:
WHILE: 'while' <EXPR> 'do' <STATS> 'end' ';' .
Informal:
- WHILE starts with the 'while' keyword, followed an expression, the 'do' keyword, statements and terminates with 'end' ';' .
- Typical head controlled loop, where Expression is tested before execution of statements, those statements may never be executed
Semantics:
- WHILE tests the expression result for nil and false.
- If the expression result is neither nil nor false, the loop will be reentered on the first statement of STATS
- If the expression result is nil or false, the loop will be terminated and control flow returns after 'end' ';'
- WHILE may result in an endless loop
Example:
x := 1;
while x < 10 do // terminate loop, when x > 10
x := x + 1; // increment x by 1
end;
y := nil;
while y do // terminate when false/nil
y := x + 1; // never executed
end;
y := 1;
while y do // endless loop, since y <> nil and y <> false
x := x + 1;
end;Function Declaration
Semi-Formal:
FUNCARG: <NAME> [ ':=' <EXPR> ] | 'varargs' [ ':=' <EXPR> ] .
FUNCARGS: '(' [ <FUNCARG> (',' <FUNCARG>) ] ')' .
FUNCVARS: 'var' <NAME> [, <NAME> ] ';' .
FUNC: 'function' <NAME> [ <FUNCARGS> ] ';' [<VAR>] <STATS> 'end' ';' .Informal:
- A function declaration starts with the keyword 'function', optionally followed by a list of function parameters, the head terminator ';', optionally a list of variables, a list of statements and the body terminating keywords 'end' ';' .
- A list of variables is a simple list of names.
- A list of parameters is a simple list of names, where names may be followed by ':=' and an initializing (default) expression.
- Parameters with default expression must be behind those without default expressions, f.e. function a(b := 1, c); .. syntactically incorrect. An exception is the name 'varargs' which must be last. varargs may too have a default expression, but it is not required.
- Parameter names, the function name, variables and the special name 'result' are variable names in the function. Both function name and 'result' may be reused as either variable (in 'var' part) or parameter, f.e. function a(a,result); .. is correct. But, duplicate variable and parameter names are incorrect.
Semantics (declaration, function objects, calling):
- Whenever a function declaration is evaluated, it registers a function object with the function name in the current scope.
- Function objects are anonymous, the name serves simply the purpose of storing the function object for both declaration and calling (and self calling/recursion)
- A call (see expressions) on function object, enters the function with given arguments, where:
- For all parameters without default expression, there must be an argument which is set in the corresponding parameter name
- For all parameters with default expressions, there may be an argument which is set in the corresponding parameter, if there is no argument the default expression will be evaluated and the value set in that variable
- Default expression are evaluated on call, not on declaration and those depend on the environment at call time
- Default expressions are evaluated in parameter passing order (left to right)
- Default expressions are allowed to access other parameters to the left, f.e. function a(b := 1, c := b); ... assigns 1 to b and local variable b to c
- Default expressions can not access the current name and those to the right, 'result' or the function name, these names will read from the global environment
- If the call passes more arguments then actual parameters (varargs name is not accounted as such) exist, every argument not matching a parameter is put into a list set into varargs (called vararg parameters)
- varargs is either a list if there are vararg parameters or
- nil if there are no vararg parameters or
- a default value if there are no vararg parameters an a default expression was specified
- All names used in a function access the global scope except for those registered as variables, the function name, 'result' and parameter names. These are in the functions scope
- Variables are initialized with nil
- In the function body, all variables can be read and written without restrictions.
- Result is initialized with nil, when not used as parameter or function name
- Result is special, as its the name (or variable) used to return infos to the caller (return value).
- The function name contains the function object for self calling (recursion) when not used as parameter or variable name
- The function is left (return) after the last statement and result is passed back as return value (function result)
Example simple fun:
function simple;
Result := 'simple';
end;
writeln simple; // outputs: function object infos
writeln simple(); // outputs: simpleExample parameter:
function sum(a,b);
Result := a+b;
end;
x := sum(1,2); // -> x = 3
x := sum(1); // error, wrong arg num
x := sum(1,2,3); // error, wrong arg numExample default parameter:
function sum(a,b,c := 0);
Result := a+b+c;
end;
x := sum(1,2); // -> x = 3
x := sum(1); // error, wrong arg num
x := sum(1,2,3); // -> x = 6
function sum2(a:=1,b,c); // error -> b/c missing default expressions
end;Example varargs:
function sum(a,b,varargs);
Result := a+b;
if varargs then
foreach x in varargs do // <- x is global!
Result := Result + x;
end;
end;
end;
x := sum(1,2); // -> x = 3
x := sum(1); // error, wrong arg num
x := sum(1,2,3); // -> x = 6
function better_sum(varargs := []);
var x;
Result := 0;
foreach x in varargs do // <- now, x is local
Result := Result + x;
end;
end;
sum := better_sum; // replace old fun sum with better sum
x := sum(1,2); // -> x = 3
x := sum(1); // -> x = 1
x := sum(1,2,3); // -> x = 6
x := sum(1,2,3,4); // -> x = 10TODO: some more, variable usage, sub functions
ForEach In Do
TODO
Compiler Macros (Language Extensions)
Core Macros
TODO (write*, halt, assert, include, use)
Extension Macros
TODO (output handling macros ...)