Skip to content

coolandcodes/antro

Repository files navigation

antro

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.

FrontEnd Design

  • 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)

Backend Design

No yet determined.

What is a Regular Grammar ?

  • 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).

What is a Context-Free Grammar ?

  • 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.

Sample program written in antro

	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.

About

Module/File Imports

The require keyword is used to require/import a module (folder of source files) or a single source file as an implicit dependency.

Entry Point Definition

The begin keyword is used to defined the entry point of the antro program. It is truncated by the end keyword.

Other Definitions

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.

Variable Creation

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.

Exception Handling - Part 1

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.

Exception Handling - Part 2

The or_panic keyword is the antro equivalent of panic keyword in Golang.

Invaraints

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.

Defering Action

The defer keyword is the antro equivalent of the defer keyword in Golang.

Outputs

The retn keyword is used to return a value from a function definition or begin block.

License

This is released under the MIT license.

Design Inspiration

Antro language design was inspired by Go, Rust, Python and JavaScript all combined.

About

This is a toy parser for an experimental programming language

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages