aldana-lang
Make Arduino Programming easy for Students.
Aldana Lang is a superset of C/C++ Arduino. Like Typescript that compiles (transpiles) to javascript, Aldana compiles to C/C++ for Arduino.
The main propose of this project is for have a high-level language for Arduino. The direction of Aldana is something like Javascript for the functional programming (literal objects, const & let, etc), Golang/Typescript for type assignation and interfaces and classes creation, and a little bit of Python.
An example
import "./utils"
# Sums two given numbers
func sum(a: int, b: int): int {
return a+b
}
var result = sum(2, 2)
print(intToStr(result))
#include "./utils.cpp"
// Sums two given numbers
int sum(int a, int b) {
return a+b;
}
int main(){
int result = sum(2, 2)
print(intToStr(result))
return 0;
}
Goals
- No need parenthesis for evaluate expression in statements at least it for setting order.
- No need semicolon at the end of the line.
- Coercion (or relative) when declare variables.
- Using special keyword for manipulate pointers.
- Accepts many ways to write the same things.
Syntax Reference
The following tables represents the transpilation of Aldana to C/C++.
Primitive Types
Type | Transpile |
---|---|
int | C Integer |
float | C Float |
str | C Char[] |
char | C Char |
bool | C/C++ Boolean |
byte | Typedef uint8_t byte |
Primitive Plural Types
Type | Transpile |
---|---|
ints | C Integer[] |
floats | C Float[] |
strs | C Char[][] |
chars | Aldana Str |
bools | C Bool[] |
bytes | Defined byte[] |
Primitive Plural Types as Array Of
Type | Transpile |
---|---|
array of int | C Integer[] |
array of float | C Float[] |
array of str | C Char[][] |
array of char | Aldana Str |
array of bool | C Bool[] |
array of byte | Defined byte[] |
Types Definition
Type | Transpile |
---|---|
object | C Structure |
interface | C++ Abstract Class with only pure virtual methods |
class | C++ Class |
type | C Typedef |
Modularization
Expected a string path, no need for extension.
Keyword | Transpile |
---|---|
import | C #include as source |
include | C #include as header |
Arithmetical Operators
Operator | Transpile |
---|---|
+ | C Plus Operator |
- | C Minus Operator |
* | C Multiply Operator |
/ | C Division Operator |
// | C Division Operator with Integer casting result |
^ | Internal Calculation, Pow() from Math.h, Literal Multiplication |
mod | C Module Operator |
abs | C Macro for ABS ABS(N) N < 0 ? -N : N |
percent | C Macro for PERCENT PERCENT (N, P) N*(P/100) |
Macros
Keyword | Transpile |
---|---|
def | C #define |
Booleans
Keyword | Transpile |
---|---|
true | C++ True |
false | C++ False |
Logic Operators
Also can be used the C logic operators
Keyword | Transpile |
---|---|
and | C && |
or | C || |
is, equal | C == |
not | C ! |
is not | C != |
greater | C > |
less | C < |
greater or equal | C >= |
less or equal | C <= |
Pointer
Keyword | Transpile |
---|---|
ptr | C & operator |
val | C * operator |
var bar: int = 12
var foo: prt of bar
var zaa: val of foo
Transpiled to C.
int bar = 12 // 12
int *foo = &bar // 0xbfff
int zaa = *foo // 12
Byte
The byte starts with 0x
following by the hex number, 0x0A
.
Comments
The comments are transpiled to C-like comments.
# Single Line Comment
#-
Multiline Comment
#
// Single Line Comment
/*
Multiline Comment
*/
Variables
Const variables. It's mandatory assign some value.
const foo = 1234
const foo: float = 12.0
const foo = [123, 12121, 4568]
const foo: bools = [false, true, false]
const foo: array of str = ["Hello", "World"]
Non-const variables. It's mandatory assign the type when there is no value assignation.
var foo: int
var foo: float = 12.0
var foo = [123, 12121, 4568]
var foo: bools = [false, true, false]
var foo: array of str = ["Hello", "World"]
Condition
if expression {
} elif expression {
} else {
}
Foreach
Foreach loop allows to itering an array by each element.
foreach element in array {
}
Transpiled to C
for(uint i=0, l=sizeof(array)/sizeof(array[0]); i<l; i++) {
array[i]
}
Forange
Forange loop allows to itering by a range of numbers.
forange of 1 upto 9 as i {
}
Transpiled to C
for(uint i=1, i<=9; i++) {
}
That loop can be short as. Starts at zero.
forange upto 9 as i {
}
Transpiled to C
for(uint i=0, i<=9; i++) {
}
Forlen
Forlen loop allows to itering an array by its len.
forlen of array as i {
}
Transpiled to C. Same as foreach.
for(uint i=0, l=sizeof(array)/sizeof(array[0]); i<l; i++) {
array[i]
}
While loop
while expression {
}
Transpiled to C.
while (expression) {
}
Repeat loop
Allows to repeat a block of code a number of times.
repeat times {
}
Transpiled to C.
for (uint i=0; i<=times; i++){
}
Also can be use as
keyword for know which iteration is the current.
repeat times as i {
}
Transpiled to C. The transpiler will aware the i
variable is usable.
for (uint i=0; i<=times; i++){
}
Define Macro
def {
name: value,
name(param): expression,
name(param): {
multiline_expression
}
}
Transpiled to C.
#define name value
#define name(param) (expression)
#define name(param) {\
multiline_expression\
}
Functions
func foo(arg1: int): int {
return arg1
}
Transpiled to C.
int foo(int arg1) {
return arg1
}
Inline functions
func foo(arg1: int): int => arg1
Transpiled to C.
int foo(int arg1) {
return arg1
}
How Aldana works?
There're three parts: a Lexer, a Tree-Nodes (ALD) and C/C++ Parser.
The first one has the job of generate tokens; the second one converts those contextless tokents into Tree-Nodes (called ALD) given them a context; the last one consume that ALD and parse its nodes into C/C++ code.
The cool thing here is the resulting transpilation only depends on the parser, thus changing the parse it will result into another kind of transpilation, either for another language or even the same language but different flavor.
Ald-Types
Those are the type of tokens and nodes of Aldana Lang. It's like this because short a lot of code, otherwise it would have be relating each token type with each node type.
Token
It's the minium part of the lang, it has three properties:
- Type
- An Ald-Type that serve as Family Type.
- SubType
- An Ald-Type that serve as Sub-Type that belong to a Type Family.
- Value
- The bytes that represent the token or nothing.
The 'Type Family' is used to agroup releated tokens such as: Keywords, Aritmethic Operators, etc.
The 'Sub-Type' is a subset of 'Type Family' such as: Const, Var, etc.
The 'Value' are the original bytes of the Token, however in some case this property is empty because the Type or SubType describes explicit the Token value such as: Artimethic Operator->Plus Sign.
Lexer
The Lexer reads each byte of the Aldana Source Code, and by the 'Lexer Token Rules' creates the token or even ignore the byte (white-space for example).
In case some byte it's not contemplated, the lexer ends at Invalid Code State. Otherwise, retrive a list of tokens.
Tree-Node (ALD)
ALD has the responsability to add context to a list of tokens. That's because that list doesn't make any sense, for example: [Keyword: const][Identifier: foo][Equal][String: Hello World]
. Of course, if some human read that list makes all sense, but not for the computer, because the context and interpretation of that context is given by the human mind. The computer doesn't difference between that list or this one: [String: Hello World][Identifier: foo][Equal][Keyword: const]
. To the computer, those list are just that: two list with the same number of tokens.
So, ALD reads those tokens and try to make them sense. In more deep understanding, ALD identfies a Token, then evaluate which context must use for that token, for example: [Keyword: const]
belongs to 'Const Variable Declaration Context', which it has Syntax Rules (the aldana-lang syntax rules!), with those can be determinate not only if there is syntax erros but also the Node properties.
Node
It's a superset of tokens, which have a specific token.
Think the Node as XML representation: <Const name="foo" value="Hello World" type="String"/>
Another example with a condition:
<Condition>
<If expression="expression">
<Then>
...
</Then>
</If>
<Elif expression="expression">
<Then>
...
</Then>
</Elif>
<Else>
<Then>
...
</Then>
</Else>
</Condition>
Parser
The Parser is responsable for convert (parse) the ALD result into code of specific language (in this case C/C++). Thinking the ALD as XML, is easy to imagine how to do the convertion.
To finish just, the Parser relates the nodes to some kind of templates for its specific lang, and result into a transpiled code.
Pseudocode for illustration:
const condition = ALD.parse(`
<Condition>
<If expression="expression">
<Then>
...
</Then>
</If>
<Elif expression="expression">
<Then>
...
</Then>
</Elif>
<Else>
<Then>
...
</Then>
</Else>
</Condition>
`);
const transpiled =
`if (${condition.if.expression}){
${condition.if.then};
} ${condtion.elif &&
`else if (${condition.elif.expression}){
${condition.elif.then};
}`} ${condtion.else &&
`else {
${condition.else.then};
}`}`;