Syntax

Lamb's syntax most closely resembles that of the Haskell-like languages, but introduces a more familiar C-style like feel through its use of blocks and brace delimiters.

Scripts are stored in .lb or .lamb files. These files are then fed from the lexer to the parser, and from the parser to the compiler to be turned into bytecode.

Comments


Lamb only features lime comments, and these begin with a --:

-- This is a line comment

Reserved Words


Lamb doesn't feature many reserved words:

fn case if elif else return struct union struct from import let def

So... just don't go using these for variable names.

Identifiers


Identifiers in Lamb are able to be a series of numbers, letters or underscores. Ergo, the following are possible identifiers:

x
_
X_123
x_123
_x_123  

Identifiers are case sensitive in Lamb, though choosing between camelCase and snake_case is a matter of personal preference.

Top-Level Definitions

Top level definitions are bindings written in the outermost scope, and they begin with the def keyword. Top level definitions are required to have a type annotation.

def answer: int = 42; 
def greeting: list[usv] = "hello there"; 

Blocks


Blocks in Lamb are denoted by { } and are allowed to contain any series of statements followed by an expression. In fact, blocks themselves are expressions, and when the final item in a block is a statement, they evaluate to nil. Blocks can also contain local definitions, denoted by the let keyword. Locals don't need type ascriptions.

def block: list[usv] = {
  print("hello");
  print(" world");
  println(" I am writing from Lamb!");
  let local := "And the value of this block is this string!";
  local
}; 

Function Syntax


Function definitions in Lamb take the form fn(<args>) -> <expr>. This allows them to either be in the short form, like:

def inc := fn(x) -> x + 1;  

or the more familiar form (from the perspective of a C developer):

def inc := fn(x) -> {
  x + 1
};  

Because blocks are just expressions, you can omit a return statement in a block if you would like, though you can of course still use one:

def inc := fn(x) -> {
  return x + 1;
};  

Precedence and Associativity


Most operators in Lamb follow C, with the exception that the bit operators have higher precedence than the comparison operators.

OperatorsDescriptionPrecedenceAssociativity
- ~ !Negate, Compliment, Logical Not12Right
* / %Multiply, Divide, Modulo11Left
+Add, Subtract10Left
<< >>Left Shift, Right Shift9Left
&Bitwise And8Left
^Bitwise Xor7Left
|Bitwise Or6Left
> >= < <= = !=Comparison5Left
||Logical Or4Left
&&Logical And3Left
<. .>Left Compose, Right Compose2Left
<$ $>Left Apply, Right Apply1Left