Syntax and Grammar
This chapter covers the fundamental syntax and grammar rules of the Crush language.
Lexical Structure
Comments
// Single-line comment
/*
* Multi-line comment
* Can span multiple lines
*/
fn main() {
// Comments can appear anywhere
io.print("Hello"); // Including after statements
}
Identifiers
Identifiers (variable names, function names) must:
- Start with a letter or underscore
- Contain only letters, digits, and underscores
- Not be a reserved keyword
// Valid identifiers
let x = 1;
let my_variable = 2;
let _private = 3;
let counter123 = 4;
// Invalid identifiers
// let 123abc = 5; // Cannot start with digit
// let my-var = 6; // Hyphens not allowed
Keywords
Reserved keywords in Crush:
fn let mut if else
while for in return break
continue import export use as
true false null spawn yield
struct match try catch throw
capability async await lang
Literals
// Integer literals
let dec = 42;
let hex = 0x2A;
let bin = 0b101010;
// Float literals
let pi = 3.14159;
let sci = 1.5e-10;
// String literals
let s1 = "Hello";
let s2 = 'World';
let multiline = """
This is a
multi-line string
""";
// Boolean literals
let t = true;
let f = false;
// Null literal
let n = null;
Statements vs Expressions
Statements
Statements perform actions but don’t produce values:
// Variable declaration
let x = 42;
// Function definition
fn greet() {
io.print("Hello");
}
// Control flow
if x > 0 {
io.print("Positive");
}
// Expression statement
io.print("Hello");
Expressions
Expressions produce values:
// Arithmetic expressions
let sum = 5 + 3;
// Function calls
let result = calculate(10);
// Conditionals as expressions (future feature)
// let max = if a > b { a } else { b };
Semicolons
Semicolons are required at the end of statements:
let x = 42;
io.print(x);
return x;
Exception: The last expression in a block doesn’t need a semicolon:
fn add(a: Int, b: Int) -> Int {
return a + b; // Semicolon required
}
fn add_implicit(a: Int, b: Int) -> Int {
a + b // No semicolon (implicit return, future feature)
}
Blocks
Blocks are delimited by curly braces {}:
{
let x = 10;
let y = 20;
io.print(x + y);
}
fn main() {
// Function body is a block
let message = "Hello";
io.print(message);
}
if condition {
// If body is a block
io.print("True");
}
Variable Declaration
// Basic declaration
let x = 42;
// With type hint
let name: String = "Alice";
// Multiple declarations
let a = 1;
let b = 2;
let c = 3;
Function Definition
// Basic function
fn greet() {
io.print("Hello!");
}
// With parameters
fn add(a: Int, b: Int) {
return a + b;
}
// With return type
fn multiply(x: Int, y: Int) -> Int {
return x * y;
}
// With type hints
fn process(data: String, count: Int) -> Bool {
// ...
return true;
}
Operators
Arithmetic
let sum = a + b; // Addition
let diff = a - b; // Subtraction
let prod = a * b; // Multiplication
let quot = a / b; // Division
let rem = a % b; // Modulo
let neg = -a; // Negation
Comparison
let eq = a == b; // Equal
let ne = a != b; // Not equal
let lt = a < b; // Less than
let gt = a > b; // Greater than
let le = a <= b; // Less or equal
let ge = a >= b; // Greater or equal
Logical
let and_result = a and b; // Logical AND
let or_result = a or b; // Logical OR
let not_result = not a; // Logical NOT
String Concatenation
let greeting = "Hello, " + name + "!";
let message = "Count: " + count; // Auto-converts to string
Operator Precedence
From highest to lowest:
- Function calls, field access:
f(),obj.field - Unary:
-,not - Multiplicative:
*,/,% - Additive:
+,- - Comparison:
<,>,<=,>= - Equality:
==,!= - Logical AND:
and - Logical OR:
or
Use parentheses to override precedence:
let result = (a + b) * c;
let condition = (x > 0) and (y < 10);
Control Flow Syntax
If Statement
if condition {
// then block
}
if condition {
// then block
} else {
// else block
}
if condition1 {
// block 1
} else if condition2 {
// block 2
} else {
// block 3
}
While Loop
while condition {
// loop body
}
while i < 10 {
io.print(i);
i = i + 1;
}
For Loop
for item in collection {
// loop body
}
for i in range(0, 10) {
io.print(i);
}
Break and Continue
while true {
if should_exit {
break;
}
if should_skip {
continue;
}
// ...
}
Capability Calls
Capability calls use the @ prefix:
// Basic capability call
io.print("Hello");
// With multiple arguments
fs.write("file.txt", "content");
// Storing result
let content = fs.read("file.txt");
// Chaining (if result is an object)
let data = net.http("https://api.example.com").json();
Language Blocks
Embed other languages with @language { ... }:
@python {
print("Hello from Python")
x = 42
}
@javascript {
console.log("Hello from JavaScript");
const y = 100;
}
@bash {
echo "Hello from Bash"
ls -la
}
Import Statements
// Import module
import std.io;
// Import with alias
import std.fs as filesystem;
// Import specific items (future feature)
// import std.io.{print, read};
Important:
importcreates aliases for capabilities, not direct access. The@prefix is still required for all capability calls:import std.io as console; console.print("Hello"); // ✓ Correct // console.print("Hello"); // ✗ Error: missing @
Export Statements
// Export variable for other capsules
export result = calculate();
// Export function
export fn utility() {
// ...
}
Type Annotations
// Variable type hints
let name: String = "Alice";
let age: Int = 30;
let score: Float = 95.5;
let active: Bool = true;
// Function parameter and return types
fn calculate(x: Int, y: Int) -> Int {
return x + y;
}
// Array types (future feature)
// let numbers: Array<Int> = [1, 2, 3];
// Map types (future feature)
// let config: Map<String, Int> = {"key": 42};
Code Organization
Single File
// Imports at top
import std.io;
import std.fs;
// Function definitions
fn helper() {
// ...
}
fn main() {
// Entry point
}
Multiple Files (future feature)
// lib.crush
export fn utility() {
// ...
}
// main.crush
import lib;
fn main() {
lib.utility();
}
Style Guidelines
Naming Conventions
// Functions: snake_case
fn calculate_total() { }
// Variables: snake_case
let user_name = "Alice";
// Constants: SCREAMING_SNAKE_CASE (future feature)
// const MAX_SIZE = 100;
// Types: PascalCase
struct UserData { }
Indentation
Use 4 spaces (not tabs):
fn main() {
if condition {
while loop {
io.print("Nested");
}
}
}
Line Length
Keep lines under 100 characters when possible.
Spacing
// Spaces around operators
let sum = a + b;
// Space after commas
fn call(a, b, c) { }
// No space before semicolon
let x = 42;
// Space after keywords
if condition {
while loop {
Grammar Summary
program = statement*
statement = var_decl
| fn_def
| if_stmt
| while_stmt
| for_stmt
| return_stmt
| expr_stmt
| import_stmt
| export_stmt
var_decl = "let" IDENT (":" type)? "=" expression ";"
fn_def = "fn" IDENT "(" params? ")" ("->" type)? block
if_stmt = "if" expression block ("else" (if_stmt | block))?
while_stmt = "while" expression block
for_stmt = "for" IDENT "in" expression block
return_stmt = "return" expression? ";"
expr_stmt = expression ";"
expression = literal
| IDENT
| binary_op
| unary_op
| call
| cap_call
| lang_block
cap_call = "@" IDENT "." IDENT "(" args? ")"
lang_block = "@" IDENT "{" ... "}"
block = "{" statement* "}"
Next Steps
- Data Types: Learn about Crush’s type system
- Variables: Variable scoping and management
- Control Flow: Detailed control flow guide
- Functions: Function definition and calling