# Lettuce
Everything is an expression
Commands are inputs for pipes
types: int, float, string, block
This "# is a comment"
Module and imports
```
mod new_module # Declares a module in the path
use new_module::a # Statement to make function available in this file
use new_module::{b, c} # Statement to make function available in this file
```
Value declarations
```
let a = 2
let b = 3
let c = $a + $b
```
Comparisons
```
$a || $b
$a or $b
```
```
$a && $b
$a and $b
```
```
let a = command val --flag -f
| command b # This comment gets ignored
| command c
```
```
alias a = command val --flag -f
```
This is a block and it cannot be stored
```
{
1 + 2
}
```
But it has a return value
and can be stored
```
let a = {
1 + 2
}
```
This is a closure and can be stored and used in commands
```
let d = {||
}
```
```
let b = command a | command c | command $d
let b = (command a | command c | command d)
```
```
let c = "this is a string"
let d = """this is a "raw" string"""
```
String interpolation
```
let e = f"this: {$a}"
let f = f"""this: {$a}"""
```
This becomes a list of strings
```
let e = [bare words are strings]
$e.1 # returns words
```
This becomes a table
```
let f = [[a, b]; [1 2] [3 4]]
$f.0 # returns [1 2] (row)
$f.a # returns [1 3] (col)
```
a function definition
```
def inner(a: int, c: string) -> string {
# Single if is an statement
if (true) {
return
}
}
```
if/else is an expression and both should return the same value type
```
let a = if true {
1
} else {
2
}
```
Return type should match (type checker)
```
def output() -> string {
"value"
}
```
```
'name while true {
if true {
break 'name
}
}
```
for loops over elements in list
```
for i in 1..20 {
if (i == 3) {
continue
}
if (i > 10) {
break
}
}
```
# Grammar
```
program -> declaration* EOF
// Declarations
declaration -> modDecl
| funDecl
| varDecl
| modImport
| pipeline
modDecl -> "mod" IDENTIFIER
funDecl -> "def" function
varDecl -> "let" IDENTIFIER ( "=" expression )?
modImport -> modImportSingle | modImportMulti
// Statements
pipeline -> exprStmt
| whileStmt
| forStmt
| ifStmt
| returnStmt
| breakStmt
| continueStmt
| block
exprStmt -> expression
whileStmt -> "while" "(" expression ")" statement
forStmt -> "for" primary "in" array|range closure
ifStmt -> "if" "(" expression ")" statement
("else" statement)?
returnStmt -> "return" expression?
breakStmt -> "break"
continueStmt -> "continue"
block -> "{" declaration* "}"
closure -> "{|" IDENTIFIER+ ( "," IDENTIFIER ) "|" declaration* "}"
// Expressions
expression -> assignment
assignment -> pipe ( "=" pipe | block | closure )?
pipe -> logic_or ( "|" logic_or )*
logic_or -> logic_and ( ( "or" | "||" ) logic_and)*
logic_and -> equality ( ( "and" | "&&" ) equality)*
equality -> comparison (( "==" | "!=" ) term)*
comparison -> term (( ">" | ">=" | "<" | "<=") term)*
term -> factor (( "-" | "+" ) unary)*
factor -> unary (( "/" | "*" ) unary)*
unary -> ( "!" | "-" ) unary | call
call -> primary ( (" " arguments? " ") )*
primary -> "true" | "false" | "$nothing"
| INTEGER
| FLOAT
| STRING
| RAW_STRING
| VALUE
| RANGE
| ARRAY
| TABLE
| IDENTIFIER
| "(" expression ")"
// Utility rules
modImportSingle -> "use" IDENTIFIER "::" IDENTIFIER
modImportMulti -> "use" IDENTIFIER "::" "{" IDENTIFIER ("," IDENTIFIER)+ "}"
function -> IDENTIFIER "(" parameters? ")" block
parameters -> IDENTIFIER ( "," IDENTIFIER )*
arguments -> expression ( " " expression )*
// Tokens from Lexing
INTEGER -> DIGIT+
FLOAT -> DIGIT+ "." DIGIT+
STRING -> "\"" <any char except "\"">* "\""
RAW_STRING -> "\"\"\"" .* "\"\"\""
FLAG -> "-" "-"? ALPHA+
VALUE -> "$" IDENTIFIER
IDENTIFIER -> ALPHA ( ALPHA | DIGIT )*
RANGE -> DIGIT* ".." DIGIT*
ALPHA -> "a" ... "z" | "A" ... "Z" | "_"
DIGIT -> "0" ... "9"
// Collections
PRIMITIVE -> INTEGER | FLOAT | STRING | IDENTIFIER
ARRAY -> "[" PRIMITIVE+ ( ","? PRIMITIVE )* "]"
TABLE -> "[" "[" IDENTIFIER ( ","? IDENTIFIER )* "]:" ARRAY* "]"
```