Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7af4faa
doc: proposal new syntax
Jul 8, 2025
713b458
doc: list indexing
Jul 8, 2025
3897384
doc: simplify slice
Jul 8, 2025
fd31560
doc: class is assigned to as well
Jul 8, 2025
57e0b5a
doc: experiment with error handling syntax
Jul 8, 2025
3eaf918
doc: set of type conds is unordered
Jul 8, 2025
1b94595
doc: add reasoning for index notation
Jul 8, 2025
e4eafa1
doc: improve grammar and spelling in README
Jul 9, 2025
42cae17
doc: expand type refinement, add recover
Jul 9, 2025
36427b8
doc: class is set of statements
Jul 9, 2025
8968168
doc: add traits to Mamba
Jul 9, 2025
afbbb5d
doc: add trait as separate language construct
Jul 9, 2025
6ce42eb
doc: describe total functions
Jul 9, 2025
034b3d2
doc: add constant functions
Jul 9, 2025
63c9783
doc: Use linear algebra for examples
Jul 10, 2025
39535f2
doc: expand interweave total recursive calls
Jul 10, 2025
a80a9ac
doc: rewrite const to meta functions
Jul 11, 2025
bdd6588
test: add tests for README examples
Jul 11, 2025
4cb0e64
feat: simplify lex, remove comment token
Jul 11, 2025
5a6d6f2
feat: remove bitwise operators from grammar
Jul 11, 2025
03a8a88
[ci skip] test: rewrite test input
Jul 16, 2025
ca9775c
doc: elaborate notation
JSAbrahams Jul 11, 2025
93b422f
feat: remove unused tokens
Jul 21, 2025
d5bd621
doc: refine grammar further
Jul 27, 2025
3e14644
refactor: simplify parse call
Jul 27, 2025
ee95da2
feat: enhance code block,set and list,set parsing
Aug 3, 2025
6ce7f50
fix: bracket type tokenization
Mar 23, 2026
058b96f
fix: inline class def
Mar 23, 2026
b91e563
fix: bracket type direct function call
Mar 23, 2026
265f62a
doc: update year LICENSE
Mar 23, 2026
5aa2202
fix: allow pure in function definition
Mar 23, 2026
e2b83d4
test: control flow if stmt parsing
Mar 23, 2026
3961147
feat: printing of token name
Mar 27, 2026
f7f68a6
fix: right bracket type tuples
Mar 27, 2026
cbebe82
test: start introducing new grammar to test suite
Mar 27, 2026
7aa6c40
fix: allow newlines before expr or statement
Mar 28, 2026
12c7675
fix: parse class body
Mar 28, 2026
6240098
fix: parse import set
Mar 30, 2026
b92d38d
chore: add llvm build tools preview to nix
Mar 30, 2026
6455016
fix: multiple aliases for imports
Mar 30, 2026
6eedd04
feat: simplify grammar, class only requires where
Mar 30, 2026
5ae21c2
chore: bump toolchain 1.88 -> 1.94.1
Apr 13, 2026
c9d807e
feat: remove pass from language
Apr 13, 2026
b839cdc
fix: newlines in match
Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2025 Joël S. Abrahams
Copyright (c) 2026 Joël S. Abrahams

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
660 changes: 526 additions & 134 deletions README.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/features/safety/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ The first way of writing is preferred, as this more clearly separates the return
However, using Result may be better in some other situations. For instance, it allows us to use the type alias feature of the language, which can be convenient in certain situations, such as when we wish to enforce consistency.
See "Types" for a more in-depth explanation. A trivial case would be:

type MyResult <- Result[Int, MyErr]
type MyResult := Result[Int, MyErr]

def g (x: Int): MyResult := if x is 10 then MyErr("x was 10") else x
def g (x: Int): MyResult := if x is 10 then ! MyErr("x was 10") else x

Small side note: using the default behaviour feature of the language, we can rewrite `g` as such:

def g (x: Int): Int ! [MyErr] := x
def g (0) := ! MyErr("x was 10")
def g (x: Int): Int ! { MyErr } := x
def g (0) := ! MyErr("x was 10")

Note that if the signature of a function states that a certain type of exception is thrown, it must be thrown at some
point, or we will get a type error:
Expand Down
4 changes: 2 additions & 2 deletions docs/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

### [3.1 Grammar](grammar.md)

