Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
The Hash Programming Language
Conditional statements
-Conditional statements in the Hash
programming language are very similar to other languages such as Python, Javascript, C and Rust. However, there is one subtle difference, which is that the statement provided to a conditional statement must always evaluate to an explicit boolean value.
Conditional statements in the Hash
programming language are very similar to other languages such as Python, Javascript, C and Rust. However, there is one subtle difference, which is that the statement provided to a conditional statement must always evaluate to an explicit boolean value.
If-else statements
If statements are very basic constructs in Hash. An example of a basic if-else
statement is as follows:
#![allow(unused)] fn main() { // checking if the value 'a' evaluates to being 'true' -if a { print("a"); } else { print("b"); } +if a { print("a"); } else { print("b"); } // using a comparison operator -if b == 2 { print("b is " + b); } else { print ("b is not " + conv(b)); } +if b == 2 { print("b is " + b); } else { print ("b is not " + conv(b)); } }
Obviously, this checks if the evaluation of a
returns a boolean value. If it does not
evaluate to something to be considered as true, then the block expression defined
@@ -196,31 +196,31 @@
If-else
#![allow(unused)]
fn main() {
if b == 2 {
- print("b is 2")
+ print("b is 2")
} else if b == 3 {
- print("b is 3")
+ print("b is 3")
} else {
- print("b isn't 2 or 3 ")
+ print("b isn't 2 or 3 ")
}
}
-As mentioned in the introduction, a conditional statement must evaluate an explicit boolean value. The if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements.
+As mentioned in the introduction, a conditional statement must evaluate an explicit boolean value. The if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements.
An example of an invalid program is:
#![allow(unused)]
fn main() {
a: u8 = 12;
-if a { print("a") }
+if a { print("a") }
}
Additional syntax
Furthermore, if you do not want an else statement you can do:
#![allow(unused)]
fn main() {
-if a { print("a") } // if a is true, then execute
+if a { print("a") } // if a is true, then execute
}
which is syntactic sugar for:
#![allow(unused)]
fn main() {
-if a { print("a") } else {}
+if a { print("a") } else {}
}
Additionally, since the if
statement body's are also equivalent to functional bodies, you
can also specifically return a type as you would normally do within a function body:
@@ -253,16 +253,16 @@ }
Furthermore, for more complicated conditional statements, you can include an expression
block which is essentially treated as if it was a functional body, like so:
#![allow(unused)]
fn main() {
-f: str = "file.txt";
+f: str = "file.txt";
if { a = open(f); is_ok(a) } {
// run if is_ok(a) returns true
@@ -290,17 +290,17 @@ Match cases
a := input<u8>();
m2 := match a {
- 1 => "one";
- 2 => "two";
- _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
+ _ => "not one or two";
}
// Or as a function
convert: (x: u8) => str = (x) => match x {
- 1 => "one";
- 2 => "two";
- _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
+ _ => "not one or two";
}
m := convert(input<u8>());
@@ -310,8 +310,8 @@ Match cases
#![allow(unused)]
fn main() {
match x {
- 1 => "one";
- 2 => "two";
+ 1 => "one";
+ 2 => "two";
_ => unreachable(); // we know that 'x' should never be 1 or 2.
}
}
@@ -320,13 +320,13 @@ Match cases
#![allow(unused)]
fn main() {
convert: (x: u8) => str = (x) => match x {
- _ => "not one or two";
- 1 => "one";
- 2 => "two";
+ _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
}
}
-The value of m
will always evaluate as "not one or two"
since the wildcard matches any condition.
-Match statements are also really good for destructing enum types in Hash.
+
The value of m
will always evaluate as "not one or two"
since the wildcard matches any condition.
+Match statements are also really good for destructing enum types in Hash.
For example,
#![allow(unused)]
fn main() {
@@ -341,8 +341,8 @@ Match cases
result: Result<u16, str> = Ok(12);
match result {
- Ok(value) => print("Got '" + conv(value) + "' from operation");
- Err(e) => panic("Failed to get result: " + e);
+ Ok(value) => print("Got '" + conv(value) + "' from operation");
+ Err(e) => panic("Failed to get result: " + e);
}
}
To specify multiple conditions for a single case within a match
statement, you can do so by
@@ -352,9 +352,9 @@
Match cases
x: u32 = input<u32>();
match x {
- 1 | 2 | 3 => print("x is 1, 2, or 3");
- 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator
- _ => print("x is something else");
+ 1 | 2 | 3 => print("x is 1, 2, or 3");
+ 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator
+ _ => print("x is something else");
}
}
To specify more complex conditional statements like and within the match case, you
@@ -365,10 +365,10 @@
Match cases
y: bool = true;
match x {
- 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true");
- {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator
- {2 | 4 if y} => print("x is 6 and y is true");
- _ => print("x is something else");
+ 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true");
+ {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator
+ {2 | 4 if y} => print("x is 6 and y is true");
+ _ => print("x is something else");
}
}
Loop constructs
@@ -386,7 +386,7 @@ General
For loop
Basics
For loops are special loop control statements that are designed to be used
-with iterators.
+with iterators.
For loops can be defined as:
#![allow(unused)]
fn main() {
@@ -394,7 +394,7 @@ Basics
print(i);
}
}
-Iterating over lists is also quite simple using the iter
function to
+
Iterating over lists is also quite simple using the iter
function to
convert the list into an iterator:
#![allow(unused)]
fn main() {
@@ -412,7 +412,7 @@ Basics
}
iterators
Iterators ship with the standard library, but you can define your own iterators via the Hash generic typing system.
-An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
+
An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
So, for the example above, the range
function is essentially a RangeIterator
of the u8
, u16
, u32
, ...
types.
More details about generics are here.
While loop
@@ -470,7 +470,7 @@ }
-It is worth noting that the looping expression whether block or not must explicitly have the
+
It is worth noting that the looping expression whether block or not must explicitly have the
boolean
return type. For example, the code below will fail typechecking:
#![allow(unused)]
fn main() {
@@ -498,7 +498,7 @@ Loop
c: u64 = 1;
loop {
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
}
}
@@ -511,7 +511,7 @@ Loop
loop {
if c == 10 { break }
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
} // this will loop 10 times, and print all 10 times
}
@@ -523,7 +523,7 @@ Loop
c += 1;
if c % 2 != 0 { continue };
- print("I loop and I print when I get a " + c);
+ print("I loop and I print when I get a " + c);
} // this will loop 10 times, and print only when c is even
}
diff --git a/features/functions.html b/features/functions.html
index 52008a739..47a0e2528 100644
--- a/features/functions.html
+++ b/features/functions.html
@@ -217,12 +217,12 @@ fn main() {
func := (a: str, b = 'c', c = 2) -> u32 => { ... };
-func("foobar"); // a = "foobar", b = 'c', c = 2
+func("foobar"); // a = "foobar", b = 'c', c = 2
// You can optionally provide the arguments in declaration order:
-func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
+func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
// Or you can provide them as named arguments:
-func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
+func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
}
Named arguments can be used for more context when providing arguments, using the syntax arg_name = arg_value
.
After the first named argument is provided, all following arguments must be named.
@@ -230,25 +230,25 @@
foo := (a: str, b: str, c: str, d: str) => { .. };
-foo("a", "b", "c", "d") // Allowed -- no arguments are named.
-foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
-foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
-foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
-foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
+foo("a", "b", "c", "d") // Allowed -- no arguments are named.
+foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
+foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
+foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
+foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
Grammar
The grammar for function definitions and function types is as follows:
function_param =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
-function_type = "(" ( function_param "," )* function_param? ")" "->" type
+function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
+function_type = "(" ( function_param "," )* function_param? ")" "->" type
The grammar for function calls is as follows:
-function_call_arg = expr | ( ident "=" expr )
-function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
+function_call_arg = expr | ( ident "=" expr )
+function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
#![allow(unused)]
fn main() {
if b == 2 {
- print("b is 2")
+ print("b is 2")
} else if b == 3 {
- print("b is 3")
+ print("b is 3")
} else {
- print("b isn't 2 or 3 ")
+ print("b isn't 2 or 3 ")
}
}
if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements. if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements.#![allow(unused)]
fn main() {
a: u8 = 12;
-if a { print("a") }
+if a { print("a") }
}
#![allow(unused)]
fn main() {
-if a { print("a") } // if a is true, then execute
+if a { print("a") } // if a is true, then execute
}
#![allow(unused)]
fn main() {
-if a { print("a") } else {}
+if a { print("a") } else {}
}
if
statement body's are also equivalent to functional bodies, you
can also specifically return a type as you would normally do within a function body:Furthermore, for more complicated conditional statements, you can include an expression block which is essentially treated as if it was a functional body, like so:
#![allow(unused)] fn main() { -f: str = "file.txt"; +f: str = "file.txt"; if { a = open(f); is_ok(a) } { // run if is_ok(a) returns true @@ -290,17 +290,17 @@
Match cases
a := input<u8>(); m2 := match a { - 1 => "one"; - 2 => "two"; - _ => "not one or two"; + 1 => "one"; + 2 => "two"; + _ => "not one or two"; } // Or as a function convert: (x: u8) => str = (x) => match x { - 1 => "one"; - 2 => "two"; - _ => "not one or two"; + 1 => "one"; + 2 => "two"; + _ => "not one or two"; } m := convert(input<u8>()); @@ -310,8 +310,8 @@Match cases
@@ -320,13 +320,13 @@#![allow(unused)] fn main() { match x { - 1 => "one"; - 2 => "two"; + 1 => "one"; + 2 => "two"; _ => unreachable(); // we know that 'x' should never be 1 or 2. } }
Match cases
-#![allow(unused)] fn main() { convert: (x: u8) => str = (x) => match x { - _ => "not one or two"; - 1 => "one"; - 2 => "two"; + _ => "not one or two"; + 1 => "one"; + 2 => "two"; } }
The value of
-m
will always evaluate as"not one or two"
since the wildcard matches any condition.Match statements are also really good for destructing enum types in Hash. +
The value of
+m
will always evaluate as"not one or two"
since the wildcard matches any condition.Match statements are also really good for destructing enum types in Hash. For example,
#![allow(unused)] fn main() { @@ -341,8 +341,8 @@
Match cases
result: Result<u16, str> = Ok(12); match result { - Ok(value) => print("Got '" + conv(value) + "' from operation"); - Err(e) => panic("Failed to get result: " + e); + Ok(value) => print("Got '" + conv(value) + "' from operation"); + Err(e) => panic("Failed to get result: " + e); } }To specify multiple conditions for a single case within a
match
statement, you can do so by @@ -352,9 +352,9 @@Match cases
x: u32 = input<u32>(); match x { - 1 | 2 | 3 => print("x is 1, 2, or 3"); - 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator - _ => print("x is something else"); + 1 | 2 | 3 => print("x is 1, 2, or 3"); + 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator + _ => print("x is something else"); } }
To specify more complex conditional statements like and within the match case, you @@ -365,10 +365,10 @@
Match cases
y: bool = true; match x { - 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true"); - {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator - {2 | 4 if y} => print("x is 6 and y is true"); - _ => print("x is something else"); + 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true"); + {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator + {2 | 4 if y} => print("x is 6 and y is true"); + _ => print("x is something else"); } }Loop constructs
@@ -386,7 +386,7 @@General
For loop
Basics
For loops are special loop control statements that are designed to be used -with iterators.
+with iterators.For loops can be defined as:
-#![allow(unused)] fn main() { @@ -394,7 +394,7 @@
Basics
print(i); } }
Iterating over lists is also quite simple using the iter
function to
+
Iterating over lists is also quite simple using the iter
function to
convert the list into an iterator:
#![allow(unused)] fn main() { @@ -412,7 +412,7 @@
Basics
}
iterators
Iterators ship with the standard library, but you can define your own iterators via the Hash generic typing system.
-An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
+
An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
So, for the example above, the range
function is essentially a RangeIterator
of the u8
, u16
, u32
, ...
types.
More details about generics are here.
While loop
@@ -470,7 +470,7 @@}
-It is worth noting that the looping expression whether block or not must explicitly have the
+
It is worth noting that the looping expression whether block or not must explicitly have the
boolean
return type. For example, the code below will fail typechecking:
#![allow(unused)]
fn main() {
@@ -498,7 +498,7 @@ Loop
c: u64 = 1;
loop {
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
}
}
@@ -511,7 +511,7 @@ Loop
loop {
if c == 10 { break }
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
} // this will loop 10 times, and print all 10 times
}
@@ -523,7 +523,7 @@ Loop
c += 1;
if c % 2 != 0 { continue };
- print("I loop and I print when I get a " + c);
+ print("I loop and I print when I get a " + c);
} // this will loop 10 times, and print only when c is even
}
diff --git a/features/functions.html b/features/functions.html
index 52008a739..47a0e2528 100644
--- a/features/functions.html
+++ b/features/functions.html
@@ -217,12 +217,12 @@ fn main() {
func := (a: str, b = 'c', c = 2) -> u32 => { ... };
-func("foobar"); // a = "foobar", b = 'c', c = 2
+func("foobar"); // a = "foobar", b = 'c', c = 2
// You can optionally provide the arguments in declaration order:
-func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
+func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
// Or you can provide them as named arguments:
-func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
+func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
}
Named arguments can be used for more context when providing arguments, using the syntax arg_name = arg_value
.
After the first named argument is provided, all following arguments must be named.
@@ -230,25 +230,25 @@
foo := (a: str, b: str, c: str, d: str) => { .. };
-foo("a", "b", "c", "d") // Allowed -- no arguments are named.
-foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
-foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
-foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
-foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
+foo("a", "b", "c", "d") // Allowed -- no arguments are named.
+foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
+foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
+foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
+foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
Grammar
The grammar for function definitions and function types is as follows:
function_param =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
-function_type = "(" ( function_param "," )* function_param? ")" "->" type
+function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
+function_type = "(" ( function_param "," )* function_param? ")" "->" type
The grammar for function calls is as follows:
-function_call_arg = expr | ( ident "=" expr )
-function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
+function_call_arg = expr | ( ident "=" expr )
+function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
boolean
return type. For example, the code below will fail typechecking:#![allow(unused)]
fn main() {
@@ -498,7 +498,7 @@ Loop
c: u64 = 1;
loop {
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
}
}
Named arguments can be used for more context when providing arguments, using the syntax arg_name = arg_value
.
After the first named argument is provided, all following arguments must be named.
@@ -230,25 +230,25 @@
foo := (a: str, b: str, c: str, d: str) => { .. };
-foo("a", "b", "c", "d") // Allowed -- no arguments are named.
-foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
-foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
-foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
-foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
+foo("a", "b", "c", "d") // Allowed -- no arguments are named.
+foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
+foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
+foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
+foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
Grammar
The grammar for function definitions and function types is as follows:
function_param =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
-function_type = "(" ( function_param "," )* function_param? ")" "->" type
+function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
+function_type = "(" ( function_param "," )* function_param? ")" "->" type
The grammar for function calls is as follows:
-function_call_arg = expr | ( ident "=" expr )
-function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
+function_call_arg = expr | ( ident "=" expr )
+function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
function_param =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
-function_type = "(" ( function_param "," )* function_param? ")" "->" type
+function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
+function_type = "(" ( function_param "," )* function_param? ")" "->" type
function_call_arg = expr | ( ident "=" expr )
-function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
+function_call_arg = expr | ( ident "=" expr )
+function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
The Hash Programming Language
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on +
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)] fn main() { dump_ast main := () => { - println("Hello, world!"); + println("Hello, world!"); } }
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)] fn main() { -#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")] +#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")] jpeg_read_header := (&raw cinfo, bool require_image) -> i32; }
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
@@ -276,15 +276,15 @@#![allow(unused)] @@ -209,9 +209,9 @@
Token macros}
}
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -296,9 +296,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
diff --git a/features/modules.html b/features/modules.html
index d3fe196ec..5b4f7b981 100644
--- a/features/modules.html
+++ b/features/modules.html
@@ -190,17 +190,17 @@ Importing
└── main.hash
Modules in hash allow for a source to be split up into smaller code fragments, allowing for better source code organisation and maintenance.
-You can import modules by specifying the path relative to the current path.
+You can import modules by specifying the path relative to the current path.
For example, if you wanted to include the modules a
, b
, and or c
within your main file
#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
-b := import("lib/b");
-c := import("lib/sub/c");
+a := import("lib/a");
+b := import("lib/b");
+c := import("lib/sub/c");
}
By doing so, you are placing everything that is defined within each of those modules under
-the namespace.
+the namespace.
Exporting
In order to export items from a module, use the pub
keyword.
For example:
@@ -218,9 +218,9 @@ Exporting
priv c := 1;
/// b.hash
-{ a } := import("a.hash"); // Ok
-{ b } := import("a.hash"); // Error: b is private
-{ c } := import("a.hash"); // Error: c is private.
+{ a } := import("a.hash"); // Ok
+{ b } := import("a.hash"); // Error: b is private
+{ c } := import("a.hash"); // Error: c is private.
}
Referencing exports
Furthermore, if the a
module contained a public structure definition like Point
:
@@ -236,7 +236,7 @@ Refer
#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
+a := import("lib/a");
p1 := a::Point(
x = 2,
@@ -252,7 +252,7 @@ Refer
follows:
#![allow(unused)]
fn main() {
-{ Point } := import("lib/a");
+{ Point } := import("lib/a");
p1 := Point(x=2, y=3);
}
@@ -260,7 +260,7 @@ Refer
can rename the exported members to your liking using the as
pattern operator:
#![allow(unused)]
fn main() {
-{ Point as LibPoint } = import("lib/a");
+{ Point as LibPoint } = import("lib/a");
p1 := LibPoint(x=2, y=3);
}
@@ -276,17 +276,17 @@ Inline modules<
};
// b.hash
-a := import("a.hash");
+a := import("a.hash");
red := a::nested::Colour::Red;
}
These follow the same conventions as .hash
files, and members need to be exported with pub
in order to be visible from the outside.
However, the external module items are always visible from within a mod
block, so in the above example, bar
can be used from within nested
.
Grammar
The grammar for file modules is as follows:
-file_module = ( expr ";" )*
+file_module = ( expr ";" )*
The grammar for mod
blocks (which are expressions) is as follows:
-mod_block = "mod" "{" ( expr ";" )* "}"
+mod_block = "mod" "{" ( expr ";" )* "}"
token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -296,9 +296,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
diff --git a/features/modules.html b/features/modules.html
index d3fe196ec..5b4f7b981 100644
--- a/features/modules.html
+++ b/features/modules.html
@@ -190,17 +190,17 @@ Importing
└── main.hash
a
, b
, and or c
within your main file#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
-b := import("lib/b");
-c := import("lib/sub/c");
+a := import("lib/a");
+b := import("lib/b");
+c := import("lib/sub/c");
}
pub
keyword.
For example:a
module contained a public structure definition like Point
:#![allow(unused)] fn main() { // main.hash -a := import("lib/a"); +a := import("lib/a"); p1 := a::Point( x = 2, @@ -252,7 +252,7 @@
Refer follows:
@@ -260,7 +260,7 @@#![allow(unused)] fn main() { -{ Point } := import("lib/a"); +{ Point } := import("lib/a"); p1 := Point(x=2, y=3); }
Refer can rename the exported members to your liking using the
as
pattern operator:@@ -276,17 +276,17 @@#![allow(unused)] fn main() { -{ Point as LibPoint } = import("lib/a"); +{ Point as LibPoint } = import("lib/a"); p1 := LibPoint(x=2, y=3); }
Inline modules< }; // b.hash -a := import("a.hash"); +a := import("a.hash"); red := a::nested::Colour::Red; }
These follow the same conventions as .hash
files, and members need to be exported with pub
in order to be visible from the outside.
However, the external module items are always visible from within a mod
block, so in the above example, bar
can be used from within nested
.
Grammar
The grammar for file modules is as follows:
-file_module = ( expr ";" )*
+file_module = ( expr ";" )*
The grammar for mod
blocks (which are expressions) is as follows:
-mod_block = "mod" "{" ( expr ";" )* "}"
+mod_block = "mod" "{" ( expr ";" )* "}"
The Hash Programming Language
Name bindings
Basics
-Name bindings are made of three distinct components. The name, the type and the +
Name bindings are made of three distinct components. The name, the type and the value that is assigned to the name.
Declaration of variables happens using the :
and =
symbols:
x: i32 = 3;
The :
symbol is used to denote the type of a variable, and the =
symbol is used to assign a value to it.
The type can be omitted, in which case it is inferred:
x := "Terence Tao"; // `x: str` inferred
-x: str = "Terence Tao"; // same thing
-x: i32 = "Terence Tao"; // Compile error: `str` is not assignable to `i32`.
+x := "Terence Tao"; // `x: str` inferred
+x: str = "Terence Tao"; // same thing
+x: i32 = "Terence Tao"; // Compile error: `str` is not assignable to `i32`.
Declaration and assignment can happen separately:
x: i32:
@@ -198,12 +198,12 @@ Basics
A variable declaration is an expression like any other, and returns the value of the variable.
This means that you can write something like:
while (bytes_read := read_file(&buffer)) != 0 {
- print("Read some bytes!");
+ print("Read some bytes!");
}
Hash is statically typed, which means that variables cannot change types:
-x := "Ha!";
-x = Some("Baaa"); // Compile error: `Option<str>` is not assignable to `str`.
+x := "Ha!";
+x = Some("Baaa"); // Compile error: `Option<str>` is not assignable to `str`.
Mutability
By default, all bindings in Hash are constant.
@@ -237,12 +237,12 @@
Visibility
Grammar
The grammar for name bindings (and partial name bindings/reassignments) is as follows:
pattern = pattern_binding | ...(other patterns)
-pattern_binding = ( "pub" | "priv" )? "mut"? identifier
+pattern_binding = ( "pub" | "priv" )? "mut"? identifier
name_binding =
- | ( pattern ":=" expr ) // Declaration and assignment, infer type
- | ( pattern ( ":" type )? "=" expr ) // Assignment
- | ( pattern ( ":" type ) ) // Declaration
+ | ( pattern ":=" expr ) // Declaration and assignment, infer type
+ | ( pattern ( ":" type )? "=" expr ) // Assignment
+ | ( pattern ( ":" type ) ) // Declaration
Basics
As in many other languages, the programmer can specify the type of a variable or a literal by using some special syntax. For example, in languages such as typescript, -you can say that:
+you can say that:#![allow(unused)] fn main() { some_value as str }
which implies that you are asserting that some_value
is a string, this is essentially a way to avoid explicitly stating that type of a variable every
-single time and telling the compiler "Trust me some_value
is a string
".
some_value
is a string
".
The principle is somewhat similar in Hash
, but it is more strictly enforced.
For example, within the statement x := 37;
, the type of x
can be any of the
integer types. This might lead to unexpected behaviour in future statements, where
@@ -253,37 +253,37 @@
Basics
So, you can either declare x
to be some integer type explicitly like so:
x: u32 = 37;
-Or you can, use as
to imply a type for a variable, which the compiler will assume
+
Or you can, use as
to imply a type for a variable, which the compiler will assume
to be true, like so:
x := 37 as u32;
Failing type assertions
If you specify a type assertion, the compiler will either attempt to infer this information from the left-hand side of the as
operator
-to the right. If the inference results in a different type to the right-hand side, this will raise a typechecking failure.
For example, if you were to specify the expression:
#![allow(unused)] fn main() { -"A" as char +"A" as char }
The compiler will report this error as:
error[0001]: Types mismatch, got a `str`, but wanted a `char`.
--> <interactive>:1:8
-1 | "A" as char
+1 | "A" as char
| ^^^^ This specifies that the expression should be of type `char`
--> <interactive>:1:1
-1 | "A" as char
+1 | "A" as char
| ^^^ Found this to be of type `str`
Usefulness
Why are type assertions when there is already type inference within the language? Well, sometimes the type inference -system does not have enough information to infer the types of variables and declarations. +system does not have enough information to infer the types of variables and declarations. Type inference may not have enough information when dealing with functions that are generic, so it can sometimes -be useful to assert to the compiler that a given variable is a certain type.
+be useful to assert to the compiler that a given variable is a certain type.Additionally, whilst the language is in an early stage of maturity and some things that are quirky or broken, type assertions can come to the rescue and help the compiler to understand your program.
-In general, type assertions should be used when the compiler cannot infer the type of some expression with +
In general, type assertions should be used when the compiler cannot infer the type of some expression with the given information and needs assistance. You shouldn't need to use type assertions often.
diff --git a/features/patterns.html b/features/patterns.html index c688862c7..1250afeb0 100644 --- a/features/patterns.html +++ b/features/patterns.html @@ -190,29 +190,29 @@Literal pat
foo := get_foo(); // foo: i32
match foo {
- 1 => print("Got one");
- 2 => print("Got two");
- 3 => print("Got three");
- _ => print("Got something else");
+ 1 => print("Got one");
+ 2 => print("Got two");
+ 3 => print("Got three");
+ _ => print("Got something else");
}
}
On the left-hand side of the match cases there are the literal patterns 1
, 2
and 3
.
These perform foo == 1
, foo == 2
and foo == 3
in sequence, and the code follows the branch which succeeds first.
-If no branch succeeds, the _
branch is followed, which means "match anything".
+If no branch succeeds, the _
branch is followed, which means "match anything".
Literals can be integer literals for integer types (signed or unsigned), string literals for the str
type, or character literals for the char
type:
#![allow(unused)]
fn main() {
match my_char {
- 'A' => print("First letter");
- 'B' => print("Second letter");
- x => print("Letter is: " + conv(x));
+ 'A' => print("First letter");
+ 'B' => print("Second letter");
+ x => print("Letter is: " + conv(x));
}
match my_str {
- "fizz" => print("Multiple of 3");
- "buzz" => print("Multiple of 5");
- "fizzbuzz" => print("Multiple of 15");
- _ => print("Not a multiple of 3 or 5");
+ "fizz" => print("Multiple of 3");
+ "buzz" => print("Multiple of 5");
+ "fizzbuzz" => print("Multiple of 15");
+ _ => print("Not a multiple of 3 or 5");
}
}
Binding patterns
@@ -221,8 +221,8 @@ Binding pat
#![allow(unused)]
fn main() {
match fallible_operation() { // fallible_operation: () -> Result<f32, i32>
- Ok(success) => print("Got success " + conv(result)); // success: f32
- Err(failure) => print("Got failure " + conv(failure)); // failure: i32
+ Ok(success) => print("Got success " + conv(result)); // success: f32
+ Err(failure) => print("Got failure " + conv(failure)); // failure: i32
}
}
Tuple patterns
@@ -233,12 +233,12 @@ Tuple patterns<
Cat := struct(name: str);
// Creating a tuple:
-my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
+my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
// Tuple pattern:
(Cat(name), elements) := my_val;
-assert(name == "Bob");
+assert(name == "Bob");
assert(elements == [1, 2, 3]);
}
Constructor patterns
@@ -250,11 +250,11 @@ Con
fn main() {
Option := <T> => enum(Some(value: T), None);
-my_val := Some("haha");
+my_val := Some("haha");
match my_val {
// Matching the Some(..) constructor
- Some(inner) => assert(inner == "haha"); // inner: str
+ Some(inner) => assert(inner == "haha"); // inner: str
// Matching the None constructor
None => assert(false);
}
@@ -265,14 +265,14 @@ Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -280,25 +280,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -322,10 +322,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -339,7 +339,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -350,9 +350,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -361,16 +361,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -401,27 +401,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
diff --git a/features/primitives.html b/features/primitives.html
index 388e75fbb..c90ca3726 100644
--- a/features/primitives.html
+++ b/features/primitives.html
@@ -223,15 +223,15 @@ Lists
// type
Grammar for lists:
-list_literal = "[" ( expr "," )* expr? "]"
-list_type = "[" type "]"
+list_literal = "[" ( expr "," )* expr? "]"
+list_type = "[" type "]"
Tuples
Tuples have a familiar syntax with many other languages:
- Empty tuples:
(,)
or ()
- Singleton tuple :
(A,)
-- Many membered tuple:
(A, B, C)
or (A, B, C,)
+- Many membered tuple:
(A, B, C)
or (A, B, C,)
Examples:
empty_tuple: (,) = (,);
@@ -242,7 +242,7 @@ Tuples
// ^^
// type
-some_tuple: (str, u32) = ("string", 12);
+some_tuple: (str, u32) = ("string", 12);
// ^^^^^^^^^^
// type
@@ -252,7 +252,7 @@ Tuples
If this is the case, you should consider using a structural data type which will allow you to do the same thing, and name the fields.
Read more about patterns here.
Grammar for tuples:
-tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
+tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
Named tuples
Named tuples are tuples that specify field names for each field within the tuple.
@@ -270,13 +270,13 @@
Named tuples
Then, you can create a Comment
instance and then access its fields like so:
comment := Comment(
- contents = "Hello, world",
+ contents = "Hello, world",
anchor = (
start = 2,
end = 4
),
edited = false,
- author_id = "f9erf8g43"
+ author_id = "f9erf8g43"
);
print(abs(comment.anchor.start - comment.anchor.end));
@@ -287,7 +287,7 @@ Named tuples
-Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
+
Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
However, this is not recommended because specifically naming tuples implies that the type
cares about the names of the fields rather than simply being a positionally structural type.
Sets
@@ -299,7 +299,7 @@ Sets
Many membered set: {A, B, C}
or {A, B, C,}
.
Set type: {A}
, for example foo: {i32} = {1, 2, 3}
.
-set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
+set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
Map
Maps in Hash represent collections of key-value pairs.
@@ -309,9 +309,9 @@
Map
Empty map: {:}
.
Singleton map : {A:1}
or {A:1,}
.
Many membered map: {A: 1, B: 2, C: 3}
or {A: 1, B: 2, C: 3,}
.
-Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
+Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
-map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
+map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
Note: the grammar for literal types can be found in the Types section.
diff --git a/features/structs-enums.html b/features/structs-enums.html
index f37d0fca0..ec3e1a4ef 100644
--- a/features/structs-enums.html
+++ b/features/structs-enums.html
@@ -197,9 +197,9 @@ Struct types}
Structs are nominal types.
An argument of type Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.
@@ -212,8 +212,8 @@ Struct types}
Enum types
Hash enums are similar to Rust enums or Haskell data types.
@@ -231,15 +231,15 @@
Enum types
Each variant can be paired with some data, in the form of a comma-separated list of types.
#![allow(unused)]
fn main() {
-err := NetworkError::Unexpected("something went terribly wrong", 32);
+err := NetworkError::Unexpected("something went terribly wrong", 32);
}
They can be match
ed to discover what they contain:
#![allow(unused)]
fn main() {
handle_error := (error: NetworkError) => match error {
- NoBytesReceived => print("No bytes received, stopping");
- ConnectionTerminated => print("Connection was terminated");
- Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
+ NoBytesReceived => print("No bytes received, stopping");
+ ConnectionTerminated => print("Connection was terminated");
+ Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
};
}
Like structs, enums are nominal types, rather than structural.
@@ -263,18 +263,18 @@
Generic types
Grammar
The grammar for struct definitions is as follows:
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
The grammar for enum definitions is as follows:
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
diff --git a/features/traits-impls.html b/features/traits-impls.html
index 1fbbe68f2..42929a9d0 100644
--- a/features/traits-impls.html
+++ b/features/traits-impls.html
@@ -196,18 +196,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -294,13 +294,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
diff --git a/features/type-functions.html b/features/type-functions.html
index 1af72daea..7e7f3b2eb 100644
--- a/features/type-functions.html
+++ b/features/type-functions.html
@@ -216,7 +216,7 @@ Type functions<
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
diff --git a/features/types.html b/features/types.html
index c860935b4..d7e29df30 100644
--- a/features/types.html
+++ b/features/types.html
@@ -192,33 +192,33 @@ Grammar
| union_type
| ref_type
-tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
+tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
-list_type = "[" type "]"
+list_type = "[" type "]"
-map_type = "{" type ":" type "}"
+map_type = "{" type ":" type "}"
-set_type = "{" type "}"
+set_type = "{" type "}"
-grouped_type = "(" type ")"
+grouped_type = "(" type ")"
named_type = access_name
-function_type_param = type | ( ident ":" type )
+function_type_param = type | ( ident ":" type )
-function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
+function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
-type_function_call_arg = type | ( ident "=" type )
-type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
+type_function_call_arg = type | ( ident "=" type )
+type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
-type_function_param = ident ( ":" type )? ( "=" type )?
+type_function_param = ident ( ":" type )? ( "=" type )?
-type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
+type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
-merge_type = ( type "~" )+ type
-union_type = ( type "|" )+ type
+merge_type = ( type "~" )+ type
+union_type = ( type "|" )+ type
-ref_type = "&" ( "raw" )? ( "mut" )? type
+ref_type = "&" ( "raw" )? ( "mut" )? type
diff --git a/interpreter/backends.html b/interpreter/backends.html
index 26302f34f..9946a3de8 100644
--- a/interpreter/backends.html
+++ b/interpreter/backends.html
@@ -180,12 +180,12 @@ Compiler
Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
diff --git a/interpreter/options.html b/interpreter/options.html
index b2f53e563..d3bc360ae 100644
--- a/interpreter/options.html
+++ b/interpreter/options.html
@@ -179,10 +179,10 @@ The Hash Programming Language
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -191,7 +191,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
diff --git a/print.html b/print.html
index 26ebdd093..0ed356fc6 100644
--- a/print.html
+++ b/print.html
@@ -188,16 +188,16 @@
1
, 2
and 3
.
These perform foo == 1
, foo == 2
and foo == 3
in sequence, and the code follows the branch which succeeds first.
-If no branch succeeds, the _
branch is followed, which means "match anything".
+If no branch succeeds, the _
branch is followed, which means "match anything".
Literals can be integer literals for integer types (signed or unsigned), string literals for the str
type, or character literals for the char
type:#![allow(unused)]
fn main() {
match my_char {
- 'A' => print("First letter");
- 'B' => print("Second letter");
- x => print("Letter is: " + conv(x));
+ 'A' => print("First letter");
+ 'B' => print("Second letter");
+ x => print("Letter is: " + conv(x));
}
match my_str {
- "fizz" => print("Multiple of 3");
- "buzz" => print("Multiple of 5");
- "fizzbuzz" => print("Multiple of 15");
- _ => print("Not a multiple of 3 or 5");
+ "fizz" => print("Multiple of 3");
+ "buzz" => print("Multiple of 5");
+ "fizzbuzz" => print("Multiple of 15");
+ _ => print("Not a multiple of 3 or 5");
}
}
#![allow(unused)] fn main() { match fallible_operation() { // fallible_operation: () -> Result<f32, i32> - Ok(success) => print("Got success " + conv(result)); // success: f32 - Err(failure) => print("Got failure " + conv(failure)); // failure: i32 + Ok(success) => print("Got success " + conv(result)); // success: f32 + Err(failure) => print("Got failure " + conv(failure)); // failure: i32 } }
Tuple patterns
@@ -233,12 +233,12 @@Tuple patterns<
Cat := struct(name: str);
// Creating a tuple:
-my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
+my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
// Tuple pattern:
(Cat(name), elements) := my_val;
-assert(name == "Bob");
+assert(name == "Bob");
assert(elements == [1, 2, 3]);
}
Constructor patterns
@@ -250,11 +250,11 @@ Con
fn main() {
Option := <T> => enum(Some(value: T), None);
-my_val := Some("haha");
+my_val := Some("haha");
match my_val {
// Matching the Some(..) constructor
- Some(inner) => assert(inner == "haha"); // inner: str
+ Some(inner) => assert(inner == "haha"); // inner: str
// Matching the None constructor
None => assert(false);
}
@@ -265,14 +265,14 @@ Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -280,25 +280,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -322,10 +322,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -339,7 +339,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -350,9 +350,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -361,16 +361,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -401,27 +401,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
diff --git a/features/primitives.html b/features/primitives.html
index 388e75fbb..c90ca3726 100644
--- a/features/primitives.html
+++ b/features/primitives.html
@@ -223,15 +223,15 @@ Lists
// type
Grammar for lists:
-list_literal = "[" ( expr "," )* expr? "]"
-list_type = "[" type "]"
+list_literal = "[" ( expr "," )* expr? "]"
+list_type = "[" type "]"
Tuples
Tuples have a familiar syntax with many other languages:
- Empty tuples:
(,)
or ()
- Singleton tuple :
(A,)
-- Many membered tuple:
(A, B, C)
or (A, B, C,)
+- Many membered tuple:
(A, B, C)
or (A, B, C,)
Examples:
empty_tuple: (,) = (,);
@@ -242,7 +242,7 @@ Tuples
// ^^
// type
-some_tuple: (str, u32) = ("string", 12);
+some_tuple: (str, u32) = ("string", 12);
// ^^^^^^^^^^
// type
@@ -252,7 +252,7 @@ Tuples
If this is the case, you should consider using a structural data type which will allow you to do the same thing, and name the fields.
Read more about patterns here.
Grammar for tuples:
-tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
+tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
Named tuples
Named tuples are tuples that specify field names for each field within the tuple.
@@ -270,13 +270,13 @@
Named tuples
Then, you can create a Comment
instance and then access its fields like so:
comment := Comment(
- contents = "Hello, world",
+ contents = "Hello, world",
anchor = (
start = 2,
end = 4
),
edited = false,
- author_id = "f9erf8g43"
+ author_id = "f9erf8g43"
);
print(abs(comment.anchor.start - comment.anchor.end));
@@ -287,7 +287,7 @@ Named tuples
-Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
+
Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
However, this is not recommended because specifically naming tuples implies that the type
cares about the names of the fields rather than simply being a positionally structural type.
Sets
@@ -299,7 +299,7 @@ Sets
Many membered set: {A, B, C}
or {A, B, C,}
.
Set type: {A}
, for example foo: {i32} = {1, 2, 3}
.
-set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
+set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
Map
Maps in Hash represent collections of key-value pairs.
@@ -309,9 +309,9 @@
Map
Empty map: {:}
.
Singleton map : {A:1}
or {A:1,}
.
Many membered map: {A: 1, B: 2, C: 3}
or {A: 1, B: 2, C: 3,}
.
-Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
+Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
-map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
+map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
Note: the grammar for literal types can be found in the Types section.
diff --git a/features/structs-enums.html b/features/structs-enums.html
index f37d0fca0..ec3e1a4ef 100644
--- a/features/structs-enums.html
+++ b/features/structs-enums.html
@@ -197,9 +197,9 @@ Struct types}
Structs are nominal types.
An argument of type Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.
@@ -212,8 +212,8 @@ Struct types}
Enum types
Hash enums are similar to Rust enums or Haskell data types.
@@ -231,15 +231,15 @@
Enum types
Each variant can be paired with some data, in the form of a comma-separated list of types.
#![allow(unused)]
fn main() {
-err := NetworkError::Unexpected("something went terribly wrong", 32);
+err := NetworkError::Unexpected("something went terribly wrong", 32);
}
They can be match
ed to discover what they contain:
#![allow(unused)]
fn main() {
handle_error := (error: NetworkError) => match error {
- NoBytesReceived => print("No bytes received, stopping");
- ConnectionTerminated => print("Connection was terminated");
- Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
+ NoBytesReceived => print("No bytes received, stopping");
+ ConnectionTerminated => print("Connection was terminated");
+ Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
};
}
Like structs, enums are nominal types, rather than structural.
@@ -263,18 +263,18 @@
Generic types
Grammar
The grammar for struct definitions is as follows:
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
The grammar for enum definitions is as follows:
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
diff --git a/features/traits-impls.html b/features/traits-impls.html
index 1fbbe68f2..42929a9d0 100644
--- a/features/traits-impls.html
+++ b/features/traits-impls.html
@@ -196,18 +196,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -294,13 +294,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
diff --git a/features/type-functions.html b/features/type-functions.html
index 1af72daea..7e7f3b2eb 100644
--- a/features/type-functions.html
+++ b/features/type-functions.html
@@ -216,7 +216,7 @@ Type functions<
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
diff --git a/features/types.html b/features/types.html
index c860935b4..d7e29df30 100644
--- a/features/types.html
+++ b/features/types.html
@@ -192,33 +192,33 @@ Grammar
| union_type
| ref_type
-tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
+tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
-list_type = "[" type "]"
+list_type = "[" type "]"
-map_type = "{" type ":" type "}"
+map_type = "{" type ":" type "}"
-set_type = "{" type "}"
+set_type = "{" type "}"
-grouped_type = "(" type ")"
+grouped_type = "(" type ")"
named_type = access_name
-function_type_param = type | ( ident ":" type )
+function_type_param = type | ( ident ":" type )
-function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
+function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
-type_function_call_arg = type | ( ident "=" type )
-type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
+type_function_call_arg = type | ( ident "=" type )
+type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
-type_function_param = ident ( ":" type )? ( "=" type )?
+type_function_param = ident ( ":" type )? ( "=" type )?
-type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
+type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
-merge_type = ( type "~" )+ type
-union_type = ( type "|" )+ type
+merge_type = ( type "~" )+ type
+union_type = ( type "|" )+ type
-ref_type = "&" ( "raw" )? ( "mut" )? type
+ref_type = "&" ( "raw" )? ( "mut" )? type
diff --git a/interpreter/backends.html b/interpreter/backends.html
index 26302f34f..9946a3de8 100644
--- a/interpreter/backends.html
+++ b/interpreter/backends.html
@@ -180,12 +180,12 @@ Compiler
Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
diff --git a/interpreter/options.html b/interpreter/options.html
index b2f53e563..d3bc360ae 100644
--- a/interpreter/options.html
+++ b/interpreter/options.html
@@ -179,10 +179,10 @@ The Hash Programming Language
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -191,7 +191,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
diff --git a/print.html b/print.html
index 26ebdd093..0ed356fc6 100644
--- a/print.html
+++ b/print.html
@@ -188,16 +188,16 @@
Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -280,25 +280,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -322,10 +322,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -339,7 +339,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -350,9 +350,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -361,16 +361,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -401,27 +401,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
diff --git a/features/primitives.html b/features/primitives.html
index 388e75fbb..c90ca3726 100644
--- a/features/primitives.html
+++ b/features/primitives.html
@@ -223,15 +223,15 @@ Lists
// type
Grammar for lists:
-list_literal = "[" ( expr "," )* expr? "]"
-list_type = "[" type "]"
+list_literal = "[" ( expr "," )* expr? "]"
+list_type = "[" type "]"
Tuples
Tuples have a familiar syntax with many other languages:
- Empty tuples:
(,)
or ()
- Singleton tuple :
(A,)
-- Many membered tuple:
(A, B, C)
or (A, B, C,)
+- Many membered tuple:
(A, B, C)
or (A, B, C,)
Examples:
empty_tuple: (,) = (,);
@@ -242,7 +242,7 @@ Tuples
// ^^
// type
-some_tuple: (str, u32) = ("string", 12);
+some_tuple: (str, u32) = ("string", 12);
// ^^^^^^^^^^
// type
@@ -252,7 +252,7 @@ Tuples
If this is the case, you should consider using a structural data type which will allow you to do the same thing, and name the fields.
Read more about patterns here.
Grammar for tuples:
-tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
+tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
Named tuples
Named tuples are tuples that specify field names for each field within the tuple.
@@ -270,13 +270,13 @@
Named tuples
Then, you can create a Comment
instance and then access its fields like so:
comment := Comment(
- contents = "Hello, world",
+ contents = "Hello, world",
anchor = (
start = 2,
end = 4
),
edited = false,
- author_id = "f9erf8g43"
+ author_id = "f9erf8g43"
);
print(abs(comment.anchor.start - comment.anchor.end));
@@ -287,7 +287,7 @@ Named tuples
-Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
+
Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
However, this is not recommended because specifically naming tuples implies that the type
cares about the names of the fields rather than simply being a positionally structural type.
Sets
@@ -299,7 +299,7 @@ Sets
Many membered set: {A, B, C}
or {A, B, C,}
.
Set type: {A}
, for example foo: {i32} = {1, 2, 3}
.
-set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
+set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
Map
Maps in Hash represent collections of key-value pairs.
@@ -309,9 +309,9 @@
Map
Empty map: {:}
.
Singleton map : {A:1}
or {A:1,}
.
Many membered map: {A: 1, B: 2, C: 3}
or {A: 1, B: 2, C: 3,}
.
-Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
+Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
-map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
+map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
Note: the grammar for literal types can be found in the Types section.
diff --git a/features/structs-enums.html b/features/structs-enums.html
index f37d0fca0..ec3e1a4ef 100644
--- a/features/structs-enums.html
+++ b/features/structs-enums.html
@@ -197,9 +197,9 @@ Struct types}
Structs are nominal types.
An argument of type Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.
@@ -212,8 +212,8 @@ Struct types}
Enum types
Hash enums are similar to Rust enums or Haskell data types.
@@ -231,15 +231,15 @@
Enum types
Each variant can be paired with some data, in the form of a comma-separated list of types.
#![allow(unused)]
fn main() {
-err := NetworkError::Unexpected("something went terribly wrong", 32);
+err := NetworkError::Unexpected("something went terribly wrong", 32);
}
They can be match
ed to discover what they contain:
#![allow(unused)]
fn main() {
handle_error := (error: NetworkError) => match error {
- NoBytesReceived => print("No bytes received, stopping");
- ConnectionTerminated => print("Connection was terminated");
- Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
+ NoBytesReceived => print("No bytes received, stopping");
+ ConnectionTerminated => print("Connection was terminated");
+ Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
};
}
Like structs, enums are nominal types, rather than structural.
@@ -263,18 +263,18 @@
Generic types
Grammar
The grammar for struct definitions is as follows:
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
The grammar for enum definitions is as follows:
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
diff --git a/features/traits-impls.html b/features/traits-impls.html
index 1fbbe68f2..42929a9d0 100644
--- a/features/traits-impls.html
+++ b/features/traits-impls.html
@@ -196,18 +196,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -294,13 +294,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
diff --git a/features/type-functions.html b/features/type-functions.html
index 1af72daea..7e7f3b2eb 100644
--- a/features/type-functions.html
+++ b/features/type-functions.html
@@ -216,7 +216,7 @@ Type functions<
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
diff --git a/features/types.html b/features/types.html
index c860935b4..d7e29df30 100644
--- a/features/types.html
+++ b/features/types.html
@@ -192,33 +192,33 @@ Grammar
| union_type
| ref_type
-tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
+tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
-list_type = "[" type "]"
+list_type = "[" type "]"
-map_type = "{" type ":" type "}"
+map_type = "{" type ":" type "}"
-set_type = "{" type "}"
+set_type = "{" type "}"
-grouped_type = "(" type ")"
+grouped_type = "(" type ")"
named_type = access_name
-function_type_param = type | ( ident ":" type )
+function_type_param = type | ( ident ":" type )
-function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
+function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
-type_function_call_arg = type | ( ident "=" type )
-type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
+type_function_call_arg = type | ( ident "=" type )
+type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
-type_function_param = ident ( ":" type )? ( "=" type )?
+type_function_param = ident ( ":" type )? ( "=" type )?
-type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
+type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
-merge_type = ( type "~" )+ type
-union_type = ( type "|" )+ type
+merge_type = ( type "~" )+ type
+union_type = ( type "|" )+ type
-ref_type = "&" ( "raw" )? ( "mut" )? type
+ref_type = "&" ( "raw" )? ( "mut" )? type
diff --git a/interpreter/backends.html b/interpreter/backends.html
index 26302f34f..9946a3de8 100644
--- a/interpreter/backends.html
+++ b/interpreter/backends.html
@@ -180,12 +180,12 @@ Compiler
Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
diff --git a/interpreter/options.html b/interpreter/options.html
index b2f53e563..d3bc360ae 100644
--- a/interpreter/options.html
+++ b/interpreter/options.html
@@ -179,10 +179,10 @@ The Hash Programming Language
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -191,7 +191,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
diff --git a/print.html b/print.html
index 26ebdd093..0ed356fc6 100644
--- a/print.html
+++ b/print.html
@@ -188,16 +188,16 @@
#![allow(unused)] fn main() { match arr { - [a, b] => print(conv(a) + " " + conv(b)); - _ => print("Other"); // Matches everything other than [X, Y] for some X and Y + [a, b] => print(conv(a) + " " + conv(b)); + _ => print("Other"); // Matches everything other than [X, Y] for some X and Y } }
...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
spread
operator like so:#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
spread
operator is that you can only use it once in the list pattern.
@@ -322,10 +322,10 @@ diff --git a/features/primitives.html b/features/primitives.html index 388e75fbb..c90ca3726 100644 --- a/features/primitives.html +++ b/features/primitives.html @@ -223,15 +223,15 @@#![allow(unused)] fn main() { // imports only a and b from the module -{a, b} := import("./my_lib"); +{a, b} := import("./my_lib"); // imports c as my_c, and d from the module. -{c as my_c, d} := import("./other_lib"); +{c as my_c, d} := import("./other_lib"); // imports Cat from the nested module as NestedCat {Cat as NestedCat} := mod { @@ -339,7 +339,7 @@
Or-patterns
For example:@@ -350,9 +350,9 @@#![allow(unused)] fn main() { -symmetric_result: Result<str, str> := Ok("bilbobaggins"); +symmetric_result: Result<str, str> := Ok("bilbobaggins"); (Ok(inner) | Err(inner)) := symmetric_result; // inner: str }
Or-patterns
#![allow(unused)] fn main() { match color { - Red | Blue | Green => print("Primary additive"); - Cyan | Magenta | Yellow => print("Primary subtractive"); - _ => print("Unimportant color"); + Red | Blue | Green => print("Primary additive"); + Cyan | Magenta | Yellow => print("Primary subtractive"); + _ => print("Unimportant color"); } }
Conditional patterns
@@ -361,16 +361,16 @@Con fn main() { match my_result { Ok(inner) if inner > threshold * 2.0 => { - print("Phew, above twice the threshold"); + print("Phew, above twice the threshold"); }; Ok(inner) if inner > threshold => { - print("Phew, above the threshold but cutting it close!"); + print("Phew, above the threshold but cutting it close!"); }; Ok(inner) => { - print("The result was successful but the value was below the threshold"); + print("The result was successful but the value was below the threshold"); }; Err(_) => { - print("The result was unsuccessful... Commencing auto-destruct sequence."); + print("The result was unsuccessful... Commencing auto-destruct sequence."); auto_destruct(); }; } @@ -401,27 +401,27 @@
Grammar
| literal_pattern | list_pattern -or_pattern = ( single_pattern "|" )+ single_pattern +or_pattern = ( single_pattern "|" )+ single_pattern binding_pattern = identifier -tuple_pattern_member = identifier | ( identifier "=" single_pattern ) +tuple_pattern_member = identifier | ( identifier "=" single_pattern ) -constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )? +constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )? tuple_pattern = - | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" ) - | ( "(" tuple_pattern_member "," ")" ) + | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" ) + | ( "(" tuple_pattern_member "," ")" ) -module_pattern_member = identifier ( "as" single_pattern )? +module_pattern_member = identifier ( "as" single_pattern )? -module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}" +module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}" literal_pattern = integer_literal | string_literal | character_literal | float_literal -list_pattern_member = pattern | ( "..." identifier? ) +list_pattern_member = pattern | ( "..." identifier? ) -list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]" +list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"Lists
// type
Grammar for lists:
-list_literal = "[" ( expr "," )* expr? "]"
-list_type = "[" type "]"
+list_literal = "[" ( expr "," )* expr? "]"
+list_type = "[" type "]"
Tuples
Tuples have a familiar syntax with many other languages:
- Empty tuples:
(,)
or ()
- Singleton tuple :
(A,)
-- Many membered tuple:
(A, B, C)
or (A, B, C,)
+- Many membered tuple:
(A, B, C)
or (A, B, C,)
Examples:
empty_tuple: (,) = (,);
@@ -242,7 +242,7 @@ Tuples
// ^^
// type
-some_tuple: (str, u32) = ("string", 12);
+some_tuple: (str, u32) = ("string", 12);
// ^^^^^^^^^^
// type
@@ -252,7 +252,7 @@ Tuples
If this is the case, you should consider using a structural data type which will allow you to do the same thing, and name the fields.
Read more about patterns here.
Grammar for tuples:
-tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
+tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
Named tuples
Named tuples are tuples that specify field names for each field within the tuple.
@@ -270,13 +270,13 @@
Named tuples
Then, you can create a Comment
instance and then access its fields like so:
comment := Comment(
- contents = "Hello, world",
+ contents = "Hello, world",
anchor = (
start = 2,
end = 4
),
edited = false,
- author_id = "f9erf8g43"
+ author_id = "f9erf8g43"
);
print(abs(comment.anchor.start - comment.anchor.end));
@@ -287,7 +287,7 @@ Named tuples
-Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
+
Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
However, this is not recommended because specifically naming tuples implies that the type
cares about the names of the fields rather than simply being a positionally structural type.
Sets
@@ -299,7 +299,7 @@ Sets
Many membered set: {A, B, C}
or {A, B, C,}
.
Set type: {A}
, for example foo: {i32} = {1, 2, 3}
.
-set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
+set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
Map
Maps in Hash represent collections of key-value pairs.
@@ -309,9 +309,9 @@
Map
Empty map: {:}
.
Singleton map : {A:1}
or {A:1,}
.
Many membered map: {A: 1, B: 2, C: 3}
or {A: 1, B: 2, C: 3,}
.
-Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
+Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
-map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
+map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
Note: the grammar for literal types can be found in the Types section.
diff --git a/features/structs-enums.html b/features/structs-enums.html
index f37d0fca0..ec3e1a4ef 100644
--- a/features/structs-enums.html
+++ b/features/structs-enums.html
@@ -197,9 +197,9 @@ Struct types}
Structs are nominal types.
An argument of type Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.
@@ -212,8 +212,8 @@ Struct types}
Enum types
Hash enums are similar to Rust enums or Haskell data types. @@ -231,15 +231,15 @@
Enum types
Each variant can be paired with some data, in the form of a comma-separated list of types.#![allow(unused)] fn main() { -err := NetworkError::Unexpected("something went terribly wrong", 32); +err := NetworkError::Unexpected("something went terribly wrong", 32); }
They can be match
ed to discover what they contain:
#![allow(unused)] fn main() { handle_error := (error: NetworkError) => match error { - NoBytesReceived => print("No bytes received, stopping"); - ConnectionTerminated => print("Connection was terminated"); - Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") "); + NoBytesReceived => print("No bytes received, stopping"); + ConnectionTerminated => print("Connection was terminated"); + Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") "); }; }
Like structs, enums are nominal types, rather than structural. @@ -263,18 +263,18 @@
Generic types
Grammar
The grammar for struct definitions is as follows:
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
The grammar for enum definitions is as follows:
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
diff --git a/features/traits-impls.html b/features/traits-impls.html
index 1fbbe68f2..42929a9d0 100644
--- a/features/traits-impls.html
+++ b/features/traits-impls.html
@@ -196,18 +196,18 @@ Traits
Dog ~= Printable { // `Self = Dog` inferred - print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}"); + print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}"); };Now a Dog
is assignable to any type that has bound Printable
.
The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -294,13 +294,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
diff --git a/features/type-functions.html b/features/type-functions.html
index 1af72daea..7e7f3b2eb 100644
--- a/features/type-functions.html
+++ b/features/type-functions.html
@@ -216,7 +216,7 @@ Type functions<
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
diff --git a/features/types.html b/features/types.html
index c860935b4..d7e29df30 100644
--- a/features/types.html
+++ b/features/types.html
@@ -192,33 +192,33 @@ Grammar
| union_type
| ref_type
-tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
+tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
-list_type = "[" type "]"
+list_type = "[" type "]"
-map_type = "{" type ":" type "}"
+map_type = "{" type ":" type "}"
-set_type = "{" type "}"
+set_type = "{" type "}"
-grouped_type = "(" type ")"
+grouped_type = "(" type ")"
named_type = access_name
-function_type_param = type | ( ident ":" type )
+function_type_param = type | ( ident ":" type )
-function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
+function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
-type_function_call_arg = type | ( ident "=" type )
-type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
+type_function_call_arg = type | ( ident "=" type )
+type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
-type_function_param = ident ( ":" type )? ( "=" type )?
+type_function_param = ident ( ":" type )? ( "=" type )?
-type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
+type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
-merge_type = ( type "~" )+ type
-union_type = ( type "|" )+ type
+merge_type = ( type "~" )+ type
+union_type = ( type "|" )+ type
-ref_type = "&" ( "raw" )? ( "mut" )? type
+ref_type = "&" ( "raw" )? ( "mut" )? type
diff --git a/interpreter/backends.html b/interpreter/backends.html
index 26302f34f..9946a3de8 100644
--- a/interpreter/backends.html
+++ b/interpreter/backends.html
@@ -180,12 +180,12 @@ Compiler
Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
diff --git a/interpreter/options.html b/interpreter/options.html
index b2f53e563..d3bc360ae 100644
--- a/interpreter/options.html
+++ b/interpreter/options.html
@@ -179,10 +179,10 @@ The Hash Programming Language
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -191,7 +191,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
diff --git a/print.html b/print.html
index 26ebdd093..0ed356fc6 100644
--- a/print.html
+++ b/print.html
@@ -188,16 +188,16 @@
Name bindings
Basics
-Name bindings are made of three distinct components. The name, the type and the +
Name bindings are made of three distinct components. The name, the type and the value that is assigned to the name.
Declaration of variables happens using the :
and =
symbols:
x: i32 = 3;
The :
symbol is used to denote the type of a variable, and the =
symbol is used to assign a value to it.
The type can be omitted, in which case it is inferred:
x := "Terence Tao"; // `x: str` inferred
-x: str = "Terence Tao"; // same thing
-x: i32 = "Terence Tao"; // Compile error: `str` is not assignable to `i32`.
+x := "Terence Tao"; // `x: str` inferred
+x: str = "Terence Tao"; // same thing
+x: i32 = "Terence Tao"; // Compile error: `str` is not assignable to `i32`.
Declaration and assignment can happen separately:
x: i32:
@@ -208,12 +208,12 @@ Basics
A variable declaration is an expression like any other, and returns the value of the variable.
This means that you can write something like:
while (bytes_read := read_file(&buffer)) != 0 {
- print("Read some bytes!");
+ print("Read some bytes!");
}
Hash is statically typed, which means that variables cannot change types:
-x := "Ha!";
-x = Some("Baaa"); // Compile error: `Option<str>` is not assignable to `str`.
+x := "Ha!";
+x = Some("Baaa"); // Compile error: `Option<str>` is not assignable to `str`.
Mutability
By default, all bindings in Hash are constant.
@@ -247,12 +247,12 @@
Visibility
Grammar
The grammar for name bindings (and partial name bindings/reassignments) is as follows:
pattern = pattern_binding | ...(other patterns)
-pattern_binding = ( "pub" | "priv" )? "mut"? identifier
+pattern_binding = ( "pub" | "priv" )? "mut"? identifier
name_binding =
- | ( pattern ":=" expr ) // Declaration and assignment, infer type
- | ( pattern ( ":" type )? "=" expr ) // Assignment
- | ( pattern ( ":" type ) ) // Declaration
+ | ( pattern ":=" expr ) // Declaration and assignment, infer type
+ | ( pattern ( ":" type )? "=" expr ) // Assignment
+ | ( pattern ( ":" type ) ) // Declaration
Functions
Overview
@@ -295,12 +295,12 @@ fn main() {
func := (a: str, b = 'c', c = 2) -> u32 => { ... };
-func("foobar"); // a = "foobar", b = 'c', c = 2
+func("foobar"); // a = "foobar", b = 'c', c = 2
// You can optionally provide the arguments in declaration order:
-func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
+func("foobar", 'b', 3); // a = "foobar", b = 'b', c = 3
// Or you can provide them as named arguments:
-func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
+func("foobar", c = 3); // a = "foobar", b = 'c', c = 3
}
Named arguments can be used for more context when providing arguments, using the syntax arg_name = arg_value
.
After the first named argument is provided, all following arguments must be named.
@@ -308,25 +308,25 @@
foo := (a: str, b: str, c: str, d: str) => { .. };
-foo("a", "b", "c", "d") // Allowed -- no arguments are named.
-foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
-foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
-foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
-foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
+foo("a", "b", "c", "d") // Allowed -- no arguments are named.
+foo(a="a", b="b", c="c", d="d") // Allowed -- all arguments are named.
+foo("a", "b", c="c", d="d") // Allowed -- first two arguments are named.
+foo(a="a", "b", c="c", d="d") // Not allowed -- argument b must be named if a is named.
+foo("a", "b", c="c", "d") // Not allowed -- argument d must be named.
Grammar
The grammar for function definitions and function types is as follows:
function_param =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
-function_type = "(" ( function_param "," )* function_param? ")" "->" type
+function_def = "(" ( function_param "," )* function_param? ")" ( "->" type )? ("=>" expr)
+function_type = "(" ( function_param "," )* function_param? ")" "->" type
The grammar for function calls is as follows:
-function_call_arg = expr | ( ident "=" expr )
-function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
+function_call_arg = expr | ( ident "=" expr )
+function_call = expr "(" ( function_call_arg "," )* function_call_arg? ")"
Primitives
There are the following primitive types:
@@ -375,15 +375,15 @@ Lists
// type
Grammar for lists:
-list_literal = "[" ( expr "," )* expr? "]"
-list_type = "[" type "]"
+list_literal = "[" ( expr "," )* expr? "]"
+list_type = "[" type "]"
Tuples
Tuples have a familiar syntax with many other languages:
- Empty tuples:
(,)
or ()
- Singleton tuple :
(A,)
-- Many membered tuple:
(A, B, C)
or (A, B, C,)
+- Many membered tuple:
(A, B, C)
or (A, B, C,)
Examples:
empty_tuple: (,) = (,);
@@ -394,7 +394,7 @@ Tuples
// ^^
// type
-some_tuple: (str, u32) = ("string", 12);
+some_tuple: (str, u32) = ("string", 12);
// ^^^^^^^^^^
// type
@@ -404,7 +404,7 @@ Tuples
If this is the case, you should consider using a structural data type which will allow you to do the same thing, and name the fields.
Read more about patterns here.
Grammar for tuples:
-tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
+tuple_literal = ( "(" ( expr "," )* ")" ) | ( "(" ( expr "," )* expr ")" )
Named tuples
Named tuples are tuples that specify field names for each field within the tuple.
@@ -422,13 +422,13 @@
Named tuples
Then, you can create a Comment
instance and then access its fields like so:
comment := Comment(
- contents = "Hello, world",
+ contents = "Hello, world",
anchor = (
start = 2,
end = 4
),
edited = false,
- author_id = "f9erf8g43"
+ author_id = "f9erf8g43"
);
print(abs(comment.anchor.start - comment.anchor.end));
@@ -439,7 +439,7 @@ Named tuples
-Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
+
Named tuples can be coerced into unnamed tuples if the type layout of both tuples matches.
However, this is not recommended because specifically naming tuples implies that the type
cares about the names of the fields rather than simply being a positionally structural type.
Sets
@@ -451,7 +451,7 @@ Sets
Many membered set: {A, B, C}
or {A, B, C,}
.
Set type: {A}
, for example foo: {i32} = {1, 2, 3}
.
-set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
+set_literal = ( "{" "," "}" ) | ( "{" ( expr "," )+ "}" ) | ( "{" ( expr "," )* expr "}" )
Map
Maps in Hash represent collections of key-value pairs.
@@ -461,22 +461,22 @@
Map
Empty map: {:}
.
Singleton map : {A:1}
or {A:1,}
.
Many membered map: {A: 1, B: 2, C: 3}
or {A: 1, B: 2, C: 3,}
.
-Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
+Map type: {K:V}
, for example names: {str:str} = {"thom":"yorke", "jonny":"greenwood"}
.
-map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
+map_literal = ( "{" ":" "}" ) | ( "{" ( expr ":" expr "," )* ( expr ":" expr )? "}" )
Note: the grammar for literal types can be found in the Types section.
Conditional statements
-Conditional statements in the Hash
programming language are very similar to other languages such as Python, Javascript, C and Rust. However, there is one subtle difference, which is that the statement provided to a conditional statement must always evaluate to an explicit boolean value.
+Conditional statements in the Hash
programming language are very similar to other languages such as Python, Javascript, C and Rust. However, there is one subtle difference, which is that the statement provided to a conditional statement must always evaluate to an explicit boolean value.
If-else statements
If statements are very basic constructs in Hash. An example of a basic if-else
statement is as follows:
#![allow(unused)]
fn main() {
// checking if the value 'a' evaluates to being 'true'
-if a { print("a"); } else { print("b"); }
+if a { print("a"); } else { print("b"); }
// using a comparison operator
-if b == 2 { print("b is " + b); } else { print ("b is not " + conv(b)); }
+if b == 2 { print("b is " + b); } else { print ("b is not " + conv(b)); }
}
Obviously, this checks if the evaluation of a
returns a boolean value. If it does not
evaluate to something to be considered as true, then the block expression defined
@@ -486,31 +486,31 @@
If-else
#![allow(unused)]
fn main() {
if b == 2 {
- print("b is 2")
+ print("b is 2")
} else if b == 3 {
- print("b is 3")
+ print("b is 3")
} else {
- print("b isn't 2 or 3 ")
+ print("b isn't 2 or 3 ")
}
}
-As mentioned in the introduction, a conditional statement must evaluate an explicit boolean value. The if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements.
+As mentioned in the introduction, a conditional statement must evaluate an explicit boolean value. The if
statement syntax will not infer a boolean value from a statement within Hash
. This design feature is motivated by the fact that in many languages, common bugs and mistakes occur with the automatic inference of conditional statements.
An example of an invalid program is:
#![allow(unused)]
fn main() {
a: u8 = 12;
-if a { print("a") }
+if a { print("a") }
}
Additional syntax
Furthermore, if you do not want an else statement you can do:
#![allow(unused)]
fn main() {
-if a { print("a") } // if a is true, then execute
+if a { print("a") } // if a is true, then execute
}
which is syntactic sugar for:
#![allow(unused)]
fn main() {
-if a { print("a") } else {}
+if a { print("a") } else {}
}
Additionally, since the if
statement body's are also equivalent to functional bodies, you
can also specifically return a type as you would normally do within a function body:
@@ -543,16 +543,16 @@ }
Furthermore, for more complicated conditional statements, you can include an expression
block which is essentially treated as if it was a functional body, like so:
#![allow(unused)]
fn main() {
-f: str = "file.txt";
+f: str = "file.txt";
if { a = open(f); is_ok(a) } {
// run if is_ok(a) returns true
@@ -580,17 +580,17 @@ Match cases
a := input<u8>();
m2 := match a {
- 1 => "one";
- 2 => "two";
- _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
+ _ => "not one or two";
}
// Or as a function
convert: (x: u8) => str = (x) => match x {
- 1 => "one";
- 2 => "two";
- _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
+ _ => "not one or two";
}
m := convert(input<u8>());
@@ -600,8 +600,8 @@ Match cases
#![allow(unused)]
fn main() {
match x {
- 1 => "one";
- 2 => "two";
+ 1 => "one";
+ 2 => "two";
_ => unreachable(); // we know that 'x' should never be 1 or 2.
}
}
@@ -610,13 +610,13 @@ Match cases
#![allow(unused)]
fn main() {
convert: (x: u8) => str = (x) => match x {
- _ => "not one or two";
- 1 => "one";
- 2 => "two";
+ _ => "not one or two";
+ 1 => "one";
+ 2 => "two";
}
}
-The value of m
will always evaluate as "not one or two"
since the wildcard matches any condition.
-Match statements are also really good for destructing enum types in Hash.
+
The value of m
will always evaluate as "not one or two"
since the wildcard matches any condition.
+Match statements are also really good for destructing enum types in Hash.
For example,
#![allow(unused)]
fn main() {
@@ -631,8 +631,8 @@ Match cases
result: Result<u16, str> = Ok(12);
match result {
- Ok(value) => print("Got '" + conv(value) + "' from operation");
- Err(e) => panic("Failed to get result: " + e);
+ Ok(value) => print("Got '" + conv(value) + "' from operation");
+ Err(e) => panic("Failed to get result: " + e);
}
}
To specify multiple conditions for a single case within a match
statement, you can do so by
@@ -642,9 +642,9 @@
Match cases
x: u32 = input<u32>();
match x {
- 1 | 2 | 3 => print("x is 1, 2, or 3");
- 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator
- _ => print("x is something else");
+ 1 | 2 | 3 => print("x is 1, 2, or 3");
+ 4 | 5 | {2 | 4} => print("x is either 4, 5 or 6"); // using bitwise or operator
+ _ => print("x is something else");
}
}
To specify more complex conditional statements like and within the match case, you
@@ -655,10 +655,10 @@
Match cases
y: bool = true;
match x {
- 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true");
- {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator
- {2 | 4 if y} => print("x is 6 and y is true");
- _ => print("x is something else");
+ 1 | 2 | 3 if y => print("x is 1, 2, or 3 when y is true");
+ {4 if y} | y => print("x is 4 and y is true, or x is equal to y"); // using bitwise or operator
+ {2 | 4 if y} => print("x is 6 and y is true");
+ _ => print("x is something else");
}
}
Loop constructs
@@ -676,7 +676,7 @@General
For loop
Basics
For loops are special loop control statements that are designed to be used -with iterators.
+with iterators.For loops can be defined as:
-#![allow(unused)] fn main() { @@ -684,7 +684,7 @@
Basics
print(i); } }
Iterating over lists is also quite simple using the iter
function to
+
Iterating over lists is also quite simple using the iter
function to
convert the list into an iterator:
#![allow(unused)] fn main() { @@ -702,7 +702,7 @@
Basics
}
iterators
Iterators ship with the standard library, but you can define your own iterators via the Hash generic typing system.
-An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
+
An iterator I
of T
it means to have an implementation next<I, T>
in scope the current scope.
So, for the example above, the range
function is essentially a RangeIterator
of the u8
, u16
, u32
, ...
types.
More details about generics are here.
While loop
@@ -760,7 +760,7 @@}
-It is worth noting that the looping expression whether block or not must explicitly have the
+
It is worth noting that the looping expression whether block or not must explicitly have the
boolean
return type. For example, the code below will fail typechecking:
#![allow(unused)]
fn main() {
@@ -788,7 +788,7 @@ Loop
c: u64 = 1;
loop {
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
}
}
@@ -801,7 +801,7 @@ Loop
loop {
if c == 10 { break }
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
} // this will loop 10 times, and print all 10 times
}
@@ -813,7 +813,7 @@ Loop
c += 1;
if c % 2 != 0 { continue };
- print("I loop and I print when I get a " + c);
+ print("I loop and I print when I get a " + c);
} // this will loop 10 times, and print only when c is even
}
Operators & Symbols
@@ -879,13 +879,13 @@ Basics
As in many other languages, the programmer can specify the type of a variable or
a literal by using some special syntax. For example, in languages such as typescript,
-you can say that:
+you can say that:
#![allow(unused)]
fn main() {
some_value as str
}
which implies that you are asserting that some_value
is a string, this is essentially a way to avoid explicitly stating that type of a variable every
-single time and telling the compiler "Trust me some_value
is a string
".
+single time and telling the compiler "Trust me some_value
is a string
".
The principle is somewhat similar in Hash
, but it is more strictly enforced.
For example, within the statement x := 37;
, the type of x
can be any of the
integer types. This might lead to unexpected behaviour in future statements, where
@@ -893,37 +893,37 @@
Basics
So, you can either declare x
to be some integer type explicitly like so:
x: u32 = 37;
-Or you can, use as
to imply a type for a variable, which the compiler will assume
+
Or you can, use as
to imply a type for a variable, which the compiler will assume
to be true, like so:
x := 37 as u32;
Failing type assertions
If you specify a type assertion, the compiler will either attempt to infer this information from the left-hand side of the as
operator
-to the right. If the inference results in a different type to the right-hand side, this will raise a typechecking failure.
+to the right. If the inference results in a different type to the right-hand side, this will raise a typechecking failure.
For example, if you were to specify the expression:
#![allow(unused)]
fn main() {
-"A" as char
+"A" as char
}
The compiler will report this error as:
error[0001]: Types mismatch, got a `str`, but wanted a `char`.
--> <interactive>:1:8
-1 | "A" as char
+1 | "A" as char
| ^^^^ This specifies that the expression should be of type `char`
--> <interactive>:1:1
-1 | "A" as char
+1 | "A" as char
| ^^^ Found this to be of type `str`
Usefulness
Why are type assertions when there is already type inference within the language? Well, sometimes the type inference
-system does not have enough information to infer the types of variables and declarations.
+system does not have enough information to infer the types of variables and declarations.
Type inference may not have enough information when dealing with functions that are generic, so it can sometimes
-be useful to assert to the compiler that a given variable is a certain type.
+be useful to assert to the compiler that a given variable is a certain type.
Additionally, whilst the language is in an early stage of maturity and some things that are quirky or broken, type
assertions can come to the rescue and help the compiler to understand your program.
-In general, type assertions should be used when the compiler cannot infer the type of some expression with
+
In general, type assertions should be used when the compiler cannot infer the type of some expression with
the given information and needs assistance. You shouldn't need to use type assertions often.
Types
Grammar
@@ -941,33 +941,33 @@ Grammar
| union_type
| ref_type
-tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
+tuple_type = ( "(" ( type "," )* ")" ) | ( "(" ( type "," )+ type ")" )
-list_type = "[" type "]"
+list_type = "[" type "]"
-map_type = "{" type ":" type "}"
+map_type = "{" type ":" type "}"
-set_type = "{" type "}"
+set_type = "{" type "}"
-grouped_type = "(" type ")"
+grouped_type = "(" type ")"
named_type = access_name
-function_type_param = type | ( ident ":" type )
+function_type_param = type | ( ident ":" type )
-function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
+function_type = "(" ( function_type_param "," )* function_type_param? ")" "->" type
-type_function_call_arg = type | ( ident "=" type )
-type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
+type_function_call_arg = type | ( ident "=" type )
+type_function_call = ( grouped_type | named_type ) "<" ( type_function_call_arg "," )* type_function_call_arg? ">"
-type_function_param = ident ( ":" type )? ( "=" type )?
+type_function_param = ident ( ":" type )? ( "=" type )?
-type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
+type_function = "<" ( type_function_param "," )* type_function_param? ">" "->" type
-merge_type = ( type "~" )+ type
-union_type = ( type "|" )+ type
+merge_type = ( type "~" )+ type
+union_type = ( type "|" )+ type
-ref_type = "&" ( "raw" )? ( "mut" )? type
+ref_type = "&" ( "raw" )? ( "mut" )? type
Struct types
In Hash, structs are pre-defined collections of heterogeneous types, similar to C or Rust:
@@ -990,9 +990,9 @@ Grammar
name: str,
);
-d := Dog(name = "Bob");
+d := Dog(name = "Bob");
-print(d); // Dog(name = "Bob", age = 42)
+print(d); // Dog(name = "Bob", age = 42)
}
Structs are nominal types.
An argument of type Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.
@@ -1005,8 +1005,8 @@ Grammar
name: str,
);
-print(dog_name(d)); // "Bob"
-print(dog_name(FakeDog(age = 1, name = "Max"))); // Error: Type mismatch: was expecting `Dog`, got `FakeDog`.
+print(dog_name(d)); // "Bob"
+print(dog_name(FakeDog(age = 1, name = "Max"))); // Error: Type mismatch: was expecting `Dog`, got `FakeDog`.
}
Enum types
Hash enums are similar to Rust enums or Haskell data types.
@@ -1024,15 +1024,15 @@
Enum types
Each variant can be paired with some data, in the form of a comma-separated list of types.
#![allow(unused)]
fn main() {
-err := NetworkError::Unexpected("something went terribly wrong", 32);
+err := NetworkError::Unexpected("something went terribly wrong", 32);
}
They can be match
ed to discover what they contain:
#![allow(unused)]
fn main() {
handle_error := (error: NetworkError) => match error {
- NoBytesReceived => print("No bytes received, stopping");
- ConnectionTerminated => print("Connection was terminated");
- Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
+ NoBytesReceived => print("No bytes received, stopping");
+ ConnectionTerminated => print("Connection was terminated");
+ Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
};
}
Like structs, enums are nominal types, rather than structural.
@@ -1056,18 +1056,18 @@
Generic types
Grammar
The grammar for struct definitions is as follows:
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
The grammar for enum definitions is as follows:
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
Hash language modules
A module in Hash
can contain variable definitions, function definitions, type definitions or include other modules.
@@ -1083,17 +1083,17 @@
Importing
└── main.hash
Modules in hash allow for a source to be split up into smaller code fragments, allowing for better source code organisation and maintenance.
-You can import modules by specifying the path relative to the current path.
+You can import modules by specifying the path relative to the current path.
For example, if you wanted to include the modules a
, b
, and or c
within your main file
#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
-b := import("lib/b");
-c := import("lib/sub/c");
+a := import("lib/a");
+b := import("lib/b");
+c := import("lib/sub/c");
}
By doing so, you are placing everything that is defined within each of those modules under
-the namespace.
+the namespace.
Exporting
In order to export items from a module, use the pub
keyword.
For example:
@@ -1111,9 +1111,9 @@ Exporting
priv c := 1;
/// b.hash
-{ a } := import("a.hash"); // Ok
-{ b } := import("a.hash"); // Error: b is private
-{ c } := import("a.hash"); // Error: c is private.
+{ a } := import("a.hash"); // Ok
+{ b } := import("a.hash"); // Error: b is private
+{ c } := import("a.hash"); // Error: c is private.
}
Referencing exports
Furthermore, if the a
module contained a public structure definition like Point
:
@@ -1129,7 +1129,7 @@ Refer
#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
+a := import("lib/a");
p1 := a::Point(
x = 2,
@@ -1145,7 +1145,7 @@ Refer
follows:
#![allow(unused)]
fn main() {
-{ Point } := import("lib/a");
+{ Point } := import("lib/a");
p1 := Point(x=2, y=3);
}
@@ -1153,7 +1153,7 @@ Refer
can rename the exported members to your liking using the as
pattern operator:
#![allow(unused)]
fn main() {
-{ Point as LibPoint } = import("lib/a");
+{ Point as LibPoint } = import("lib/a");
p1 := LibPoint(x=2, y=3);
}
@@ -1169,17 +1169,17 @@ Inline modules<
};
// b.hash
-a := import("a.hash");
+a := import("a.hash");
red := a::nested::Colour::Red;
}
These follow the same conventions as .hash
files, and members need to be exported with pub
in order to be visible from the outside.
However, the external module items are always visible from within a mod
block, so in the above example, bar
can be used from within nested
.
Grammar
The grammar for file modules is as follows:
-file_module = ( expr ";" )*
+file_module = ( expr ";" )*
The grammar for mod
blocks (which are expressions) is as follows:
-mod_block = "mod" "{" ( expr ";" )* "}"
+mod_block = "mod" "{" ( expr ";" )* "}"
Patterns
Pattern matching is a very big part of Hash
and the productivity of the language.
@@ -1195,29 +1195,29 @@
Literal pat
foo := get_foo(); // foo: i32
match foo {
- 1 => print("Got one");
- 2 => print("Got two");
- 3 => print("Got three");
- _ => print("Got something else");
+ 1 => print("Got one");
+ 2 => print("Got two");
+ 3 => print("Got three");
+ _ => print("Got something else");
}
}
On the left-hand side of the match cases there are the literal patterns 1
, 2
and 3
.
These perform foo == 1
, foo == 2
and foo == 3
in sequence, and the code follows the branch which succeeds first.
-If no branch succeeds, the _
branch is followed, which means "match anything".
+If no branch succeeds, the _
branch is followed, which means "match anything".
Literals can be integer literals for integer types (signed or unsigned), string literals for the str
type, or character literals for the char
type:
#![allow(unused)]
fn main() {
match my_char {
- 'A' => print("First letter");
- 'B' => print("Second letter");
- x => print("Letter is: " + conv(x));
+ 'A' => print("First letter");
+ 'B' => print("Second letter");
+ x => print("Letter is: " + conv(x));
}
match my_str {
- "fizz" => print("Multiple of 3");
- "buzz" => print("Multiple of 5");
- "fizzbuzz" => print("Multiple of 15");
- _ => print("Not a multiple of 3 or 5");
+ "fizz" => print("Multiple of 3");
+ "buzz" => print("Multiple of 5");
+ "fizzbuzz" => print("Multiple of 15");
+ _ => print("Not a multiple of 3 or 5");
}
}
Binding patterns
@@ -1226,8 +1226,8 @@ Binding pat
#![allow(unused)]
fn main() {
match fallible_operation() { // fallible_operation: () -> Result<f32, i32>
- Ok(success) => print("Got success " + conv(result)); // success: f32
- Err(failure) => print("Got failure " + conv(failure)); // failure: i32
+ Ok(success) => print("Got success " + conv(result)); // success: f32
+ Err(failure) => print("Got failure " + conv(failure)); // failure: i32
}
}
Tuple patterns
@@ -1238,12 +1238,12 @@ Tuple patterns<
Cat := struct(name: str);
// Creating a tuple:
-my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
+my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
// Tuple pattern:
(Cat(name), elements) := my_val;
-assert(name == "Bob");
+assert(name == "Bob");
assert(elements == [1, 2, 3]);
}
Constructor patterns
@@ -1255,11 +1255,11 @@ Con
fn main() {
Option := <T> => enum(Some(value: T), None);
-my_val := Some("haha");
+my_val := Some("haha");
match my_val {
// Matching the Some(..) constructor
- Some(inner) => assert(inner == "haha"); // inner: str
+ Some(inner) => assert(inner == "haha"); // inner: str
// Matching the None constructor
None => assert(false);
}
@@ -1270,14 +1270,14 @@ Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -1285,25 +1285,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -1327,10 +1327,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -1344,7 +1344,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -1355,9 +1355,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -1366,16 +1366,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -1406,27 +1406,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
Traits and implementations
Traits
@@ -1448,18 +1448,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -1546,13 +1546,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
Type functions
Hash supports functions both at the value level and at the type level.
@@ -1594,7 +1594,7 @@
Grammar
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
@@ -1649,29 +1649,29 @@ Grammar
Memory
Still under construction.
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
+
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
-For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
+or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
dump_ast main := () => {
- println("Hello, world!");
+ println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
-#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
+#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
#![allow(unused)]
@@ -1681,9 +1681,9 @@ Token macros}
@@ -1748,15 +1748,15 @@ }
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -1768,9 +1768,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
@@ -1796,10 +1796,10 @@ Future
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -1808,7 +1808,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
@@ -1827,12 +1827,12 @@ Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
@@ -1857,8 +1857,8 @@ Planned bac
the language but contributing to it's development.
Loop transpilation
As mentioned at the start of the loops section in the basics chapter, the loop
control flow keyword
-is the most universal control flow since to you can use loop
to represent
-both the for
and while
loops.
+is the most universal control flow since to you can use loop
to represent
+both the for
and while
loops.
for loop transpilation
Since for
loops are used for iterators in hash, we transpile the construct into
a primitive loop. An iterator can be traversed by calling the next
function on the
@@ -1885,7 +1885,7 @@
i := [1,2,3,5].into_iter();
for x in i {
- print("x is " + x);
+ print("x is " + x);
}
@@ -1894,13 +1894,13 @@ }
While loop internal representation
-In general, a while loop transpilation process occurs by transferring the looping
+
In general, a while loop transpilation process occurs by transferring the looping
condition into a match block, which compares a boolean condition. If the boolean
condition evaluates to false
, the loop will immediately break
. Otherwise
the body expression is expected. A rough outline of what the transpilation process for a while
loop looks like:
@@ -1940,7 +1940,7 @@ }
If Statement transpilation
As mentioned at the start of the conditionals section in the basics chapter, if statements can be
-represented as match
statements. This is especially advised when you have many if
branched and
+represented as match
statements. This is especially advised when you have many if
branched and
more complicated branch conditions.
Internally, the compiler will convert if
statements into match cases so that it has to do
less work in the following stages of compilation.
@@ -1968,19 +1968,19 @@ #![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
} else {
- print("Neither")
+ print("Neither")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
- _ => { print("Neither") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
+ _ => { print("Neither") };
}
}
However, this representation is not entirely accurate because the compiler will optimise out some components
@@ -1994,21 +1994,21 @@
Missing '
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}
Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
Future features
This page is dedicated to documenting future planned features within the language.
diff --git a/searcher.js b/searcher.js
index d2b0aeed3..dc03e0a02 100644
--- a/searcher.js
+++ b/searcher.js
@@ -316,7 +316,7 @@ window.search = window.search || {};
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
boolean
return type. For example, the code below will fail typechecking:#![allow(unused)]
fn main() {
@@ -788,7 +788,7 @@ Loop
c: u64 = 1;
loop {
- print("I looped " + c + " times!");
+ print("I looped " + c + " times!");
c += 1;
}
}
#![allow(unused)]
fn main() {
some_value as str
}
some_value
is a string, this is essentially a way to avoid explicitly stating that type of a variable every
-single time and telling the compiler "Trust me some_value
is a string
". Hash
, but it is more strictly enforced.
For example, within the statement x := 37;
, the type of x
can be any of the
integer types. This might lead to unexpected behaviour in future statements, where
@@ -893,37 +893,37 @@ x
to be some integer type explicitly like so:x: u32 = 37;
as
to imply a type for a variable, which the compiler will assume
+as
to imply a type for a variable, which the compiler will assume
to be true, like so:x := 37 as u32;
as
operator
-to the right. If the inference results in a different type to the right-hand side, this will raise a typechecking failure. #![allow(unused)]
fn main() {
-"A" as char
+"A" as char
}
error[0001]: Types mismatch, got a `str`, but wanted a `char`.
--> <interactive>:1:8
-1 | "A" as char
+1 | "A" as char
| ^^^^ This specifies that the expression should be of type `char`
--> <interactive>:1:1
-1 | "A" as char
+1 | "A" as char
| ^^^ Found this to be of type `str`
Dog
can only be fulfilled by an instance of Dog
, and you can't pass in a struct that has the same fields but is of a different named type.#![allow(unused)]
fn main() {
-err := NetworkError::Unexpected("something went terribly wrong", 32);
+err := NetworkError::Unexpected("something went terribly wrong", 32);
}
match
ed to discover what they contain:#![allow(unused)]
fn main() {
handle_error := (error: NetworkError) => match error {
- NoBytesReceived => print("No bytes received, stopping");
- ConnectionTerminated => print("Connection was terminated");
- Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
+ NoBytesReceived => print("No bytes received, stopping");
+ ConnectionTerminated => print("Connection was terminated");
+ Unexpected(message, code) => print("An unexpected error occurred: " + err + " (" + conv(code) + ") ");
};
}
Grammar
struct_member =
- | ( ident ":=" expr ) // Declaration and assignment, infer type
- | ( ident ( ":" type )? "=" expr ) // Assignment
- | ( ident ( ":" type ) ) // Declaration
+ | ( ident ":=" expr ) // Declaration and assignment, infer type
+ | ( ident ( ":" type )? "=" expr ) // Assignment
+ | ( ident ( ":" type ) ) // Declaration
-struct_def := "struct" "(" struct_member* ")"
+struct_def := "struct" "(" struct_member* ")"
enum_member =
| ident // No fields
- | ident "(" struct_member* ")" // With fields
+ | ident "(" struct_member* ")" // With fields
-enum_def := "enum" "(" enum_member* ")"
+enum_def := "enum" "(" enum_member* ")"
Hash
can contain variable definitions, function definitions, type definitions or include other modules.
@@ -1083,17 +1083,17 @@ a
, b
, and or c
within your main file#![allow(unused)]
fn main() {
// main.hash
-a := import("lib/a");
-b := import("lib/b");
-c := import("lib/sub/c");
+a := import("lib/a");
+b := import("lib/b");
+c := import("lib/sub/c");
}
pub
keyword.
For example:a
module contained a public structure definition like Point
:#![allow(unused)] fn main() { // main.hash -a := import("lib/a"); +a := import("lib/a"); p1 := a::Point( x = 2, @@ -1145,7 +1145,7 @@
Refer follows:
@@ -1153,7 +1153,7 @@#![allow(unused)] fn main() { -{ Point } := import("lib/a"); +{ Point } := import("lib/a"); p1 := Point(x=2, y=3); }
Refer can rename the exported members to your liking using the
as
pattern operator:@@ -1169,17 +1169,17 @@#![allow(unused)] fn main() { -{ Point as LibPoint } = import("lib/a"); +{ Point as LibPoint } = import("lib/a"); p1 := LibPoint(x=2, y=3); }
Inline modules< }; // b.hash -a := import("a.hash"); +a := import("a.hash"); red := a::nested::Colour::Red; }
These follow the same conventions as .hash
files, and members need to be exported with pub
in order to be visible from the outside.
However, the external module items are always visible from within a mod
block, so in the above example, bar
can be used from within nested
.
Grammar
The grammar for file modules is as follows:
-file_module = ( expr ";" )*
+file_module = ( expr ";" )*
The grammar for mod
blocks (which are expressions) is as follows:
-mod_block = "mod" "{" ( expr ";" )* "}"
+mod_block = "mod" "{" ( expr ";" )* "}"
Patterns
Pattern matching is a very big part of Hash
and the productivity of the language.
@@ -1195,29 +1195,29 @@
Literal pat
foo := get_foo(); // foo: i32
match foo {
- 1 => print("Got one");
- 2 => print("Got two");
- 3 => print("Got three");
- _ => print("Got something else");
+ 1 => print("Got one");
+ 2 => print("Got two");
+ 3 => print("Got three");
+ _ => print("Got something else");
}
}
On the left-hand side of the match cases there are the literal patterns 1
, 2
and 3
.
These perform foo == 1
, foo == 2
and foo == 3
in sequence, and the code follows the branch which succeeds first.
-If no branch succeeds, the _
branch is followed, which means "match anything".
+If no branch succeeds, the _
branch is followed, which means "match anything".
Literals can be integer literals for integer types (signed or unsigned), string literals for the str
type, or character literals for the char
type:
#![allow(unused)] fn main() { match my_char { - 'A' => print("First letter"); - 'B' => print("Second letter"); - x => print("Letter is: " + conv(x)); + 'A' => print("First letter"); + 'B' => print("Second letter"); + x => print("Letter is: " + conv(x)); } match my_str { - "fizz" => print("Multiple of 3"); - "buzz" => print("Multiple of 5"); - "fizzbuzz" => print("Multiple of 15"); - _ => print("Not a multiple of 3 or 5"); + "fizz" => print("Multiple of 3"); + "buzz" => print("Multiple of 5"); + "fizzbuzz" => print("Multiple of 15"); + _ => print("Not a multiple of 3 or 5"); } }
Binding patterns
@@ -1226,8 +1226,8 @@Binding pat
#![allow(unused)]
fn main() {
match fallible_operation() { // fallible_operation: () -> Result<f32, i32>
- Ok(success) => print("Got success " + conv(result)); // success: f32
- Err(failure) => print("Got failure " + conv(failure)); // failure: i32
+ Ok(success) => print("Got success " + conv(result)); // success: f32
+ Err(failure) => print("Got failure " + conv(failure)); // failure: i32
}
}
Tuple patterns
@@ -1238,12 +1238,12 @@ Tuple patterns<
Cat := struct(name: str);
// Creating a tuple:
-my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
+my_val := (Cat("Bob"), [1, 2, 3]); // my_val: (Cat, [i32])
// Tuple pattern:
(Cat(name), elements) := my_val;
-assert(name == "Bob");
+assert(name == "Bob");
assert(elements == [1, 2, 3]);
}
Constructor patterns
@@ -1255,11 +1255,11 @@ Con
fn main() {
Option := <T> => enum(Some(value: T), None);
-my_val := Some("haha");
+my_val := Some("haha");
match my_val {
// Matching the Some(..) constructor
- Some(inner) => assert(inner == "haha"); // inner: str
+ Some(inner) => assert(inner == "haha"); // inner: str
// Matching the None constructor
None => assert(false);
}
@@ -1270,14 +1270,14 @@ Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -1285,25 +1285,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -1327,10 +1327,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -1344,7 +1344,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -1355,9 +1355,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -1366,16 +1366,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -1406,27 +1406,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
Traits and implementations
Traits
@@ -1448,18 +1448,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -1546,13 +1546,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
Type functions
Hash supports functions both at the value level and at the type level.
@@ -1594,7 +1594,7 @@
Grammar
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
@@ -1649,29 +1649,29 @@ Grammar
Memory
Still under construction.
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
+
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
-For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
+or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
dump_ast main := () => {
- println("Hello, world!");
+ println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
-#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
+#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
#![allow(unused)]
@@ -1681,9 +1681,9 @@ Token macros}
@@ -1748,15 +1748,15 @@ }
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -1768,9 +1768,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
@@ -1796,10 +1796,10 @@ Future
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -1808,7 +1808,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
@@ -1827,12 +1827,12 @@ Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
@@ -1857,8 +1857,8 @@ Planned bac
the language but contributing to it's development.
Loop transpilation
As mentioned at the start of the loops section in the basics chapter, the loop
control flow keyword
-is the most universal control flow since to you can use loop
to represent
-both the for
and while
loops.
+is the most universal control flow since to you can use loop
to represent
+both the for
and while
loops.
for loop transpilation
Since for
loops are used for iterators in hash, we transpile the construct into
a primitive loop. An iterator can be traversed by calling the next
function on the
@@ -1885,7 +1885,7 @@
i := [1,2,3,5].into_iter();
for x in i {
- print("x is " + x);
+ print("x is " + x);
}
@@ -1894,13 +1894,13 @@ }
While loop internal representation
-In general, a while loop transpilation process occurs by transferring the looping
+
In general, a while loop transpilation process occurs by transferring the looping
condition into a match block, which compares a boolean condition. If the boolean
condition evaluates to false
, the loop will immediately break
. Otherwise
the body expression is expected. A rough outline of what the transpilation process for a while
loop looks like:
@@ -1940,7 +1940,7 @@ }
If Statement transpilation
As mentioned at the start of the conditionals section in the basics chapter, if statements can be
-represented as match
statements. This is especially advised when you have many if
branched and
+represented as match
statements. This is especially advised when you have many if
branched and
more complicated branch conditions.
Internally, the compiler will convert if
statements into match cases so that it has to do
less work in the following stages of compilation.
@@ -1968,19 +1968,19 @@ #![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
} else {
- print("Neither")
+ print("Neither")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
- _ => { print("Neither") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
+ _ => { print("Neither") };
}
}
However, this representation is not entirely accurate because the compiler will optimise out some components
@@ -1994,21 +1994,21 @@
Missing '
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}
Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
Future features
This page is dedicated to documenting future planned features within the language.
diff --git a/searcher.js b/searcher.js
index d2b0aeed3..dc03e0a02 100644
--- a/searcher.js
+++ b/searcher.js
@@ -316,7 +316,7 @@ window.search = window.search || {};
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
#![allow(unused)]
fn main() {
match fallible_operation() { // fallible_operation: () -> Result<f32, i32>
- Ok(success) => print("Got success " + conv(result)); // success: f32
- Err(failure) => print("Got failure " + conv(failure)); // failure: i32
+ Ok(success) => print("Got success " + conv(result)); // success: f32
+ Err(failure) => print("Got failure " + conv(failure)); // failure: i32
}
}
Constructor patterns
@@ -1255,11 +1255,11 @@Con
fn main() {
Option := <T> => enum(Some(value: T), None);
-my_val := Some("haha");
+my_val := Some("haha");
match my_val {
// Matching the Some(..) constructor
- Some(inner) => assert(inner == "haha"); // inner: str
+ Some(inner) => assert(inner == "haha"); // inner: str
// Matching the None constructor
None => assert(false);
}
@@ -1270,14 +1270,14 @@ Con
Dog := struct(name: str, breed: str);
Dog(breed = dog_breed, name = dog_name) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // dog_breed: str, dog_name: str
// Same as:
Dog(name, breed) = Dog(
- name = "Bob",
- breed = "Husky"
+ name = "Bob",
+ breed = "Husky"
) // breed: str, name: str
}
List patterns
@@ -1285,25 +1285,25 @@ List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Only matches [] and [X] for some X
+ [a, b, ...] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Only matches [] and [X] for some X
}
}
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)]
fn main() {
match arr {
- [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
+ [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest));
[...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always []
- _ => print("Other"); // Only matches []
+ _ => print("Other"); // Only matches []
}
}
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -1327,10 +1327,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -1344,7 +1344,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -1355,9 +1355,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -1366,16 +1366,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -1406,27 +1406,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
Traits and implementations
Traits
@@ -1448,18 +1448,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -1546,13 +1546,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
Type functions
Hash supports functions both at the value level and at the type level.
@@ -1594,7 +1594,7 @@
Grammar
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
@@ -1649,29 +1649,29 @@ Grammar
Memory
Still under construction.
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
+
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
-For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
+or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
dump_ast main := () => {
- println("Hello, world!");
+ println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
-#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
+#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
#![allow(unused)]
@@ -1681,9 +1681,9 @@ Token macros}
@@ -1748,15 +1748,15 @@ }
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -1768,9 +1768,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
@@ -1796,10 +1796,10 @@ Future
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -1808,7 +1808,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
@@ -1827,12 +1827,12 @@ Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
@@ -1857,8 +1857,8 @@ Planned bac
the language but contributing to it's development.
Loop transpilation
As mentioned at the start of the loops section in the basics chapter, the loop
control flow keyword
-is the most universal control flow since to you can use loop
to represent
-both the for
and while
loops.
+is the most universal control flow since to you can use loop
to represent
+both the for
and while
loops.
for loop transpilation
Since for
loops are used for iterators in hash, we transpile the construct into
a primitive loop. An iterator can be traversed by calling the next
function on the
@@ -1885,7 +1885,7 @@
i := [1,2,3,5].into_iter();
for x in i {
- print("x is " + x);
+ print("x is " + x);
}
@@ -1894,13 +1894,13 @@ }
While loop internal representation
-In general, a while loop transpilation process occurs by transferring the looping
+
In general, a while loop transpilation process occurs by transferring the looping
condition into a match block, which compares a boolean condition. If the boolean
condition evaluates to false
, the loop will immediately break
. Otherwise
the body expression is expected. A rough outline of what the transpilation process for a while
loop looks like:
@@ -1940,7 +1940,7 @@ }
If Statement transpilation
As mentioned at the start of the conditionals section in the basics chapter, if statements can be
-represented as match
statements. This is especially advised when you have many if
branched and
+represented as match
statements. This is especially advised when you have many if
branched and
more complicated branch conditions.
Internally, the compiler will convert if
statements into match cases so that it has to do
less work in the following stages of compilation.
@@ -1968,19 +1968,19 @@ #![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
} else {
- print("Neither")
+ print("Neither")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
- _ => { print("Neither") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
+ _ => { print("Neither") };
}
}
However, this representation is not entirely accurate because the compiler will optimise out some components
@@ -1994,21 +1994,21 @@
Missing '
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}
Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
Future features
This page is dedicated to documenting future planned features within the language.
diff --git a/searcher.js b/searcher.js
index d2b0aeed3..dc03e0a02 100644
--- a/searcher.js
+++ b/searcher.js
@@ -316,7 +316,7 @@ window.search = window.search || {};
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
List patterns
@@ -1285,25 +1285,25 @@List patterns
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
#![allow(unused)]
fn main() {
match arr {
- [a, b] => print(conv(a) + " " + conv(b));
- _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
+ [a, b] => print(conv(a) + " " + conv(b));
+ _ => print("Other"); // Matches everything other than [X, Y] for some X and Y
}
}
The ...
spread operator can be used to capture or ignore the rest of the elements of the list at some position:
#![allow(unused)] fn main() { match arr { - [a, b, ...] => print(conv(a) + " " + conv(b)); - _ => print("Other"); // Only matches [] and [X] for some X + [a, b, ...] => print(conv(a) + " " + conv(b)); + _ => print("Other"); // Only matches [] and [X] for some X } }
If you want to match the remaining elements with some pattern, you can specify a pattern after the spread
operator like so:
#![allow(unused)] fn main() { match arr { - [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest)); + [a, b, ...rest] => print(conv(a) + " " + conv(b) + " " + conv(rest)); [...rest, c] => print(conv(c)); // Only matches [X] for some X, rest is always [] - _ => print("Other"); // Only matches [] + _ => print("Other"); // Only matches [] } }
One obvious limitation of the spread
operator is that you can only use it once in the list pattern.
@@ -1327,10 +1327,10 @@
Module patter
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -1344,7 +1344,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -1355,9 +1355,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -1366,16 +1366,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -1406,27 +1406,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
Traits and implementations
Traits
@@ -1448,18 +1448,18 @@ Traits
Dog ~= Printable {
// `Self = Dog` inferred
- print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}");
};
Now a Dog
is assignable to any type that has bound Printable
.
-The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
+The ~=
operator is the combination of ~
and =
operators, and it is equivalent to
Dog = Dog ~ Printable { ... };
-The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
+The ~
operator means "attach", and it is used to attach implementations of traits to structs and enums.
Trait implementations can be created without having to attach them to a specific type:
DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -1546,13 +1546,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
Type functions
Hash supports functions both at the value level and at the type level.
@@ -1594,7 +1594,7 @@
Grammar
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
@@ -1649,29 +1649,29 @@ Grammar
Memory
Still under construction.
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
+
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
-For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
+or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
dump_ast main := () => {
- println("Hello, world!");
+ println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
-#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
+#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
#![allow(unused)]
@@ -1681,9 +1681,9 @@ Token macros}
@@ -1748,15 +1748,15 @@ }
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -1768,9 +1768,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
@@ -1796,10 +1796,10 @@ Future
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -1808,7 +1808,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
@@ -1827,12 +1827,12 @@ Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
@@ -1857,8 +1857,8 @@ Planned bac
the language but contributing to it's development.
Loop transpilation
As mentioned at the start of the loops section in the basics chapter, the loop
control flow keyword
-is the most universal control flow since to you can use loop
to represent
-both the for
and while
loops.
+is the most universal control flow since to you can use loop
to represent
+both the for
and while
loops.
for loop transpilation
Since for
loops are used for iterators in hash, we transpile the construct into
a primitive loop. An iterator can be traversed by calling the next
function on the
@@ -1885,7 +1885,7 @@
i := [1,2,3,5].into_iter();
for x in i {
- print("x is " + x);
+ print("x is " + x);
}
@@ -1894,13 +1894,13 @@ }
While loop internal representation
-In general, a while loop transpilation process occurs by transferring the looping
+
In general, a while loop transpilation process occurs by transferring the looping
condition into a match block, which compares a boolean condition. If the boolean
condition evaluates to false
, the loop will immediately break
. Otherwise
the body expression is expected. A rough outline of what the transpilation process for a while
loop looks like:
@@ -1940,7 +1940,7 @@ }
If Statement transpilation
As mentioned at the start of the conditionals section in the basics chapter, if statements can be
-represented as match
statements. This is especially advised when you have many if
branched and
+represented as match
statements. This is especially advised when you have many if
branched and
more complicated branch conditions.
Internally, the compiler will convert if
statements into match cases so that it has to do
less work in the following stages of compilation.
@@ -1968,19 +1968,19 @@ #![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
} else {
- print("Neither")
+ print("Neither")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
- _ => { print("Neither") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
+ _ => { print("Neither") };
}
}
However, this representation is not entirely accurate because the compiler will optimise out some components
@@ -1994,21 +1994,21 @@
Missing '
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}
Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
Future features
This page is dedicated to documenting future planned features within the language.
diff --git a/searcher.js b/searcher.js
index d2b0aeed3..dc03e0a02 100644
--- a/searcher.js
+++ b/searcher.js
@@ -316,7 +316,7 @@ window.search = window.search || {};
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
#![allow(unused)]
fn main() {
// imports only a and b from the module
-{a, b} := import("./my_lib");
+{a, b} := import("./my_lib");
// imports c as my_c, and d from the module.
-{c as my_c, d} := import("./other_lib");
+{c as my_c, d} := import("./other_lib");
// imports Cat from the nested module as NestedCat
{Cat as NestedCat} := mod {
@@ -1344,7 +1344,7 @@ Or-patterns
For example:
#![allow(unused)]
fn main() {
-symmetric_result: Result<str, str> := Ok("bilbobaggins");
+symmetric_result: Result<str, str> := Ok("bilbobaggins");
(Ok(inner) | Err(inner)) := symmetric_result; // inner: str
}
@@ -1355,9 +1355,9 @@ Or-patterns
#![allow(unused)]
fn main() {
match color {
- Red | Blue | Green => print("Primary additive");
- Cyan | Magenta | Yellow => print("Primary subtractive");
- _ => print("Unimportant color");
+ Red | Blue | Green => print("Primary additive");
+ Cyan | Magenta | Yellow => print("Primary subtractive");
+ _ => print("Unimportant color");
}
}
Conditional patterns
@@ -1366,16 +1366,16 @@ Con
fn main() {
match my_result {
Ok(inner) if inner > threshold * 2.0 => {
- print("Phew, above twice the threshold");
+ print("Phew, above twice the threshold");
};
Ok(inner) if inner > threshold => {
- print("Phew, above the threshold but cutting it close!");
+ print("Phew, above the threshold but cutting it close!");
};
Ok(inner) => {
- print("The result was successful but the value was below the threshold");
+ print("The result was successful but the value was below the threshold");
};
Err(_) => {
- print("The result was unsuccessful... Commencing auto-destruct sequence.");
+ print("The result was unsuccessful... Commencing auto-destruct sequence.");
auto_destruct();
};
}
@@ -1406,27 +1406,27 @@ Grammar
| literal_pattern
| list_pattern
-or_pattern = ( single_pattern "|" )+ single_pattern
+or_pattern = ( single_pattern "|" )+ single_pattern
binding_pattern = identifier
-tuple_pattern_member = identifier | ( identifier "=" single_pattern )
+tuple_pattern_member = identifier | ( identifier "=" single_pattern )
-constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
+constructor_pattern = access_name ( "(" ( tuple_pattern_member "," )* tuple_pattern_member? ")" )?
tuple_pattern =
- | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
- | ( "(" tuple_pattern_member "," ")" )
+ | ( "(" ( tuple_pattern_member "," )+ tuple_pattern_member? ")" )
+ | ( "(" tuple_pattern_member "," ")" )
-module_pattern_member = identifier ( "as" single_pattern )?
+module_pattern_member = identifier ( "as" single_pattern )?
-module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
+module_pattern = "{" ( module_pattern_member "," )* module_pattern_member? "}"
literal_pattern = integer_literal | string_literal | character_literal | float_literal
-list_pattern_member = pattern | ( "..." identifier? )
+list_pattern_member = pattern | ( "..." identifier? )
-list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
+list_pattern = "[" ( list_pattern_member "," )* list_pattern_member? "]"
Traits and implementations
Traits
@@ -1448,18 +1448,18 @@Traits
Dog ~= Printable { // `Self = Dog` inferred - print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}"); + print = (self) => io::printf(f"Doge with name {self.name} and age {self.age}"); };Dog
is assignable to any type that has bound Printable
.~=
operator is the combination of ~
and =
operators, and it is equivalent to ~=
operator is the combination of ~
and =
operators, and it is equivalent toDog = Dog ~ Printable { ... };
~
operator means "attach", and it is used to attach implementations of traits to structs and enums.~
operator means "attach", and it is used to attach implementations of traits to structs and enums.DogPrintable := Printable {
Self = Dog, // Self can no longer be inferred, it needs to be explicitly specified.
- print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
+ print = (self) => io.printf(f"Doge with name {self.name} and age {self.age}");
};
doge := Dog(..);
@@ -1546,13 +1546,13 @@ Implementatio
By default, members of impl
blocks are public, but priv
can be written to make them private.
Grammar
The grammar for trait definitions is as follows:
-trait_def = "trait" "{" ( expr ";" )* "}"
+trait_def = "trait" "{" ( expr ";" )* "}"
The grammar for trait implementations is as follows:
-trait_impl = ident "{" ( expr ";" )* "}"
+trait_impl = ident "{" ( expr ";" )* "}"
The grammar for standalone impl
blocks is as follows:
-impl_block = "impl" "{" ( expr ";" )* "}"
+impl_block = "impl" "{" ( expr ";" )* "}"
Type functions
Hash supports functions both at the value level and at the type level.
@@ -1594,7 +1594,7 @@
Grammar
)
};
-my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
+my_ref_counted_string = make_ref_counted("Bilbo bing bong"); // `Inner = str` inferred
In order to explicitly infer specific arguments, you can use the _
sigil:
Convert := <I, O> => trait {
@@ -1649,29 +1649,29 @@ Grammar
Memory
Still under construction.
Macros
-This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
+
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on
tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
-or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
-for macros that don't have any additional arguments to the macro itself.
-For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
+or #[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand
+for macros that don't have any additional arguments to the macro itself.
+For example, the #dump_ast
macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
dump_ast main := () => {
- println("Hello, world!");
+ println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
-#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
+#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
-Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
-as the subject. A token tree is a sequence of tokens that are
-enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
+
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject>
or @[macro_name(macro_arg)] <subject>
. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
+as the subject. A token tree is a sequence of tokens that are
+enclosed in a pair of delimiters. Token trees are either [...]
, {...}
or (...)
. It is then up to the macro to define various
rules for accepting the token tree:
An example of using min
and max
macros:
#![allow(unused)]
@@ -1681,9 +1681,9 @@ Token macros}
@@ -1748,15 +1748,15 @@ }
Grammar
Formally, the macro syntax invocation can be written as follows:
-token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
+token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
-token_tree ::= "{" any "}"
- | "[" any "]"
- | "(" any ")";
+token_tree ::= "{" any "}"
+ | "[" any "]"
+ | "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
-module_macro_invocation ::= "#!" macro_args;
+module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
@@ -1768,9 +1768,9 @@ Grammar
| match_case
| enum_variant;
-macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
+macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
-macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
+macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;
@@ -1796,10 +1796,10 @@ Future
Interpretor command-line arguments
The Hash
interpreter has a number of options that you can enable when running an instance of
a VM. This page documents options and configurations that you can change when running a Hash
-interpreter.
+interpreter.
General overview
-e
, --execute
: Execute a command
-Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
+Set the mode of the interpreter to 'execute' mode implying to immediately run the provided script rather than launching as an interactive mode.
For example:
$ hash -e examples/compute_pi.hash
3.1415926535897
@@ -1808,7 +1808,7 @@ -h
, --help
: Print commandline help menu
-Displays a help dialogue on how to use the command line arguments with the hash interpreter.
+Displays a help dialogue on how to use the command line arguments with the hash interpreter.
-v
, --version
: Compiler version
Displays the current interpreter version with some additional debug information about the installed interpreter.
VM Specific options
@@ -1827,12 +1827,12 @@ Current backend
The current backend uses a Bytecode representation of the program which will run in a Virtual
machine that implements garbage collection. This is similar to Python's approach to running
-programs, but however as we all know, Python is incredibly terrible for performant code
+programs, but however as we all know, Python is incredibly terrible for performant code
(unless using C bindings).
We want to move away from using a Virtual machine as the main backend and actually provide
executables that can be run on x86_64
backend using either a native (naive) backend, and
LLVM.
-However, there are advantages to having a VM implementation for the language, which are
+
However, there are advantages to having a VM implementation for the language, which are
primarily:
- We can have an interactive mode, execute code on the fly (with a minor performance hit)
@@ -1857,8 +1857,8 @@ Planned bac
the language but contributing to it's development.
Loop transpilation
As mentioned at the start of the loops section in the basics chapter, the loop
control flow keyword
-is the most universal control flow since to you can use loop
to represent
-both the for
and while
loops.
+is the most universal control flow since to you can use loop
to represent
+both the for
and while
loops.
for loop transpilation
Since for
loops are used for iterators in hash, we transpile the construct into
a primitive loop. An iterator can be traversed by calling the next
function on the
@@ -1885,7 +1885,7 @@
i := [1,2,3,5].into_iter();
for x in i {
- print("x is " + x);
+ print("x is " + x);
}
@@ -1894,13 +1894,13 @@ }
While loop internal representation
-In general, a while loop transpilation process occurs by transferring the looping
+
In general, a while loop transpilation process occurs by transferring the looping
condition into a match block, which compares a boolean condition. If the boolean
condition evaluates to false
, the loop will immediately break
. Otherwise
the body expression is expected. A rough outline of what the transpilation process for a while
loop looks like:
@@ -1940,7 +1940,7 @@ }
match
statements. This is especially advised when you have many if
branched and
+represented as match
statements. This is especially advised when you have many if
branched and
more complicated branch conditions.if
statements into match cases so that it has to do
less work in the following stages of compilation.#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
} else {
- print("Neither")
+ print("Neither")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
- _ => { print("Neither") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
+ _ => { print("Neither") };
}
}
However, this representation is not entirely accurate because the compiler will optimise out some components @@ -1994,21 +1994,21 @@
Missing '
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}
Type inference
-🚧 Still under construction! 🚧
+🚧 Still under construction! 🚧
Future features
This page is dedicated to documenting future planned features within the language.
diff --git a/searcher.js b/searcher.js
index d2b0aeed3..dc03e0a02 100644
--- a/searcher.js
+++ b/searcher.js
@@ -316,7 +316,7 @@ window.search = window.search || {};
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; }
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
#![allow(unused)]
fn main() {
if conditionA {
- print("conditionA")
+ print("conditionA")
} else if conditionB {
- print("conditionB")
+ print("conditionB")
}
// Internally, this becomes:
match true {
- _ if conditionA => { print("conditionA") };
- _ if conditionB => { print("conditionB") };
+ _ if conditionA => { print("conditionA") };
+ _ if conditionB => { print("conditionB") };
_ => { };
}
}