# 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* "]" ```