### [3.2 Keywords](keywords.md)
### [3.2 Special Characters and Symbols](reserved.md)

### [3.3 Special Characters](characters.md)
### [3.3 Standar Library](std/characters.md)
103 changes: 0 additions & 103 deletions docs/spec/characters.md

This file was deleted.

126 changes: 51 additions & 75 deletions docs/spec/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,129 +11,105 @@ The grammar of the language in Extended Backus-Naur Form (EBNF).
- ```{ ... }``` = zero or more

```ebnf
file ::= block
import ::= [ "from" id ] "import" id { "," id } [ as id { "," id } ]
file ::= { expr-or-stmt }
import ::= [ "from" id ] "import"
( import-as | "{" import-as "," "}" | "{" import-as "," import-as { "," import-as } "}" )
import-as ::= id ( "as" id )

type-def ::= "type" type [ ":" type ] ( newline block | "when" [ conditions ] )
conditions ::= ( newline indent { condition newline } dedent | condition )
condition ::= expression [ "!" expression ]
type-tuple ::= "(" [ type ] { "," type } ")"

class ::= "class" id [ fun-args ] [ ":" ( type | type-tuple ) ] ( newline block )
generics ::= "[" id { "," id } "]"
type-def ::= "type" type-not-fun ":" type-not-fun "when" ( expression | code-set )
trait-def ::= "trait" type-not-fun ( ":" type-not-fun { "," type-not-fun } ) [ ":=" statement ]
class-def ::= "class" type-not-fun [ fun-args ] [ ":" type-not-fun ] [ ":=" statement ]

id ::= { character }
id-maybe-type ::= id [ ":" type ]

type ::= ( id [ generics ] | type-tuple ) [ "->" type ]
type-tuple ::= "(" [ type { "," type } ] ")"

block ::= indent { expr-or-stmt } dedent
type-not-fun ::= id [ generics ]
type ::= id [ generics ] [ "->" type ]
generics ::= "[" id { "," id } "]"

expr-or-stmt ::= ( statement | expression ) [ comment ]
expr-or-stmt ::= ( statement | expression )
statement ::= control-flow-stmt
| definition
| reassignment
| type-def
| "retry"
| "pass"
| class
| type-def
| comment
| trait-def
| class-def
| import
expression ::= "(" expression ")"
| expression "?or" expression
| "return" [ expression ]
| expression "as" id
| control-flow-expr
| newline block
| code-set
expression ::= control-flow-expr
| collection
| index
| key-value
| operation
| anon-fun
| call
| "_"
| code-block

reassignment ::= expression ( ":=" | "+=" | "-=" | "*=" | "/=" | "^=" | ">>=" | "<<=" ) expression
call ::= expression [ ( "." | "?." ) ] id tuple [ "!" ] [ newline match-cases ]
reassignment ::= expression ( ":=" | "+=" | "-=" | "*=" | "/=" | "^=" ) expression
call ::= expression [ ( "." | "?." ) ] id tuple [ "!" match-cases [ recover expression ] ]
raise ::= "!" id { "," id }

collection ::= tuple | set | list | map
tuple ::= "(" { expression } ")"
set ::= "{" { expression } "}" | set-builder
set-builder ::= "{" expression "|" expression { "," expression } "}"
list ::= "[" { expression } "]" | list-builder
list-builder ::= "[" expression "|" expression { "," expression } "]"

slice ::= expression ( "::" | "::=" ) expression [ "::" expression ]
range ::= expression ( ".." | "..=" ) expression [ ".." expression ]
index ::= expression "[" expression "]"
# for all collections, we require one comma at least to avoid ambiguity
collection ::= tuple | set | set-builder | list | list-builder | map | map-builder
tuple ::= "(" "," ")" | "(" expression "," ")"
| "(" expression "," [ newline ] expression { "," [ newline ] expression } ")"
set ::= "{" "," "}" | "{" expression "," "}"
| "{" expression { "," [ newline ] expression } "}"
set-builder ::= "{" expression "|" expression { "," [ newline ] expression } "}"
list ::= "[" "," "]" | "[" expression "," "]"
| "[" expression { "," [ newline ] expression } "]"
list-builder ::= "[" expression "|" expression { "," [ newline ] expression } "]"
map ::= "{" expression "=>" expression "," "}"
| "{" expression "=>" expression { "," [ newline ] expression "=>" expression } "}"
map-builder ::= "{ expression "=>" expression | expression { "," [ newline ] expression } }

slice ::= expression ( "::" | "::=" ) expression
range ::= expression ( ".." | "..=" ) expression

definition ::= "def" ( variable-def | fun-def | operator-def )
definition ::= variable-def | fun-def | type-def | trait-def | class-def

variable-def ::= [ "fin" ] ( id-maybe-type | collection ) [ ":=" expression ] [ forward ]
operator-def ::= [ "pure" ] overridable-op [ "(" [ id-maybe-type ] ")" ] "->" type
[ ":=" ( expr-or-stmt | newline block ) ]

fun-def ::= [ "pure" ] id fun-args [ "->" type ] [ raise ]
[ ":=" ( expr-or-stmt | newline block ) ]
variable-def ::= "def" [ "fin" ] ( id-maybe-type | collection ) [ ":=" expression ]
# type checker should check for valid combination of meta, total, pure
fun-def ::= "def" [ "meta" ] [ "total" ] [ "pure" ]
( id | overridable-op ) fun-args [ "->" type ] [ raise ]
[ ":=" expression ]
fun-args ::= "(" [ fun-arg ] { "," fun-arg } ")"
fun-arg ::= id-maybe-type [ ":=" expression ]
forward ::= "forward" id { "," id }

anon-fun ::= "\" [ id-maybe-type { "," id-maybe-type } ] ":=" expression

operation ::= relation [ ( equality | instance-eq | boolean-logic ) relation ]
operation ::= relation [ ( equality | boolean-logic ) relation ]
relation ::= arithmetic [ comparison relation ]
arithmetic ::= term [ additive arithmetic ]
term ::= inner-term [ ( multiclative | range | slice ) term ]
term ::= inner-term [ ( multiplicative | range | slice ) term ]
inner-term ::= factor [ power inner-term ]
factor ::= [ unary ] ( literal | id | expression )

overrideable-op ::= additive | multiplicative | power | "=" | "<" | ">"
overridable-op ::= additive | multiplicative | power | "=" | "<" | ">"
unary ::= "not" | additive
additive ::= "+" | "-"
multiplicative ::= "*" | "/"
power ::= "^" | "mod"
instance-eq ::= "is" | "isa"
equality ::= "=" | "!="
comparison ::= "<=" | ">=" | "<" | ">"
boolean-logic ::= "and" | "or"

literal ::= number | string | "None"
literal ::= number | string
number ::= real | integer | e-notation
real ::= integer "." integer | "." integer | integer "."
integer ::= { digit }
e-notation ::= ( integer | real ) "E" [ "-" ] integer
string ::= """ { character } """

newline-block ::= newline block | expr-or-stmt
one-or-more-expr ::= expression { "," expression }
code-block ::= "do" expr-or-stmt { newline expr-or-stmt } "end"
code-set ::= "where" expr-or-stmt { newline expr-or-stmt } "end"

control-flow-expr::= if | match
if ::= "if" one-or-more-expr "then" newline-block [ "else" newline-block ]
match ::= "match" one-or-more-expr "with" newline match-cases
match-cases ::= indent { match-case { newline } } dedent
match-case ::= expression "=>" expr-or-stmt
if ::= "if" expression "then" expression [ "else" expression ]
match ::= "match" expression "with" map

control-flow-stmt::= while | foreach | "break" | "continue"
while ::= "while" one-or-more-expr "do" newline-block
foreach ::= "for" one-or-more-expr "in" expression "do" newline-block
while ::= "while" expression "do" expression
foreach ::= "for" expression "in" expression "do" expression

newline ::= newline-char
newline-char ::= \n | \r\n
comment ::= "#" { character } newline
newline ::= <platform dependent>
```

## Notes

An `expression` is used in a situation where an expression is required.
This allows the parser to short-circuit if something is definitely not an expression where it should be.
However, we cannot always know in advance whether something is an expression, e.g. when it is a function call.
Those cases should be verified by the type checker.
An `expr-or-stmt` may be used when it does not matter whether something is an expression or statement, such as the body of a loop.

We do not systematically desugar multiple delimited by commas, or a single expression, to tuples, as is the case in Python.
This prevents ambiguity in the grammar as specified above, and also prevents confusing situations such as `(0)` and `0` being equal.
Instead, we only do this in specific contexts, such as in the conditional of control flows.
Loading
Loading