See SPEC.tri for an up-to-date draft language specification (2024).
A new compiler is also being worked on and can be found here.
Finally, you may want to take a look at random_experiments.tri for sketches of newer language concepts.
Everything below this point was last updated on 2020-08-05.
Triforce is a programming language emphasizing performance, power and productivity.
The main goals of the language include:
- a consistent, sensible and well-thought-out language design
- a small, generalised and extensible core language, including support for powerful macro-like functions
- an advanced type system, including dependent types
- the ability to both work on a high level and a lower level close to hardware
- safety by default; less safe options will be discouraged and require opt-in
- readable, understandable code (at least for common situations)
- more things yet to be figured out...
Obviously, some compromises are going to have to be made in order to achieve these goals. The compilation process is probably going to be rather slow, for example.
The language has recently been renamed from P+ to Triforce, meaning some things are still using the old name or file extension. There are also some (minor?) problems with the name, see issue #308.
I'm currently working on a wiki for this language which will explain some things about the language in a more understandable way than this README does. The wiki is currently very incomplete however, so you can't really learn much from it yet. But it's not really a good idea to start learning this language at this moment anyway, since there's not much stability right now and the latest working compiler is not even able to compile more than a small subset of an old version of the language.
Currently, there is only syntax highlighting available for Sublime Text 3, which can be found in the st3 folder. If you don't feel like using ST3, you can either try creating your own syntax highlighting for the language, or try using the Swift highlighting (which is, well, better than nothing).
Currently, this README pretty much only consists of a language specification. Similarly to most language specifications, it's rather hard to understand and you'll probably have a hard time learning the language by reading it. It also has the extra perk of not even being written well enough to precisely describe the language! And did I mention that I'm also changing it all the time? Really, the specification is just here for the developers to remember everything. But, you can always attempt to read the rest anyway if you want xD
Keywords surrounded by
- brackets (
[]
) are optional - angle brackets (
<>
) must be replaced by a name of your choice - backticks (
`
) are required and escapes these definitions (i.e.{a`|`b}
means you must literally type{a|b}
) - braces (
{}
) and seperated by bars (|
) are part of a list of mutually exclusive required keywords - brackets (
[]
) and seperated by bars (|
) are part of a list of mutually exclusive optional keywords
The equivalence symbol (<=>
) means essentially what it does in mathematics.
<...>
and [...]
mean something required respectively optional that fits in.
\`
escapes the escaper (i.e. \`\`
means you must literally type ``
).
Everything else is required.
NOTE: This specification mainly specifies the built-in parts of the language. See the prelude and libraries for the rest.
- The compiler will evaluate programs as much as it can during compilation, and then convert the remnants into a specified output format.
- Normally this is machine code
- Programs are composed as described under "Program composition".
<program> = <expr>
<expr> = [raw] {<air> | {<anon-func>|<named-pattern>} [<expr>] [...] | <applied-named-pattern> | unparsed <expr> | parsed <unparsed-expr>}
<air> = {[<ws>] | ([<ws>]) | (([<ws>])) | <...>}
<ws> = <whitespace> [...]
<anon-func> = [impure|unpredictable] <pattern-def> [...] => <expr>
<pattern-def> = $(<named-pattern>) as <matcher>
<pattern> = {<id-token> | <pattern-def>} [...]
<matcher> = [implicitly] [unparsed] [raw] [impure|unpredictable] <match-pattern> [returning <pattern>] [...] [constructed using <pattern>]
<match-pattern> = {<id-token> | <pattern-def> | ${<pattern>|#<int>} | #(<pattern-def>)} [...]
<int> = {0|1|2|3|4|5|6|7|8|9}[...]
Note: An <expr>
may evaluate at compile time to a valid, say, <match pattern>
, and would in such a case be a valid <match pattern>
. See "Anonymous functions" §9. Syntax sugar changes things a bit too; see "Syntax sugar".
- Number structure:
{0|1|2|3|4|5|6|7|8|9}[0|1|2|3|4|5|6|7|8|9|_|.][...]
.
is a comma used for fractions_
is a separator used to improve readability- ex:
1_234_567.89
- Numbers prefixed with
0x
are hexadecimal and additionally allowa
,b
,c
,d
,e
andf
.- ex:
0x123456789abc
- ex:
- Numbers prefixed with
0o
are octal and exclude8
and9
.- ex:
0o1234567
- ex:
- Numbers prefixed with
0b
are binary and only allow0
,1
and_
.- ex:
0b00101001_00000110_01010110_10010010
- ex:
- See "Low level" for low level forms of number literals.
- The symblock
__sb_default_str__
is used for string literals.- syntax declared in the prelude:
decl symblock __sb_default_str__ enclosed by " " with escaper \;
\n
,\r
,\t
,\0
and\\
inside of a string mean "newline", "carriage return", "tab", "null" and "backslash" respectively
- syntax declared in the prelude:
- Structure:
(<input pars> => <function body>) <input args>
. <input pars>
=(<par1>) [(<par2>)] [...]
<input args>
=<arg1> [<arg2>] [...]
- Every parameter is a pattern def.
- All functions are closures (capturing their original environment).
- An empty
<function body>
is equivalent topaused __caller_scope__
. - If not enough input args are given, the function is partially applied.
- Once fully applied, functions reduce to
<function body>
(with all<input pars>
defined).- This is what it means to say that a function returns
<function body>
- This is what it means to say that a function returns
- Functions and patterns can be (partially) called/applied inside of
<function body>
,<input args>
,<pattern to define>
and<pattern to match>
.- Note that surrounding backticks (
\`\`
) are necessary when applying inside<pattern to define>
- i.e. if we
let f = Something
, thenlet f _ = SomethingElse
redefinesf
whilelet `f _` = SomethingElse
becomeslet Something = SomethingElse
- this is because
<pattern to define>
is taken asimplicitly unchecked paused
- the surrounding backticks escape all implicits, thereby forcing evaluation even when implicitly
paused
- i.e. if we
<pattern to match>
is always fully evaluated, even if it's inside apaused
block
- Note that surrounding backticks (
- Functions are pure and checked by default.
- A pure function has the following properties:
- its return value is the same for the same input
- note that the input consists of both the arguments passed and the state of the outer scope
- its evaluation has no side effects
- note that debug-mode side effects disappearing in release-mode don't count, allowing i.e.
##[dbg_mode_only] debug <...>
(?)
- note that debug-mode side effects disappearing in release-mode don't count, allowing i.e.
- its return value is the same for the same input
- The body of a checked function will be checked by the compiler as soon as it is encountered
- this means undefined patterns, incorrect syntax, mismatches, etc. are not allowed
- A pure function has the following properties:
impure <function>
allows a function to have side effects.unpredictable <function>
allows a function to have side effects and different return values for the same input.- Mainly useful for functions interacting with the outer world (user input, true randomness, etc.)
unchecked <expr>
allows for an expression (i.e. a function) to be non-checked.- This means the compiler won't check the expression until it's in its final scope
- For an example, see "Example code snippets" §2
- While functions require at least 1 parameter,
(_ as ()) => <function body>
can be used to emulate a function without parameters.- This is also the recommended way; doing
(_ as ...)
or(_ as _)
serves different (rare) purposes
- This is also the recommended way; doing
-
<pattern def>
=($(<pattern to define>) as <pattern to match> [constructed using <constructor pattern>])
where<pattern to define>
=<dname start> [{(<pattern def>)|<dname continuation>}] [...] [<dname end>]
where<dname start>
,dname continuation
,<dname end>
are allowed to contain any symbols, including whitespace (TODO: exception for ops)
<pattern to match>
=[<mname start>] [{(<pattern def>)|<mname continuation>|${<var>|#<n>}|#<pattern def>}] [...] [<mname end>]
where<mname start>
,mname continuation
,<mname end>
are allowed to contain any symbols, including whitespace (TODO: exception for ops)$<var>
is either:- a name for a parameter that is linked to a parameter of the same name in
<pattern to define>
, or - the symbol
_
(specifying to link whatever parameter is left to link)
- a name for a parameter that is linked to a parameter of the same name in
<n>
is a number specifying what parameter to link to- ex:
(($(add (1) and (2) to ($(a) as 3) plus $b) as $#1 $a $#0 $_) => <...>) ($x $y $z $w => x + y + z + w)
where<...>
=add 1 and 2 to 3 plus 4
=>($x $y $z $w => x + y + z + w) 2 3 1 4
=>2 + 3 + 1 + 4
- if we
let f = $x $y $z => <...>
then$a as f #_ #_
<=>$a as f _ _
- note: this equivalence does not hold for
$a as paused f #_ #_ #_
and$a as paused f _ _ _
- note: this equivalence does not hold for
- for more info on
#<pattern def>
, see "Example code snippets" §4
<constructor pattern>
is like<pattern to match>
except#
is not allowed and it works a bit differently- i.e. if
Player $health $max_health
is defined, thenPlayer 50 100 constructed using $constructor
defines$constructor
asPlayer
Player 50 100 constructed using Player $a $b
also works, but defines nothing
- i.e. if
- patterns named
_
(written like($_ as <...>)
) are called unnamed patterns and will not be defined
-
Call structure for ex. def.
$(example pattern taking (_ as _) and (_ as _)) as _
:- mixfix ex:
example pattern taking (x) and (123)
- prefix ex:
(example pattern taking $_ and $_) x 123
$_
is here used as an unnamed pattern since_
- mixfix ex:
-
Patterns are defined within the scope described by the pattern parsing algorithm.
-
Patterns can only be defined within
<input pars>
.- If it looks like a pattern is defined outside, it's actually a partially applied pattern
-
Patterns, like functions, can be partially applied.
- This can be done by leaving out some of the
<input args>
or by having placeholders for args using$(<placeholder pattern>)
<placeholder pattern>
has the same syntax as<pattern to define>
, but doesn't define the pattern- Given a defined pattern
$($a f $b $c)
:- ex. of leaving out args:
f
,1 f
,f 2
,1 f 2
- ex. of having placeholders:
$x f $y $z
,1 f $y $z
,$x f 2 $z
,$x f $y 3
,1 f $y 3
- ex. of both:
1 f $y 3
- ex. of leaving out args:
- This can be done by leaving out some of the
-
Patterns only consisting of names are called variables.
- This means variables are a subset of patterns in Triforce
- Whenever we talk about patterns, we're including variables
- I.e.
$(example pattern)
is a variable, while$(example pattern taking $x)
is not
- This means variables are a subset of patterns in Triforce
-
Patterns can be placed anywhere functions can as long as they become a function after full evaluation.
- This means that anywhere it says
<function>
in this README it's actually<function/pattern-becoming-function>
- TODO: check if there are any exceptions to this
- This means that anywhere it says
- Choose a
$(<...>)
and move to its outside.- NOTE: If the pattern is after 'as', choose a
#(<...>)
here instead (? TODO)
- NOTE: If the pattern is after 'as', choose a
- If inside another
$(<...>)
, move to its outside, and then keep leaving scopes until you findas
. If not, keep leaving scopes until you find=>
. - Your pattern is defined after this
as
or=>
.
-
When a pattern name is used, the compiler will try to find a matching pattern definition. If it can't find any match, it reports a compile time error.
-
If 2 or more variables in the same function have the same name they will be pattern matched to be equal.
- i.e. in
$x $y $x => <...>
the 2x
s must be equal - the equality function
==
is defined using this
- i.e. in
-
An and/or-pattern,
a|b|...|z [& <or-pattern>] [...]
, can be used in pattern matching.a|b|c
<=>a|a|b|c|b|a
<=>c|a|b
a
,b
,c
,a|b
,a|c
anda|b|c
all matcha|b|c
a|b|c
,a|b
anda|c
may matcha
,b
orc
(anda|b|c
may matcha|b
)- by default this will compile and instead pattern matching will be completed at runtime; if this fails, the program crashes
- if using
prerun
this counts as failing to match and won't compile
a|b|c & b|c|d
<=>b|c
- all matching involving and-patterns is done after the and-patterns have collapsed into or-patterns
- and/or-patterns may contain the "not" (exclusion) operator
~
- ex:
a|b|c & ~c
<=>a|b
- ex:
a|b & ~c
<=>a|b
- note:
<or-pattern> & ~Undefined
is not equivalent to<or-pattern>
, see "Pattern matching" §6
- note:
- ex:
- side-note: and/or-patterns are similar to intersection/union types in other languages
- or-patterns are lazy
-
...
is used in (and/or-)patterns to let the compiler figure out the rest.- ex:
0|1|...
is an or-pattern consisting of all integers >= 0
- ex:
-
Pattern matching is done at compile time whenever possible. If pattern matching can't be completed during compile time, it will continue during runtime.
prerun
can be used to override this and force pattern matching to be completed during compile timerun
can be used to override this and force pattern matching to only be done during runtime
-
The special
Undefined
value matches any pattern.- i.e.
$x as 123
<=>$x as Undefined|123
- note that while this will compile, if
x
isUndefined
but still used during runtime the program will crash
- note that while this will compile, if
$x as Undefined
specifies thatx
must beUndefined
- useful for expressions returning an infinite loop:
forever {<...>}: Undefined
- useful for expressions returning an infinite loop:
$x as ~Undefined
overrides this and specifies thatx
must not beUndefined
- useful for ensuring something is total:
this_should_be_total: ~Undefined
- useful for ensuring something is total:
- i.e.
- Partially applied functions and patterns are treated as values.
- called objects when of the form
<Capitalized Name> [<pars>] ()
- called objects when of the form
- There exists a special
Undefined
value, which will be inserted into compilation or-patterns whenever a value might not come to exist during runtime. This happens if:- the program specifies it might crash during runtime
- ex:
let x = rand any Int; if x == 1337 {panic!}; x
returnsUndefined|(any Int)
during compilation
- ex:
- the compiler suspects there is an infinite loop in the program
- ex:
let x: auto = 0; forever {x++}; x
returnsUndefined|1|2|...
during compilation - note that due to the halting problem the compiler may think there is an infinite loop when there isn't, hence 'suspects'
- ex:
- for more info, see "Example code snippets" §1
- the program specifies it might crash during runtime
- There are no other values.
- Numbers, strings, etc. are defined as partially applied functions or patterns
- Values are differentiated using pattern matching (as described under "Patterns" and "Pattern matching").
- Pseudo-values are similar to values but act a bit differently, and include:
- and/or-patterns
- ex:
1|2|3 & 2|3|4
- values and 1-value ("singleton") and/or-patterns are equivalent
- ex:
456
is both a value and a 1-value and/or-pattern
- ex:
- ex:
- placeholder-values
- ex:
$x
(outside of pattern definition)
- ex:
- and/or-patterns
- Pseudo-values may be created by the programmer and/or the compiler.
- programmer ex:
- or-patterns:
random value in range 0|1|...
- placeholder-values:
$n+1
- or-patterns:
- compiler ex:
- or-patterns: creates an or-pattern for what
(123|456|789) + 1
returns:124|457|790
- placeholder-values: inserts missing placeholder in
+1
by converting to$n+1
- or-patterns: creates an or-pattern for what
- programmer ex:
- Pseudo-values can be placed anywhere values can (if in the right situation).
- Pseudo-values only exist before runtime (during compilation).
- and-patterns collapse into or-patterns during compilation
- i.e.
1|2|3 & 2|3|4
collapses into2|3
- note:
<or-pattern> & ~Undefined
is an exception to this, see "Pattern matching" §6
- i.e.
- or-patterns (eventually) collapse into single values at runtime
- i.e.
1|2
collapses into either1
or2
- programs not allowing this to happen won't be accepted by the compiler (TODO: check if this works, maybe change to runtime crash?)
- i.e.
- placeholder-values are converted into something else at runtime (TODO: figure out what)
- and-patterns collapse into or-patterns during compilation
- And/Or-patterns are further described in "Pattern matching" §3
- Placeholder-values are further described in "Patterns" §5
- Triforce uses eager evaluation.
(<expr>)
returns whatever is left of<expr>
after evaluation to the outer scope.- The compiler will try to run as much as possible during compilation unless otherwise specified.
- There are 3 stages of evaluation:
- Patterns are "expanded"
- i.e. if we
let f = $x $y => <...>
thenf
is expanded tof $x $y
- i.e. if we
- Synonyms are deconstructed (see "Synonyms and Shadows")
- Function application is evaluated
- Patterns are "expanded"
paused
prevents evaluation from going past stage 1.
- 2 finally evaluated values are equal if they refer to the same named function and they have the same applied args.
- This means anonymous functions are incomparable
- A value is first finally evaluated when all synonyms have been deconstructed
- What it means for them to "refer" to the same function is that they are both partial applications of the exact same function
- See "Example code snippets" §5 for more info
- 2 placeholder-values are always equal.
- I.e.
$xyz == $abc
- I.e.
$x $y => <body>
and$x => $y => <body>
are always equal.- Note that this equality doesn't hold if
$y => <body>
is named though
- Note that this equality doesn't hold if
- Equality of and-patterns is decided after they have collapsed into or-patterns.
- Equality of or-patterns is either decided after collapse at runtime, or during compilation iff all below criteria are met:
- They consist of the same values in the same order
- They were both formed as a result of known-to-be-equal pseudo-values being combined in some way with known terms (see "Misc" §8)
- 2 values being equal and 2 values matching are related but not the same, see "Pattern matching" §2
- Comparison involving
paused
values will compare the values as if they were strings. - See "Example code snippets" §1 for an example of equality.
- 2 patterns
f $x
and$y g
are synonymous iff welet f $x = $y g
or the other way around.- We say that
f
is deconstructed tog
when evaluated- I.e.
f 123
becomes123 g
(if all patterns match) - Deconstruction keeps going on until an atomic pattern is reached
- I.e.
- If we instead
let f $x = $y => <...>
, thenf $x
gives the anonymous function$y => <...>
a name- We say that
f $x
is atomic
- We say that
- We say that
- A newly defined pattern will shadow another iff all below criteria are met:
- They have the same amount of parameters in the same order
- They have the same
<pattern to match>
:s in the same order - They have the same name
- Symbols part of the same symgroup that are next to eachother form a token.
- Every symindie is its own token.
- A symblock is a block of symbols enclosed by 2 enclosers, forming a token.
decl symgroup <symbol> [...];
declares a symbol group in the scope.\s
,\t
and\n
can be used as symbols
decl symindies <symbol> [...];
declares one or more symbol independents in the scope.decl symblock <name> enclosed by <encloser> <encloser> [with escaper <escaper>];
declares a symbol block in the scope.- all symbols are allowed for use as enclosers and escapers, but must form one token each
<name>
can also be anything forming one token- if the 2 enclosers are different, the symblock is automatically nestable
- There are 2 built-in symgroups: "default" and "whitespace".
- There are 2 built-in symindies:
(
and)
. - Symblock tokens without defined behaviour are ignored.
- define their behaviour using
func <symblock name> (implicitly unchecked paused) {<...>};
- define their behaviour using
- Tokens formed with the use of symgroups or symindies without defined behaviour will cause an error.
<n>{i|u}{8|16|32|64|128|size}
is the syntax for low level integers.i
= normal/signed int,u
= unsigned- the numbers after
{i|u}
specify the bit-sizesize
means pointer-size (usually 32- or 64-bit)- see this discussion on why we use bits rather than bytes
- ex:
1_234_567u32
- ex:
987_654_321isize
<n>f[loat]{32|64}
is the syntax for low level floating point numbers.- the numbers after
f[loat]
specify the bit-size - ex:
1234.567f64
- ex:
0x12float32
0x12f32
is instead a valid hexadecimal integer
- the numbers after
- Arithmetic operators are overloaded with built-in low level operations for low level numbers.
- i.e.
123isize + 456isize
is the low-level add operation, while123 + 456
may be defined to be something else
- i.e.
-><x>
is a pointer to<x>
(the address of<x>
)- a pointer is just a low level integer, but usually written in hex form (i.e.
0x123456789abc
)
- a pointer is just a low level integer, but usually written in hex form (i.e.
{1|2|4|8|16|size}@<addr>
gets data of the specified byte-size located at<addr>
.- we use bytes rather than bits here to make dereferencing easier
- makes it easier because pointer arithmetic uses bytes
- there will also be a higher level
@<ptr>
version in the future
- we use bytes rather than bits here to make dereferencing easier
__push_stack__ <x>
pushes<x>
to the stack.- TODO: do we need this? or is asm support enough?
__pop_stack__ <x>
pops from the stack into<x>
.- TODO: do we need this? or is asm support enough?
__asm__ (<assembly code block>)
allows you to write inline assembly.
-
$(<pattern to define>)
<=>($(<pattern to define>) as _)
<=>($(<pattern to define>) as $#0 [$#1] [...])
- If the pattern is a variable, this allows the input to be any kind of function, which you can call like
<defined pattern> [<arg1>] [<arg2>] [...]
- If the pattern isn't a variable, the amount of
$
afteras
will match the amount of parameters of the pattern- i.e.
$(pattern taking $x and $y)
<=>($(pattern taking $x and $y) as $#0 $#1)
- i.e.
- If the pattern is a variable, this allows the input to be any kind of function, which you can call like
-
(<named pattern>)
<=>($_ as <named pattern>)
<=>(_ as <named pattern>)
- Note that
$_
and_
are not generally equivalent; this is a special case - Allows for
() => <function body>
(becoming(_ as ()) => <function body>
)
- Note that
-
$(<variable to define>) as <pattern to match>
<=>$(<variable to define> _ [...]) as <pattern to match>
- i.e.
$b as Bool _
<=>$(b _) as Bool _
- i.e.
-
__caller_scope__
can be used to avoid making your program look like Lisp:(<input pars> => __caller_scope__) <input args> <rest of scope>
<=>(<input pars> ($__caller_scope__ as paused) => __caller_scope__) <input args> (<rest of scope>)
- Note that this should be used sparingly, and that the function evaluating
__caller_scope__
must be marked asunpredictable
- unless you're just returning
paused __caller_scope__
, in which case it's equivalent to returning nothing and doesn't need to be marked
- unless you're just returning
-
$(<pattern to define>) as implicitly paused [<pattern to match>] [becoming <pattern to match>]
can be used to delay evaluation of input until inside the scope where the pattern is defined:(($x as implicitly paused 1 becoming 2) => x) <expr>
<=>(($x as paused 1) => x: 2) (paused <expr>)
$<...> as implicitly paused [becoming <...>]
<=>$<...> as implicitly paused _ [becoming <...>]
-
decl $(<pattern to declare>) [...];
allows use of declared patterns before they have been defined.- Note that they must still be defined somewhere in the scope
- See "Example code snippets" §3 for an example
-
Exponentiation for fractional numbers:
123.45E-6
<=>0.00012345
, and123.45E+6
<=>123_450_000
.
__all_args__ <function>
returns all possible args that can be applied to the function.length >= 1
.- ex:
__all_args__ (f $n $str) == [any Nat, any String]
- except array is special:
f [any Nat, any String] == f (any Nat) (any String)
- except array is special:
__all_args_but_one__
does the same except doesn't apply the last arg- may be removed in the future in favor of using
__all_args__[..__all_args__.length - 1]
- may be removed in the future in favor of using
- ex:
__applied_args__ <function>
returns the args that have been applied to the function.length >= 0
.__set_attributes__ <attr> <id>
tells the compiler that<id>
has attribute<attr>
and returns<id>
.- precedence is specified using attributes
__assign__ <var> <val>
does unchecked assignment.__catch__
is a special function, more info in example 4.paused <expr>
pauses evaluation of<expr>
by returning<expr>
unevaluated.- similar to Lisp's
quote
, but is checked (unless you dounchecked paused
) - i.e. assuming
func f() {paused (1 + 2)};
, thenf() * 3
=>(1 + 2) * 3
=>9
- similar to Lisp's
raw <expr>
is identical to<expr>
except it's unhygienic.- i.e. assuming
func f() {paused raw (1 + 2)};
, thenf() * 3
=>1 + 2 * 3
=>7
- note: removes all all-encompassing parentheses, so even
raw (((((1 + 2)))))
becomes1 + 2
- it does this even for input
as implicitly raw
- it does this even for input
- i.e. assuming
listified paused <expr>
converts<expr>
to an AST in the form of a multi-dimensional list- each item in the list is either a token
String
or a list of tokens - the form of the AST will be stable once the language is stable
- note:
listified unchecked paused <expr>
produces an AST with the entire<expr>
as 1 token
- each item in the list is either a token
codified <AST>
converts<AST>
to paused codecodified (listified paused <code>)
<=>paused <code>
cloaked <pattern>
returns<pattern>
as anonymous.- ex:
cloaked (function taking $x and $y)
becomes$x $y
- ex:
continue matching [for <function>]
continues pattern matching if possible.- note: may be changed to
continue matching;
and be required to be put at end of function- the
for <function>
part could cause some issues and probably is unnecessary now when macros exist
- the
- if
<function>
isn't specified it will default to the current function - if
<function>
iscaller
it will continue matching for the caller - if it's not possible to continue, there will be an error
- note that
__catch__
can be used to prevent this
- note that
- note: may be changed to
_
is a special built-in symbol meaning different things in different contexts, but typically it means "anything".(<expr>)
always has higher precedence than<expr>
.- Number literals, char literals and string literals are built-in and bound to library implementations similarly to Agda.
- Precedence can be overriden using
##[precedence (below|above) <function>] <your function>
. - Single-line
//
and multi-line/* */
comments are built-in (to avoid issues with nested strings). Maximal munch
/Longest match
parsing is used to solve ambiguity (unless invalid; then context is used).- In case there's ambiguity between if a fully applied function or another partially applied function was intended, the compiler will assume the fully applied function was intended and give a warning about this.
- I.e.
if True then do_something
is assumed to mean the fully appliedif $cond then $body
function rather than a partially appliedif $cond then $expr else $expr
- I.e.
- An expression, or term, is said to be known to the compiler if the compiler sees it as a specific value rather than a pseudo-value.
caller
is a reserved keyword for the caller of the current function.- The file extension for the language is
.tri
. - Triforce allows ad-hoc polymorphism; you can create multiple functions with the same name but with different parameters.
- Meaning function & operator overloading is possible
More examples can be found in readme_examples.tri. There you can also find the source code for the pictured examples.
(array|list|pointer)[*<n>]
only [register|stack|heap] [volatile] [unique] [func] (array|list|pointer)[*<n>] [chan]
[register|stack|heap] [const|volatile] [unsigned|signed|fraction] number [func] [(array|list|pointer)[*<n>]] [chan]
only [register|stack|heap] [volatile] [unsigned|signed|fraction] number [func] (array|list|pointer)[*<n>] [chan]
[register|stack|heap] [const|volatile] [unsigned|signed] (int|char) [func] [(array|list|pointer)[*<n>]] [chan]
only [register|stack|heap] [volatile] [unsigned|signed] (int|char) [func] (array|list|pointer)[*<n>] [chan]
type <custom type> extends <type1>[`|`<type2>`|`<type3>...]
clang <type> <function name>([<parameters>]) { <C code> }
var`<type>`
var`<size>`
var`<memsize>`
var`<alignment>`
var`<scope>`
(default:1
)some_fraction`<precision>`
[NOTE: The precision value is the number of bits for the exponent, not the number of decimals]pointer_to_list`<length>`
[NOTE:some_list`<`<property>`>`
will not access the property of the whole list, but the properties of each item of the list.* Use->some_list`<`<property>`>`
instead. ]pointer_to_list`<separator>`
str`<encoding>`
(default:'utf-8'
)channel`<buffer>`
(default:1
)var>bit<
- You can assign properties at variable creation:
<type> [`<`<property1>`>`=<property1 value> `<`<property2>`>`=<property2 value>...] var
*This is because some_list`<`<property>`>`
decays into pointer_to_list[>>>]`<`<property>`>`
**
&
|
~
^
<<
>>
&=
|=
^=
<<=
>>=
@
>>>
<<<
in
(example:if(item in arr) ...
)
str[>>>] == "Test"
str[start >>> stop]
str == address
str[when <condition> >>> until <condition>]
pointer sublist -> some_list[start >>> stop]
pointer sublist2 -> some_list[when <condition> >>> until <condition>]
pointer new_sublist -> [1, 2, 3]
pointer new_subarr -> {1, 2, 3}
str[<<<] == "tseT"
str[stop <<< start]
match <var> { case <val1>: <code> [case <val2>: <code>...] [default: <code>] }
(equivalent of C'sswitch
)
"null terminated string"
'string size determined by <size> property'
'null terminated string, but <size> property can still be used to get size\0'
"null terminated string" == 'null terminated string\0'
return [from <function>] <value>
(NOTE: You can't return from just any function, it needs to call the function you're currently in either directly or indirectly)
repeat <n times> { <code> }
break [<value>] [from <function>]
async { <code> }
select { <cases> }
send <data> to <channel>
<type> <var> = receive from <channel>
__OS
__path
__args
__argc
__line
__item
__app
()
,[]
,.
,++
,--
!
,~
,(<type>)
,@
,->
,**
*
,/
,%
+
,-
>>
,<<
<
,<=
,>
,>=
==
,!=
&
^
|
&&
||
=
,+=
,-=
,*=
,/=
,%=
,**=
,>>=
,<<=
,&=
,^=
,|=
>>>
,<<<
,,
,in