github.com/agustin-del-pino/aldana-lang

Make Arduino Programming easy for Students


License
MIT
Install
go get github.com/agustin-del-pino/aldana-lang

Documentation

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

  1. No need parenthesis for evaluate expression in statements at least it for setting order.
  2. No need semicolon at the end of the line.
  3. Coercion (or relative) when declare variables.
  4. Using special keyword for manipulate pointers.
  5. 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.

Diagram

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};
}`}`;