From b6ab9e42c2737bf7c5e5ab8b43804752fcda23f0 Mon Sep 17 00:00:00 2001 From: feds01 Date: Sun, 18 Feb 2024 10:49:53 +0000 Subject: [PATCH] deploy: ab683a8e4c54ccdeaf41bb912ee6356c1de72e13 --- advanced/if-statement-transpilation.html | 22 +- advanced/loop-transpilation.html | 10 +- advanced/type-inference.html | 2 +- features/control-flow.html | 84 ++--- features/functions.html | 30 +- features/macros.html | 36 +- features/modules.html | 28 +- features/name-bindings.html | 22 +- features/operators.html | 20 +- features/patterns.html | 94 ++--- features/primitives.html | 22 +- features/structs-enums.html | 28 +- features/traits-impls.html | 14 +- features/type-functions.html | 2 +- features/types.html | 28 +- interpreter/backends.html | 4 +- interpreter/options.html | 6 +- print.html | 452 +++++++++++------------ searcher.js | 2 +- 19 files changed, 453 insertions(+), 453 deletions(-) diff --git a/advanced/if-statement-transpilation.html b/advanced/if-statement-transpilation.html index 5507a2280..adcda240f 100644 --- a/advanced/if-statement-transpilation.html +++ b/advanced/if-statement-transpilation.html @@ -178,7 +178,7 @@

The Hash Programming Language

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.

@@ -206,19 +206,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 @@ -232,16 +232,16 @@

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") };
   _ => { };
 }
 }
diff --git a/advanced/loop-transpilation.html b/advanced/loop-transpilation.html index 48b32fdc9..18afe7452 100644 --- a/advanced/loop-transpilation.html +++ b/advanced/loop-transpilation.html @@ -178,8 +178,8 @@

The Hash Programming Language

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 @@ -206,7 +206,7 @@

i := [1,2,3,5].into_iter(); for x in i { - print("x is " + x); + print("x is " + x); } @@ -215,13 +215,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:

diff --git a/advanced/type-inference.html b/advanced/type-inference.html index 1138cade2..565ec5e7c 100644 --- a/advanced/type-inference.html +++ b/advanced/type-inference.html @@ -177,7 +177,7 @@

The Hash Programming Language

Type inference

-

🚧 Still under construction! 🚧

+

🚧 Still under construction! 🚧

diff --git a/features/control-flow.html b/features/control-flow.html index 1402e1f27..5040b5a30 100644 --- a/features/control-flow.html +++ b/features/control-flow.html @@ -177,16 +177,16 @@

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? ")"
 

diff --git a/features/macros.html b/features/macros.html index a8ffa3cc0..88ab4f849 100644 --- a/features/macros.html +++ b/features/macros.html @@ -177,29 +177,29 @@

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.

+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)]
@@ -209,9 +209,9 @@ 

Token macros}

@@ -276,15 +276,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 
@@ -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 ";" )* "}"
 

diff --git a/features/name-bindings.html b/features/name-bindings.html index cdd67004b..76d002662 100644 --- a/features/name-bindings.html +++ b/features/name-bindings.html @@ -178,16 +178,16 @@

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
 
diff --git a/features/operators.html b/features/operators.html index 01e44a0ee..24de6a7c9 100644 --- a/features/operators.html +++ b/features/operators.html @@ -239,13 +239,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 @@ -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.

+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 matched 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:

    -
    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 matched 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:

    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();