This is a toy parser for an experimental programming language called antro scripting language which is still in development. This project is purely educational (for now) as the language cannot be used for any industry work in its current form. Therefore, the sole aim of this project is to show and teach the skills required of designing computer languages and implementing them.
The Regular Grammar as details for the Tokenizer and the Context-Free Grammar as production rules for the Parser (written in EBNF format) as well as other details of the algorithm used to implement the recursive descent strategy of the parser.
- Tokenizer (lexical analysis)
- Parser (semantic analysis)
- Joiner (an atomic message queue for passing tokens from the Tokenizer to the Parser with a lookahead of 1)
No yet determined.
- A regular grammar is the set of all strings generated by a grammar which all contain characters of the alphabet (or other single-character symbols) as defined by that grammar and which result to tokens (terminal symbols).
- A context-free grammar (CFG) is a set of recursive rewriting rules (or productions) used to generate patterns of strings. A CFG consists of the following components: a set of terminal symbols, which are the characters of the alphabet that appear in the strings generated by the grammar as well as a set of non-terminals.
require: "sys.module";
require: "errors.module";
require: "logging.module";
require: "types.module";
def: MAX 200;
begin: (void)
--# A novel programming language design for error handling (antro)
--# This uses chained exceptions behind the scenes (within the runtime).
var error = call: error("Program crashed");
var ty = call: factorUpBy2(MAX) |: or_throw error |: hook {
if (error.isThrown) {
logput(error.message + error.unrolledCauses)
}
print "An error occurred";
};
print ty;
end;
def: factorUpBy2(x){
var y, g = true;
if (x > 0) {
y = (x / 2) * 4;
} else {
g = false;
}
y = call: convertToFactor(g, x);
retn y;
};
def: convertToFactor(c, d){
var error_message_prefix = "Argument type error: ";
--# before the `retn` statement below executes...
--# ... we call the invariants below 👇🏾👇🏾
defer |: invariants {
error_message_prefix + "calling `convertToFactor(..)`",
call: type(c, "number") |: or_throw $;
error_message_prefix + "calling `convertToFactor(..)`",
call: type(d, "number") |: or_throw $;
}
retn c * d;
};
Though the above program doesn't do anything useful for now (i.e. the parser as currently written does not yet produce an Absract Syntax Tree - AST nor does it provide an Immediate Representation - IR), one can still get to understand the basics of what's going on.
The require
keyword is used to require/import a module (folder of source files) or a single source file as an implicit dependency.
The begin
keyword is used to defined the entry point of the antro program. It is truncated by the end
keyword.
The def
keyword is used to define variables in the global scope (i.e. outside functions) that cannot be changed. When using def
, it doesn't matter if the variable is defined in a global scope or local scope, it will always be a globally-scoped variable. Also, variables created with the def
keyword cannot have their values changed/mutated but only copied into a variable whose value can be changed/mutated.
The var
keyword is used to define variables or functions within a local scope (i.e. within functions) only. When using the var
keyword, it matters that it isn't used in a global scope (i.e. outside functions) else the antro parser will throw a parse error. Also, variables created with the var
keyword can have their value changed/mutated.
The or_throw
keyword is the antro equivalent of a catch block in other scripting languages like JavaScript. Antro does not directly use the try/catch model for error handling. It uses an error to catch other errors that occur higher up on the call stack. In this way, the try/catch block is abstracted away from the programmer and handled by the antro compiler and runtime.
The or_panic
keyword is the antro equivalent of panic keyword in Golang.
The invariants
keyword is used to setup invariants within a local scope (i.e. within functions). For the design of antro, i believe that invariants ought to be baked into the programming model (i.e. the programming language). In the future, i plan to setup macros just like they are used in Rust to make the invariants
block shorter and more compact. All function definitions MUST contain an invariants
block else the antro runtime will throw an error.
The defer
keyword is the antro equivalent of the defer keyword in Golang.
The retn
keyword is used to return a value from a function definition or begin
block.
This is released under the MIT license.
Antro language design was inspired by Go, Rust, Python and JavaScript all combined.