Language Walkthrough
Thanks for reading thus far! This section is a walkthrough of the language. It is designed to provide information in an order useful to someone new to programming.
A Quick Program
This will be a motivating example for the rest of the section:
fn print_number(n: int) { print("the number is {0}\n".format(n)); } fn main() { let a = 42; print_number(a); }
Functions and Statements
Let's start with functions and statements.
Functions are composed of statements between curly braces.
You already saw a function in the introduction.
That function printed ()
after the Hello, world!
message.
In dwarf we just call that the empty type/value.
It's a type, and a value, and it's the only value of it's type.
A function (and any block for that matter) returns the value of it's last statement.
A statement that ends in a semi-colon has the empty value:
fn main() { print(""); }
On the other hand, a statement that does not terminate in a semi-colon has the value of it's expression:
fn main() -> int { 42 }
Expressions
There will be a lot more to say about expressions later. For now just know that basically everything in dwarf is an expression.
Let Statement
Not all statements are composed of expressions.
In particular is the let
statement.
Since the let statement has no value, it must be terminated with a semi-colon.
The following is not valid and throws an error:
fn main() { let a = 42 }
The let statement is instead used to assign a value to a storage location, or memory. The storage location is called a variable, and has a name. You use the name to refer to the variable elsewhere in the function:
fn main() { let a = 42; print(a); }
Getting back to functions, functions take inputs, do something with them, and return an output. The following code defines a function that takes an integer, does some computation, and returns a string.
#fn main() { fn foo(a: int) -> string { let b = a * 2; "The value of b is {1}. This is here just to confuse you, {0}".format("hah!", b) } print(foo(42) + "\n"); }
Advanced Statements
There is a third type of statement, called an item statement. It's useful for defining structs and functions inside of a block expression. Below is an example of both:
fn main() { struct Point { x: float, y: float, } impl Point { fn new(x: float, y: float) -> Point { Point { x: x, y: y } } } fn foo() -> Point { Point::new(42.0, -3.14) } print(foo()); }
Conditional Expressions
Next up is the conditional expression. Conditional expressions are used to make decisions. They are composed of three parts: a condition, a then expression, and an else expression. The condition is an expression that evaluates to a boolean value. The then and else expressions are expressions that evaluate to the same type. The type of the conditional expression is the type of the then and else expressions. The following is an example of a conditional expression:
fn main() { let a = 42; let b = 69; let c = if a > b { a } else { b }; print(c); }
Expression Magic
Having everything as an expression pays great dividends. In the example above notice how we assign
c
to the result of theif
expression.if
actually has a value, which is the value of it's evaluation. It's expressions all the way down. 🥁
There are all the usual comparison operators: ==
, !=
, <
, <=
, >
, >=
:
fn main() { chacha::assert_eq(42 == 42, true); chacha::assert_eq(42 != 42, false); chacha::assert_eq(42 < 42, false); chacha::assert_eq(42 <= 42, true); chacha::assert_eq(42 > 42, false); chacha::assert_eq(42 >= 42, true); }
#fn main() { //macro_rules! println { // () => { // print("\n"); // }, // ($arg:expr) => { // print($arg as string + "\n"); // }, // ($fmt:literal, $($arg:expr),*) => { // print($fmt.format($($arg),*) + "\n"); // } //} //printtln!(); print("\n"); //println!("Hello, world!"); print("Hello, world!" as string + "\n"); //println!("The answer is {0}, what's the {1}", 42, "question?"); print("The answer is {0}, what's the {1}".format(42, "question?") + "\n"); //println!(42); print(42 as string + "\n"); }
Golden Nuggets ⭐️🌟✨
-
The interpreter is called ChaCha, but the binary is called
dwarf
. Go figure. -
I call input files
.tao
, I don't remember why. None of the tooling cares what you name them. -
The interpreter looks for a
main
function, where it will begin execution.
fn main() { print("Hello, world!\n"); }
Passing command line arguments to main from the interpreter is supported.
- In dwarf, just about everything is an expression.
The only thing that is not an expression is a
let
statement. That's not true. Items inside blocks are also statements. So that's only two things that I can think of. Putting a semicolon at the end of an expression makes it a statement, but it's still an expression underneath. This is super powerful, and allows for some really cool things.