docs: add comprehensive language overview and syntax example
Signed-off-by: erick-alcachofa <erick@artichoke.dev> Introduces 'docs/example.arti' as an exhaustive showcase of the `artichoke` programming language syntax and grammar. This file serves as a technical specification and "living documentation" for the language. Key features demonstrated include: - Module imports and symbol aliasing (using). - Generic definitions vs. Turbofish (::<>) instantiations. - Designated initializers (.field = expr) and member functions (this). - Tagged enums and explicit error handling via Result<T, E>. - Pointer (*), mutability ($), and optional (?) type qualifiers. - Slice syntax, array literals, and specialized suffixes (.*, .#, .[]). - Advanced control flow: Result/Optional unwrapping (|val|) and labeled loops. - Compile-time reflection using the .@ operator.
This commit is contained in:
parent
bdc1a151aa
commit
8f15650a42
240
docs/example.arti
Normal file
240
docs/example.arti
Normal file
@ -0,0 +1,240 @@
|
||||
/* ============================================================================
|
||||
* `artichoke` - Language Overview & Technical Specification
|
||||
* ============================================================================
|
||||
* This file serves as an exhaustive showcase of the Artichoke syntax and
|
||||
* grammar rules as of the 2025 parser-stabilization phase.
|
||||
*/
|
||||
|
||||
/* * --- Imports ---
|
||||
* Imports allow the compiler to pull symbols from other modules into the
|
||||
* current scope.
|
||||
*/
|
||||
import std::memory; /* Import the entire 'memory' module from 'std' */
|
||||
import std::*; /* Wildcard: Import all direct child elements of 'std'.
|
||||
Note: This does not recursively import submodules. */
|
||||
import my_module::some_function; /* Import a specific function symbol */
|
||||
import my_module::some_typename; /* Import a specific type symbol */
|
||||
|
||||
/* --- Type & Symbol Aliasing ---
|
||||
* The `using` keyword creates an alias for an existing symbol. This is
|
||||
* useful for shortening long namespaced paths or creating domain-specific
|
||||
* names for primitive types.
|
||||
*/
|
||||
using mem = std::memory;
|
||||
using malloc = mem::mem_alloc;
|
||||
using my_type = my_module::some_typename;
|
||||
using my_func = my_module::some_function;
|
||||
|
||||
/* --- Structs & Generics (Definition) ---
|
||||
* Generics at the definition level use a "clean" `<typename T>` syntax.
|
||||
* The Turbofish is NOT used here, only at the call site/instantiation.
|
||||
*/
|
||||
struct Point<typename T> {
|
||||
x: T,
|
||||
y: T
|
||||
}
|
||||
|
||||
struct Rectangle {
|
||||
/* Usage of a generic type REQUIRES the Turbofish `::<>`.
|
||||
This disambiguates between the 'less-than' operator and a generic list
|
||||
within the Pratt expression parser. */
|
||||
top: Point::<i32>,
|
||||
bot: Point::<i32>
|
||||
}
|
||||
|
||||
/* --- Functions ---
|
||||
* Return types follow the `->` operator.
|
||||
*/
|
||||
fn meaning_of_life() -> i32 {
|
||||
return 42;
|
||||
}
|
||||
|
||||
/* --- Named Initializers ---
|
||||
* Structs/Objects are initialized using the designated initializer syntax:
|
||||
* `.field = expression`. This makes the code self-documenting and order-independent.
|
||||
*/
|
||||
fn scale<typename T>(lhs: *Point::<T>, rhs: T) -> Point::<T> {
|
||||
return Point::<T> {
|
||||
.x = lhs->x * rhs,
|
||||
.y = lhs->y * rhs
|
||||
};
|
||||
}
|
||||
|
||||
/* --- Member Functions (Methods) ---
|
||||
* A function becomes a "member function" if the first parameter follows
|
||||
* the specific syntax: `this` <type>.
|
||||
|
||||
* Note: No colon is required after the `this` keyword.
|
||||
|
||||
* These can be called as: `expr.fn_name(params...)`
|
||||
* or as standard functions: `fn_name(expr, params...)`.
|
||||
*/
|
||||
fn add<typename T>(this *Point::<T>, other: *Point::<T>) {
|
||||
this->x += other->x;
|
||||
this->y += other->y;
|
||||
}
|
||||
|
||||
/* --- Tagged Enums (Sum Types) ---
|
||||
* Enums can hold data. The standard library provides a `Result<T, E>`
|
||||
* for robust, explicit error handling.
|
||||
*/
|
||||
enum Result<typename T, typename E> {
|
||||
Ok(T),
|
||||
Err(E)
|
||||
}
|
||||
|
||||
/* --- The Main Entry Point ---
|
||||
* Slices are represented as `[]type`. Argv here is a slice of slices of chars.
|
||||
*/
|
||||
fn main(argc: i32, argv: [][]char) -> Result::<void, i32> {
|
||||
if (argc < 2) {
|
||||
/* To initialize an enum with data, use an positional initializer. */
|
||||
return Result::<void, i32>::Err{ -1 };
|
||||
}
|
||||
|
||||
/* --- Variable Declarations ---
|
||||
* `let` -> Mutable variable (can be reassigned).
|
||||
* `def` -> Constant variable (immutable after initialization).
|
||||
* Type inference is supported when the right-hand side is unambiguous.
|
||||
*/
|
||||
let x: i32 = 10;
|
||||
let do_you_get_it = meaning_of_life();
|
||||
def PI: f64 = 3.14159265358979;
|
||||
|
||||
/* --- Pointers, Mutability, and Optionals ---
|
||||
* The following type qualifiers are used to "extend" the types
|
||||
* `*` -> Pointer (Non-nullable by default).
|
||||
* `$` -> Mutability qualifier.
|
||||
* `?` -> Nullable/Optional qualifier.
|
||||
* Qualifiers apply to the immediate element to their right.
|
||||
*/
|
||||
def ptr: *i32 = &do_you_get_it; /* Const pointer to immutable i32 */
|
||||
let mutable_pointer: *$i32 = &x; /* Mutable pointer to mutable i32 */
|
||||
|
||||
/* A mutable (`let`) pointer (*) to a mutable pointer ($*) to a mutable i32 ($i32) */
|
||||
let complex_pointer: *$*$i32 = &mutable_pointer;
|
||||
|
||||
/* Nullable types allow the `null` literal.
|
||||
Mixing qualifiers allows for complex types like "Nullable pointer to mutable i32". */
|
||||
let null_int: ?i32 = null;
|
||||
def nullable_float: ?f32 = PI;
|
||||
|
||||
/* --- Slices and Array Literals ---
|
||||
* Slices are the primary way to handle contiguous memory.
|
||||
* `<type> { ... }` is an Type-ObjectInitiated Literal (Anonymous construction).
|
||||
* If the type is an slice type, then is a standard Array Literal.
|
||||
*/
|
||||
let arrSlice: ?[]i32 = []i32 { 2, 4, 6, 8, 10 };
|
||||
|
||||
/* --- Slicing Suffixes ---
|
||||
* `artichoke` supports Python-like slicing with optional boundaries.
|
||||
*/
|
||||
let full = arrSlice[:]; /* Full range copy (can be omitted) */
|
||||
let range = arrSlice[1:3]; /* From index 1 to 2 (exclusive of 3) */
|
||||
let head = arrSlice[:2]; /* Everything before index 2 */
|
||||
let tail = arrSlice[2:]; /* Everything from index 2 to the end */
|
||||
|
||||
/* --- Specialized Suffix Operators ---
|
||||
* `.*` -> Unwraps a slice into its raw underlying pointer.
|
||||
* `.#` -> Retrieves the length of the slice.
|
||||
* `.[length]` -> Convers a raw pointer into a slice of the given length.
|
||||
*/
|
||||
def memPtr = arrSlice.*;
|
||||
def memLength = arrSlice.#;
|
||||
def newSlice = memPtr.[memLength];
|
||||
|
||||
/* --- Control Flow & Error Unwrapping ---
|
||||
* If statements can "unwrap" Result types using the `|val|` syntax.
|
||||
*/
|
||||
if (foo()) |ok_val| {
|
||||
/* If foo() returned Ok(T), ok_val is available here as type T */
|
||||
} else |err_val| {
|
||||
/* If foo() returned Err(E), err_val is available here as type E */
|
||||
}
|
||||
|
||||
/* --- While patterns ---
|
||||
* While statements can also "unwrap" Result types using the `|val|` syntax.
|
||||
* The while look will continue running until an Err result is resturned
|
||||
* which will be unrapped into the `err_val` variable if specified.
|
||||
*/
|
||||
while (foo()) |ok_val| {
|
||||
/* If foo() returned Ok(T), ok_val is available here as type T and the loop
|
||||
* will continue */
|
||||
} else |err_val| {
|
||||
/* If foo() returned Err(E), err_val is available here as type E */
|
||||
}
|
||||
|
||||
/* Normal condition based while loops are also supported
|
||||
* The other powerful while pattern that `artichoke` provides is a "iterator"
|
||||
* based while loop, if the return type of the expression provided at the
|
||||
* condition returns an optional (`?`), then it can unraps the variable by
|
||||
* using the `|val|` syntax and will continue until an empty optional value is
|
||||
* returned.
|
||||
*/
|
||||
while (foo.next()) |ok_val| {
|
||||
/* If foo() returned Ok(T), ok_val is available here as type T and the loop
|
||||
* will continue */
|
||||
}
|
||||
|
||||
/* --- Match & Switch ---
|
||||
* `match` -> Used for complex Pattern Matching (Types, Enums).
|
||||
* `switch` -> Used for Value-based branching.
|
||||
* Both must be exhaustive or include a default case `_`.
|
||||
*/
|
||||
match (foo()) {
|
||||
Result::<i32, []u8>::Ok |v| -> {
|
||||
std::io::print("Success!");
|
||||
}
|
||||
_ -> { /* Default case */ }
|
||||
}
|
||||
|
||||
/* --- Loops & Labels ---
|
||||
* Loops can be named using the `name :=` syntax to allow breaking/continuing
|
||||
* from nested contexts.
|
||||
* The loop types that `artichoke` provides are
|
||||
* * `while` loops
|
||||
* * `do-while` loops
|
||||
* * `for` loops with a C-style syntax
|
||||
* * range `for` loops
|
||||
*/
|
||||
outer_loop := while (condition) {
|
||||
inner_loop := for (let i = 0; i < 10; i += 1) {
|
||||
if (i == 5) { break outer_loop; }
|
||||
}
|
||||
}
|
||||
|
||||
/* `do-while` loop, no else branch is allowed here */
|
||||
do {
|
||||
/* Statements */
|
||||
} while (true);
|
||||
|
||||
/* `for` loop with a C-style syntax */
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
/* Statements */
|
||||
}
|
||||
|
||||
/* range `for` loop, type is deduced from expression */
|
||||
for (let element := returns_range_function()) {
|
||||
/* Statements */
|
||||
}
|
||||
|
||||
/* * --- Resource Management ---
|
||||
* `defer` -> Executes at the end of the scope in reverse order.
|
||||
* `errdefer` -> Executes only if the function returns an Err variant.
|
||||
*/
|
||||
defer cleanup();
|
||||
errdefer { log_failure(); }
|
||||
|
||||
/* * --- Compile-Time Reflection ---
|
||||
* The `.@` operator provides access to metadata.
|
||||
*/
|
||||
|
||||
/* Returns an struct with compile time information */
|
||||
def refl_info = foo.@;
|
||||
|
||||
/* Specific properties like alighment, size, offset, name are allowed too */
|
||||
def xalign = Point::<u32>::x.@alignment;
|
||||
def type_size = Point::<u32>.@size;
|
||||
|
||||
return Result::<void, i32>::Ok{};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user