The past few months I've been working on my own programming language, called Marta. This is one of my many iterations of implementing a working PL, and so far I'm happy enough to share with you my progress. With this post I expect to share briefly some of my design decisions whilst working on the language, as well as what the future holds for my project.
The syntax
If you are familiar with Scala or Rust, Marta's syntax will be no stranger for you. Take the following snippet as an example:
let baz: Int = 20;
let foo: Int = |a| => { a + baz };
println(foo(10)) // 30
As you may intuitively deduce, the code above assigns baz of type int, a value of 20. Followed by a declaration of lambda foo, which receives a as a parameter and sums them both. Finally, it prints the result.
Pretty simple, isn't it?
Parsing
In initial stages, I opted for writing my own parser and lexer however, I intended Marta's syntax to be strictly typed for now. That is, one should indicate the type of an expression (also known as ascription in the literature) during its initialization. This implied the lexing process to be troublesome when writing more complex expressions. I opted for lalrpop as my main tool to generate the parser.
The AST is generated from the following grammar:
pub Stmt: Stmt = {
<expr:Expr> ";"? => Stmt::Expr(expr),
"let" <name:Ident> ":" <t:Ascription> "=" <rhs:Expr> ";"?
=> Stmt::Assign(name, rhs, t),
<variable:Ident> "=" <rhs:Expr> ";"
=> Stmt::ReAssign(variable, rhs),
"fn" <i:Ident> <args:ParamList> "=>" <return_type:Ascription> <stmts:Block>
=> Stmt::Func(i, args, stmts, return_type),
...
}
pub Ascription: Ascription = {
"int" => Ascription::Int,
"bool" => Ascription::Bool,
"string" => Ascription::Str,
...
}
pub Expr_: Expr = {
...
<f:Ident> "(" <args:ExprList> ")"
=> Expr::Call(Call::Function(Function {func: f, args: args})),
<i:'reduce'> <list:Expr_>
=> Expr::Call(Call::Function(Function {func: i.to_string(), args: vec![ list ]})),
"|" <args:ParamList> "|" "=>" <stmts:Block>
=> Expr::Function(args, stmts),
...
}
Definitely some improvements can be made.
Type checking
As of today, Marta implements basic type checking in which the evaluation of primitives such as int, bool and string is trivial. If by any chance an incorrect operation is performed, the program will panic.
It's within my plans to implement a Hindley-Milner type system for the language, this way explicit type annotation will not always be necessary and the language would feel more natural and expressive.
Conclusions
Writing my own programming language has been a beast of a task. I'm glad how this project is coming along. Learning about type systems has been a complete joyful experience, although beginner friendly material is hard to find, one can make its own way to grasp the basics little by little. I'm still not convinced with some design decisions I made, for example: error handling lacks of some edge cases; the way the AST is being interpreted is perhaps not the most efficient way. I'll be doing some research to improve these funcionalities in the meantime. With that being said, Marta is so far the project I'm mostly proud of. As next steps you can expect some nice features in the short-mid term: a hindley milner type system, expand the standard library with some built-in methods, a backend compiler.
1: Types and Programming Languages by Benjamin Pierce