My toy calculator language. The lexer was inspired by the text/template/parse package from the standard library and this talk by Rob Pike: Lexical Scanning in Go The parser is a custom recursive descent parser. The parsing support was also inspired by go/parser and text/template/parse.
###TODO
- Add more tests for parser
- Test error cases for parser and lexer
- Add better error messages to parser
- Add support for val declarations
- Add support for var declarations
- Add if expressions
- Add eval package and implement an interpreter
- Add character literals
- Add support for let statements
- Add "=>" token to lexer
- keep parsing expression if in a paren
- Add support for function literals
- Add support for function declarations
- Add support for lists
- Add datatypes
- Add references and probably some for of gc
- Add records or structs?
- Add doc comment support to parsing
- Add typing system(?) or go with dynamic typing
- Add pattern matching(?)
- Vendor dependencies(or remove them)
###Notes
- Identifiers can be alphanumeric with an underscore '_'
- Expressions can end with a ';' but ';' are not strictly necessary. They can be used to disambiguate certain expressions.
- Unary Expressions cannot span multiple lines
- Binary Expressions can span multiple lines only if line ends with operator
- Parenthesis allows expressions to span multiple lines until the parenthesis is closed
- Operator precedence are left binding and as follows:
Highest(5): *, /, %
(4): +, -
(3): ==, !=, <, >, <=, >=
(2): &&
Lower (1): ||
Lowest (0): anything else
e.g 4+2/3 == 4 + (2/3)
4-5+4%a+5 == ((4 - 5) + (4%a)) + 5
##Grammar in EBNF
literal = NUMBER
| IDENTIFIER
| BOOL
if_expr = "if" , bool_expr , "then" , expr , "else" , expr "end"
num_expr = "+" , expr
| "-" , expr
| expr , "+" , expr
| expr , "-" , expr
| expr , "*" , expr
| expr , "/" , expr
| expr , "%" , expr
bool_expr = "!" , expr
| expr , "&&" , expr
| expr , "||" , expr
| expr , "==" , expr
| expr , ">" , expr
| expr , "<" , expr
| expr , "!=" , expr
| expr , ">=" , expr
| expr , "<=" , expr
tuple_expr = "(" , expr , "," , expr , { "," , expr } , ")" # n > 1
function = "fn" , "(" , ident_stmt , ")" , "=>" , expr , "end" # remove end keyword ?
block = expr , { ("\n" | ";") , expr}
let_expr = "let" , val_decl , "in" , expr , "end"
expr = literal
| num_expr
| bool_expr
| if_expr
| "(" , expr , ")"
| tuple_expr
| let_expr
| block
| function
| func_apcl
func_apcl = (IDENTIFIER | function ) , "(" , expr , { "," , expr } , ")"
ident_stmt = IDENTIFIER
decl = val_decl
| var_decl
val_decl = "val" , ident_stmt , "=" , expr
var_decl = "var" , ident_stmt , "=" , expr
func_decl = "def" , IDENTIFIER , "(" , IDENTIFIER , { "," , IDENTIFIER } , ")" , "=" , expr , "end"
###Planned Extensions to grammar
- Add pattern matching to grammar
pattern = "(" , pat , "," , pat , { "," , pat } , ")" # n > 1
| literal
val_decl = "val" , pat , "=" , expr
##Dependencies Depedencies are kept to a minimum.
# Used for testing and debugging
go get github.com/davecgh/go-spew/spew