Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New constant declration syntax #1062

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
18 changes: 9 additions & 9 deletions compiler/hash-ast-utils/src/pretty/mod.rs
Original file line number Diff line number Diff line change
@@ -154,7 +154,7 @@ where
&mut self,
node: ast::AstNodeRef<ast::Declaration>,
) -> Result<Self::DeclarationRet, Self::Error> {
let ast::Declaration { pat, ty, value } = node.body();
let ast::Declaration { pat, ty, value, is_constant } = node.body();

self.visit_pat(pat.ast_ref())?;

@@ -168,7 +168,12 @@ where
}

// Visit the initialiser
self.write("= ")?;
if *is_constant {
self.write(": ")?;
} else {
self.write("= ")?;
}

self.visit_expr(value.ast_ref())
}

@@ -1140,13 +1145,9 @@ where
&mut self,
node: ast::AstNodeRef<ast::AccessExpr>,
) -> Result<Self::AccessExprRet, Self::Error> {
let ast::AccessExpr { subject, property, kind } = node.body();
let ast::AccessExpr { subject, property } = node.body();
self.visit_expr(subject.ast_ref())?;

match kind {
ast::AccessKind::Namespace => self.write("::")?,
ast::AccessKind::Property => self.write(".")?,
}
self.write(".")?;

self.visit_property_kind(property.ast_ref())
}
@@ -1432,7 +1433,6 @@ where
Ty,
Pat,
Visibility,
AccessKind,
Mutability,
RefKind,
UnOp,
12 changes: 0 additions & 12 deletions compiler/hash-ast-utils/src/tree.rs
Original file line number Diff line number Diff line change
@@ -184,22 +184,10 @@ impl AstVisitor for AstTreePrinter {
vec![
TreeNode::branch("subject", vec![subject]),
TreeNode::branch("property", vec![property]),
TreeNode::leaf(labelled("kind", node.kind, "\"")),
],
))
}

type AccessKindRet = TreeNode;
fn visit_access_kind(
&self,
node: ast::AstNodeRef<ast::AccessKind>,
) -> Result<Self::AccessKindRet, Self::Error> {
match node.body() {
ast::AccessKind::Property => Ok(TreeNode::leaf("property")),
ast::AccessKind::Namespace => Ok(TreeNode::leaf("namespace")),
}
}

type RefExprRet = TreeNode;
fn visit_ref_expr(
&self,
26 changes: 4 additions & 22 deletions compiler/hash-ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1150,7 +1150,7 @@ define_tree! {
pub enum Pat {
/// An access pattern is one that denotes the access of a property from
/// another pattern. This is used to denote namespace accesses like
/// `a::b::c`
/// `a.b.c`
Access(AccessPat),

/// A simple binding pattern, assign some value to the name of the pattern
@@ -1269,6 +1269,9 @@ define_tree! {
/// Any value that is assigned to the binding, simply
/// an expression.
pub value: Child!(Expr),

/// Whether the declaration is constant or not.
pub is_constant: bool,
}

/// Unary operators that are defined within the core of the language.
@@ -1974,25 +1977,6 @@ define_tree! {
pub args: Children!(ExprArg),
}

/// A the kind of access an [AccessExpr] has
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[node]
pub enum AccessKind {
/// A namespace access, i.e. `a::b`
Namespace,
/// A property access, i.e. `a.b`
Property,
}

impl Display for AccessKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessKind::Namespace => write!(f, "namespace"),
AccessKind::Property => write!(f, "property"),
}
}
}

/// The kind of property that's being accessed, either being
/// named or numeric, e.g `foo.x`, `foo.1`, etc.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -2013,8 +1997,6 @@ define_tree! {
pub subject: Child!(Expr),
/// The property of the subject to access.
pub property: Child!(PropertyKind),
/// The kind of access, either namespacing or property
pub kind: AccessKind,
}

/// A typed expression, e.g. `foo as int`.
79 changes: 45 additions & 34 deletions compiler/hash-parser/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -58,10 +58,7 @@ impl<'s> AstGen<'s> {
// pattern which is then followed by a `:` to denote that this is a
// declaration.
if self.begins_pat() {
let pat = self.parse_singular_pat()?;
self.parse_token(TokenKind::Colon)?;
let decl = self.parse_declaration(pat)?;

let decl = self.parse_declaration()?;
let expr = self.node_with_joined_span(Expr::Declaration(decl), start);
let semi = maybe_eat_semi(self);
return Ok(Some((semi, expr)));
@@ -365,7 +362,6 @@ impl<'s> AstGen<'s> {
subject = match token.kind {
// Property access or method call
TokenKind::Dot => self.parse_property_access(subject, subject_span)?,
TokenKind::Access => self.parse_ns_access(subject, subject_span)?,
TokenKind::Lt => match self.maybe_parse_implicit_call(subject, subject_span) {
(subject, true) => subject,
// Essentially break because the type_args failed
@@ -581,19 +577,53 @@ impl<'s> AstGen<'s> {
/// ```text
/// some_var: f64 = ...;
/// ^^^^^^^^ ^^^ ^^^─────┐
/// pattern type the right hand-side expr
/// pattern annotation the right hand-side expr
/// ```
pub(crate) fn parse_declaration(&mut self, pat: AstNode<Pat>) -> ParseResult<Declaration> {
// Attempt to parse an optional type...
pub(crate) fn parse_declaration(&mut self) -> ParseResult<Declaration> {
let pat = self.parse_singular_pat()?;
let mut is_constant = false;

// Figure out if this declaration has an annotation or not...
let ty = match self.peek_kind() {
Some(TokenKind::Eq) => None,
_ => Some(self.parse_ty()?),
Some(TokenKind::Access) => {
self.skip_fast(TokenKind::Access); // `::`
is_constant = true;
None
}
_ => {
self.parse_token(TokenKind::Colon)?; // `:`

if self.peek_kind() == Some(TokenKind::Eq) {
self.skip_fast(TokenKind::Eq); // `=`
None
} else {
Some(self.parse_ty()?)
}
}
};

// Now parse the initialiser...
self.parse_token(TokenKind::Eq)?;
if !is_constant && ty.is_some() {
match self.peek_kind() {
Some(TokenKind::Eq) => {
self.skip_fast(TokenKind::Eq); // `=`
}
Some(TokenKind::Colon) => {
self.skip_fast(TokenKind::Colon); // `=`
is_constant = true;
}
tok => {
return self.err(
ParseErrorKind::UnExpected,
ExpectedItem::Colon | ExpectedItem::Eq,
tok,
)
}
}
}

let value = self.parse_expr_with_precedence(0)?;
Ok(Declaration { pat, ty, value })
Ok(Declaration { pat, ty, value, is_constant })
}

/// Given a initial left-hand side expression, attempt to parse a
@@ -636,8 +666,7 @@ impl<'s> AstGen<'s> {
}
}

/// Parse a property access expression, in other words an [AccessExpr] with
/// the [AccessKind::Property] variant.
/// Parse a property access expression, in other words an [AccessExpr].
pub(crate) fn parse_property_access(
&mut self,
subject: AstNode<Expr>,
@@ -675,32 +704,14 @@ impl<'s> AstGen<'s> {
let property = self.node_with_span(PropertyKind::NumericField(value), token.span);

return Ok(self.node_with_joined_span(
Expr::Access(AccessExpr { subject, property, kind: AccessKind::Property }),
Expr::Access(AccessExpr { subject, property }),
subject_span,
));
}
}

let property = self.parse_named_field(ParseErrorKind::ExpectedPropertyAccess)?;
Ok(self.node_with_joined_span(
Expr::Access(AccessExpr { subject, property, kind: AccessKind::Property }),
subject_span,
))
}

/// Parse a [AccessExpr] with a `namespace` access kind.
pub(crate) fn parse_ns_access(
&mut self,
subject: AstNode<Expr>,
subject_span: ByteRange,
) -> ParseResult<AstNode<Expr>> {
self.skip_fast(TokenKind::Access); // `::`

let property = self.parse_named_field(ParseErrorKind::ExpectedName)?;
Ok(self.node_with_joined_span(
Expr::Access(AccessExpr { subject, property, kind: AccessKind::Namespace }),
subject_span,
))
Ok(self.node_with_joined_span(Expr::Access(AccessExpr { subject, property }), subject_span))
}

/// Function to either parse an expression that is wrapped in parentheses or
22 changes: 12 additions & 10 deletions compiler/hash-parser/src/parser/pat.rs
Original file line number Diff line number Diff line change
@@ -112,8 +112,8 @@ impl<'s> AstGen<'s> {
}
// An access pattern which accesses the `subject` with a particular `property`
// denotes with a name.
TokenKind::Access => {
self.skip_fast(TokenKind::Access); // `::`
TokenKind::Dot => {
self.skip_fast(TokenKind::Dot); // `.`
let property = self.parse_name()?;
self.node_with_joined_span(Pat::Access(AccessPat { subject, property }), span)
}
@@ -554,9 +554,11 @@ impl<'s> AstGen<'s> {
}

fn peek_pat(&self) -> bool {
macro_rules! peek_colon(
// This is a macro that is used to simplify the lookahead for the pattern
// boundary, which can either be a `:` or a `::` token.
macro_rules! peek_pat_boundary(
() => {
matches!(self.peek_kind(), Some(TokenKind::Colon))
matches!(self.peek_kind(), Some(TokenKind::Colon | TokenKind::Access))
}
);

@@ -575,7 +577,7 @@ impl<'s> AstGen<'s> {
&& kind.is_range_lit()
{
self.skip_fast(kind);
peek_colon!()
peek_pat_boundary!()
} else {
false
}
@@ -587,12 +589,12 @@ impl<'s> AstGen<'s> {
// Other general literal patterns.
Some(kind) if kind.is_lit() => {
self.skip_fast(kind);
peek_colon!()
peek_pat_boundary!()
}
// Module, Array, Tuple patterns.
Some(TokenKind::Tree(_, _)) => {
self.skip_token();
peek_colon!()
peek_pat_boundary!()
}
// Identifier or constructor pattern.
Some(ident @ TokenKind::Ident(_)) => {
@@ -606,8 +608,8 @@ impl<'s> AstGen<'s> {
TokenKind::Tree(Delimiter::Paren, _) => self.skip_token(),
// Handle the `access` pattern case. We're looking for the next
// three tokens to be `::Ident`
TokenKind::Access => {
self.skip_fast(TokenKind::Access);
TokenKind::Dot => {
self.skip_fast(TokenKind::Dot); // `.`

match self.peek_kind() {
Some(ident @ TokenKind::Ident(_)) => {
@@ -620,7 +622,7 @@ impl<'s> AstGen<'s> {
}
}

peek_colon!()
peek_pat_boundary!()
}
// This is the case for a bind that has a visibility modifier at the beginning. In
// this scenario, it can be followed by a `mut` modifier and then a identifier or
4 changes: 2 additions & 2 deletions compiler/hash-parser/src/parser/ty.rs
Original file line number Diff line number Diff line change
@@ -219,8 +219,8 @@ impl<'s> AstGen<'s> {
Ty::Fn(FnTy { params: self.make_params(params, ParamOrigin::Fn), return_ty })
}

TokenKind::Access => {
self.skip_fast(TokenKind::Access);
TokenKind::Dot => {
self.skip_fast(TokenKind::Dot); // .

Ty::Access(AccessTy {
subject: self.node_with_joined_span(ty, span),
42 changes: 18 additions & 24 deletions compiler/hash-semantics/src/passes/resolution/exprs.rs
Original file line number Diff line number Diff line change
@@ -246,27 +246,24 @@ impl<E: SemanticEnv> ResolutionPass<'_, E> {
&self,
node: AstNodeRef<'a, ast::AccessExpr>,
) -> SemanticResult<Option<AstPath<'a>>> {
match node.kind {
ast::AccessKind::Namespace => match node.property.body() {
ast::PropertyKind::NamedField(name) => {
let mut root =
self.expr_as_ast_path(node.body.subject.ast_ref())?.ok_or_else(|| {
SemanticError::InvalidNamespaceSubject { location: node.span() }
})?;
root.push(AstPathComponent {
name: *name,
name_node_id: node.property.id(),
args: Node::at(vec![], NodeOrigin::Given(node.id())),
node_id: node.id(),
});
Ok(Some(root))
}
ast::PropertyKind::NumericField(_) => {
// Should have been caught at semantics
panic_on_span!(node.span(), "Namespace followed by numeric field found")
}
},
ast::AccessKind::Property => Ok(None),
match node.property.body() {
ast::PropertyKind::NamedField(name) => {
let mut root =
self.expr_as_ast_path(node.body.subject.ast_ref())?.ok_or_else(|| {
SemanticError::InvalidNamespaceSubject { location: node.span() }
})?;
root.push(AstPathComponent {
name: *name,
name_node_id: node.property.id(),
args: Node::at(vec![], NodeOrigin::Given(node.id())),
node_id: node.id(),
});
Ok(Some(root))
}
ast::PropertyKind::NumericField(_) => {
// Should have been caught at semantics
panic_on_span!(node.span(), "Namespace followed by numeric field found")
}
}
}

@@ -422,9 +419,6 @@ impl<E: SemanticEnv> ResolutionPass<'_, E> {
self.make_term_from_resolved_ast_path(&resolved_path, node.id())
}
None => {
// Namespace handled above.
assert!(matches!(node.kind, ast::AccessKind::Property));

let subject = self.make_term_from_ast_expr(node.subject.ast_ref())?;
let field = match node.property.body() {
ast::PropertyKind::NamedField(name) => ParamIndex::Name(*name),
8 changes: 4 additions & 4 deletions stdlib/eq.hash
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
subst := <A, a: A, b: A> => #pure (P: A -> Type, p: a ~ b, m: {P(a)}) -> {P(b)} => {
match p {
Equal::Refl(x) => m
Equal.Refl(x) => m
}
}

transport := <A, C, a: A, b: A> => (P: (y: A) -> C, p: a ~ b) -> {P(a)} ~ {P(b)} => {
match p {
Equal::Refl(x) => Equal::Refl(P(x))
Equal.Refl(x) => Equal.Refl(P(x))
}
}

sym := <A, a: A, b: A> => (p: a ~ b) -> b ~ a => {
match p {
Equal::Refl(_) => p
Equal.Refl(_) => p
}
}

trans := <A, a: A, b: A, c: A> => (p: a ~ b, q: b ~ c) -> a ~ c => {
match p {
Equal::Refl(_) => q
Equal.Refl(_) => q
}
}
38 changes: 19 additions & 19 deletions stdlib/prelude.hash
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ eq := import("eq")
/// The `cast(..)` function is used to cast some value into another
/// type provided that the types are cast compatible.
cast := <T, U> => (item: T) -> U => {
Intrinsics::cast(T, U, item)
Intrinsics.cast(T, U, item)
}

/// This is powerful mechanism that
@@ -25,7 +25,7 @@ cast := <T, U> => (item: T) -> U => {
/// allows for the program to now read the `bytes` and `length` fields of the
/// `SizedPointer` type.
transmute := <T,U> => (item: T) -> U => {
Intrinsics::transmute(T, U, item)
Intrinsics.transmute(T, U, item)
}

#[repr("c")]
@@ -42,12 +42,12 @@ print := (msg: str, /* end: char = '\n' */) => {
// stdout.
SizedPointer(bytes, len) := transmute<str, SizedPointer>(msg);

libc::write(STDOUT, bytes, len);
libc.write(STDOUT, bytes, len);

// @@Todo: un-comment this when default parameters are working
// write the end character
// end_sep := Intrinsics::cast(type char, type u8, '\n');
// libc::write(1, &raw end_sep, 1);
// end_sep := Intrinsics.cast(type char, type u8, '\n');
// libc.write(1, &raw end_sep, 1);
}

println := (message: str) => {
@@ -56,47 +56,47 @@ println := (message: str) => {
}

refl := <T, a: T> => () -> {a ~ a} => {
Equal::Refl(a)
Equal.Refl(a)
}

dbg := <T> => (item: T) -> T => {
Intrinsics::debug_print(T, item)
Intrinsics.debug_print(T, item)
item
}

#lang panic := (msg: str) -> ! => {
print(msg);
Intrinsics::abort()
Intrinsics.abort()
}

#lang
str_eq := (left: str, right: str) => {
SizedPointer(left_data, left_len) := transmute<_, SizedPointer>(left);
SizedPointer(right_data, right_len) := transmute<_, SizedPointer>(right);

left_len == right_len && Intrinsics::memcmp(left_data, right_data, left_len) == 0
left_len == right_len && Intrinsics.memcmp(left_data, right_data, left_len) == 0
}


libc := mod {
/// The `char` is equivalent to an `i32` but avoid having to make the cast.
#foreign putwchar := (c: char) -> i32 => { Intrinsics::abort() }
#foreign putwchar := (c: char) -> i32 => { Intrinsics.abort() }

#foreign write := (fd: i32, buf: &raw u8, len: usize) -> isize => { Intrinsics::abort() }
#foreign write := (fd: i32, buf: &raw u8, len: usize) -> isize => { Intrinsics.abort() }

#foreign read := (fd: i32, buf: &raw u8, len: usize) -> isize => { Intrinsics::abort() }
#foreign read := (fd: i32, buf: &raw u8, len: usize) -> isize => { Intrinsics.abort() }

#foreign open := (path: &raw u8, flags: i32, mode: i32) -> i32 => { Intrinsics::abort() }
#foreign open := (path: &raw u8, flags: i32, mode: i32) -> i32 => { Intrinsics.abort() }

#foreign close := (fd: i32) -> i32 => { Intrinsics::abort() }
#foreign close := (fd: i32) -> i32 => { Intrinsics.abort() }

#foreign exit := (status: i32) => { Intrinsics::abort() }
#foreign exit := (status: i32) => { Intrinsics.abort() }

#foreign malloc := (size: usize) -> &raw u8 => { Intrinsics::abort() }
#foreign malloc := (size: usize) -> &raw u8 => { Intrinsics.abort() }

#foreign free := (ptr: &raw u8) -> () => { Intrinsics::abort() }
#foreign free := (ptr: &raw u8) -> () => { Intrinsics.abort() }

#foreign memcpy := (dest: &raw u8, src: &raw u8, n: usize) -> &raw u8 => { Intrinsics::abort() }
#foreign memcpy := (dest: &raw u8, src: &raw u8, n: usize) -> &raw u8 => { Intrinsics.abort() }

#foreign memcmp := (s1: &raw u8, s2: &raw u8, n: usize) -> i32 => { Intrinsics::abort() }
#foreign memcmp := (s1: &raw u8, s2: &raw u8, n: usize) -> i32 => { Intrinsics.abort() }
}
12 changes: 6 additions & 6 deletions tests/cases/codegen/discriminants.hash
Original file line number Diff line number Diff line change
@@ -12,9 +12,9 @@ Direction := enum(
)

main := () => {
east := Direction::Right;
east := Direction.Right;
foo(east);
south := Direction::Down;
south := Direction.Down;
bar(south);
}

@@ -33,10 +33,10 @@ bar := (dir: Direction) => {

foo := (dir: Direction) => {
match dir {
Direction::Up => {
Direction.Up => {
println("Up");
},
Direction::Right => {
Direction.Right => {
t := transmute<_, i8>(dir);

if t == 63 {
@@ -45,10 +45,10 @@ foo := (dir: Direction) => {
println("Not 63");
}
},
Direction::Down => {
Direction.Down => {
println("Down");
},
Direction::Left => {
Direction.Left => {
println("Left");
},
}
10 changes: 5 additions & 5 deletions tests/cases/exhaustiveness/enums.hash
Original file line number Diff line number Diff line change
@@ -14,11 +14,11 @@ Pos := struct(

next_pos := (pos: Pos, dir: Direction, end: Pos) -> Option<Pos> => {
match dir {
Direction::South if pos.y != end.y => Option::Some(Pos(pos.x, pos.y + 1)),
Direction::North if pos.y != 0 => Option::Some(Pos(pos.x, pos.y - 1)),
Direction::East if pos.x != end.x => Option::Some(Pos(pos.x + 1, pos.y)),
Direction::West if pos.x != 0 => Option::Some(Pos(pos.x - 1, pos.y)),
_ => Option::None
Direction.South if pos.y != end.y => Option.Some(Pos(pos.x, pos.y + 1)),
Direction.North if pos.y != 0 => Option.Some(Pos(pos.x, pos.y - 1)),
Direction.East if pos.x != end.x => Option.Some(Pos(pos.x + 1, pos.y)),
Direction.West if pos.x != 0 => Option.Some(Pos(pos.x - 1, pos.y)),
_ => Option.None
}
}

18 changes: 9 additions & 9 deletions tests/cases/exhaustiveness/missing_enums.hash
Original file line number Diff line number Diff line change
@@ -4,9 +4,9 @@
maybe_add := (left: Option<i32>, right: Option<i32>) -> i32 => {
loop {
return match (left, right) {
(Option::Some(a), Option::Some(b)) => a + b,
(Option::Some(a), Option::None) => a,
(Option::None, Option::Some(a)) => {a},
(Option.Some(a), Option.Some(b)) => a + b,
(Option.Some(a), Option.None) => a,
(Option.None, Option.Some(a)) => {a},
}
};

@@ -23,17 +23,17 @@ Direction := enum(
// ~ERROR: patterns not covered
direction_to_int := (dir: Direction) -> i32 => {
return match dir {
Direction::North => 1,
Direction::West => 2,
Direction.North => 1,
Direction.West => 2,
}
}

// ~ERROR: patterns not covered
join_directions := (dir: Direction, other: Direction) -> Direction => {
match (dir, other) {
(Direction::North, Direction::South) => Direction::North,
(Direction::South, Direction::North) => Direction::North,
(Direction::West, Direction::East) => Direction::West,
(Direction::East, Direction::West) => Direction::West,
(Direction.North, Direction.South) => Direction.North,
(Direction.South, Direction.North) => Direction.North,
(Direction.West, Direction.East) => Direction.West,
(Direction.East, Direction.West) => Direction.West,
}
}
6 changes: 3 additions & 3 deletions tests/cases/exhaustiveness/missing_enums.stderr
Original file line number Diff line number Diff line change
@@ -3,18 +3,18 @@ error[0083]: non-exhaustive patterns: `(Option::None, Option::None)` not covered
5 | loop {
6 | return match (left, right) {
| ^^^^^^^^^^^^^ pattern `(Option::None, Option::None)` not covered
7 | (Option::Some(a), Option::Some(b)) => a + b,
7 | (Option.Some(a), Option.Some(b)) => a + b,

error[0083]: non-exhaustive patterns: `Direction::South`, and `Direction::East` not covered
--> $DIR/missing_enums.hash:25:16
24 | direction_to_int := (dir: Direction) -> i32 => {
25 | return match dir {
| ^^^ patterns `Direction::South`, and `Direction::East` not covered
26 | Direction::North => 1,
26 | Direction.North => 1,

error[0083]: non-exhaustive patterns: `(Direction::South, Direction::South)`, `(Direction::South, Direction::West)`, `(Direction::South, Direction::East)` and 9 more not covered
--> $DIR/missing_enums.hash:33:11
32 | join_directions := (dir: Direction, other: Direction) -> Direction => {
33 | match (dir, other) {
| ^^^^^^^^^^^^ patterns `(Direction::South, Direction::South)`, `(Direction::South, Direction::West)`, `(Direction::South, Direction::East)` and 9 more not covered
34 | (Direction::North, Direction::South) => Direction::North,
34 | (Direction.North, Direction.South) => Direction.North,
8 changes: 4 additions & 4 deletions tests/cases/lowering/constructor_matches.hash
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@

maybe_add := (left: Option<i32>, right: Option<i32>) -> i32 => {
match (left, right) {
(Option::Some(a), Option::Some(b)) => a + b,
(Option::Some(a), Option::None) => a,
(Option::None, Option::Some(a)) => a,
(Option::None, Option::None) => 0
(Option.Some(a), Option.Some(b)) => a + b,
(Option.Some(a), Option.None) => a,
(Option.None, Option.Some(a)) => a,
(Option.None, Option.None) => 0
}
}
2 changes: 1 addition & 1 deletion tests/cases/parser/constructors/struct_literal_init.hash
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
my_dog := MyDog (
name = "Viktor",
age = 17.2,
breed = DogBreed::Husky
breed = DogBreed.Husky
);

// Test auto-assigning the same name field as a variable in scope
24 changes: 24 additions & 0 deletions tests/cases/parser/declarations/constants.hash
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// stage=parse


m: i32 : 0
k :: 0

foo :: () => {
k + m
}

bar :: () => {
l :: foo()
t := 1 + l

l + t
}


fizz :: () => {
l: i32 : foo()
t: i32 = 1 + l

l + t
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
error: expected a name here
--> $DIR/double_colon_in_map_literal.hash:3:15
error: expected an expression, however received a `::`
--> $DIR/double_colon_in_map_literal.hash:3:13
2 |
3 | k := map!{ 1::2 };
| ^
= help: expected a `identifier`
| ^^
4 changes: 2 additions & 2 deletions tests/cases/parser/literals/enum_literals.hash
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// run=pass, stage=parse

k := MyEnum::Variant(1, 2, 3);
k := MyEnum::Variant(Some(MyEnum::Variant(None, 2, 1)), 2, 3);
k := MyEnum.Variant(1, 2, 3);
k := MyEnum.Variant(Some(MyEnum.Variant(None, 2, 1)), 2, 3);
2 changes: 1 addition & 1 deletion tests/cases/parser/misc/triple_colon_in_access_name.hash
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// run=fail, stage=parse

a::b:::c<int>();
a.b.:c<int>();
8 changes: 4 additions & 4 deletions tests/cases/parser/misc/triple_colon_in_access_name.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
error: expected a name here
--> $DIR/triple_colon_in_access_name.hash:3:7
error: expected field name access or a method call
--> $DIR/triple_colon_in_access_name.hash:3:5
2 |
3 | a::b:::c<int>();
| ^
3 | a.b.:c<int>();
| ^
= help: expected a `identifier`
8 changes: 4 additions & 4 deletions tests/cases/parser/patterns/decl_pats.hash
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// run=pass, stage=parse

// De-structure the `dog` into `age` and `name`
animals::Dog(name, age) := dog;
animals.Dog(name, age) := dog;

(a, b, c) := (2, 3, 4);

// This wouldn't be allowed but would parse
animals::ModuleVariable := 3;
animals::ModuleVariable: i32 = 3;
animals.ModuleVariable := 3;
animals.ModuleVariable: i32 = 3;


// Multiple constructors on the pattern
animals::Dog()() := dog;
animals.Dog()() := dog;
4 changes: 2 additions & 2 deletions tests/cases/parser/types/access_tys.hash
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// run=pass, stage=parse

t: animals::Dog = animals::Dog(name = "Viktor", age = 12);
t: animals::Dog<str> = animals::Dog(name = "Viktor", age = 12);
t: animals.Dog = animals.Dog(name = "Viktor", age = 12);
t: animals.Dog<str> = animals.Dog(name = "Viktor", age = 12);
2 changes: 1 addition & 1 deletion tests/cases/parser/types/issue_394.hash
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// stage=parse, warnings=ignore

main := () => {
((a.v)::c)<i32>;
((a.v).c)<i32>;
};
6 changes: 3 additions & 3 deletions tests/cases/parser/types/malformed_function_type.stderr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
error: unexpectedly encountered a `>`
--> $DIR/malformed_function_type.hash:3:20
--> $DIR/malformed_function_type.hash:3:21
2 |
3 | str_eq: (str, str) > str;
| ^
= help: expected a `=`
| ^
= help: expected either `:`, or `=`
2 changes: 1 addition & 1 deletion tests/cases/semantics/pats/intrinsics_in_pats.hash
Original file line number Diff line number Diff line change
@@ -2,6 +2,6 @@

main := () => {
match 1 {
Intrinsics::user_error => {}
Intrinsics.user_error => {}
}
}
4 changes: 2 additions & 2 deletions tests/cases/semantics/pats/intrinsics_in_pats.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error[0024]: cannot use an intrinsic in pattern position
--> $DIR/intrinsics_in_pats.hash:5:5
4 | match 1 {
5 | Intrinsics::user_error => {}
| ^^^^^^^^^^^^^^^^^^^^^^
5 | Intrinsics.user_error => {}
| ^^^^^^^^^^^^^^^^^^^^^
6 | }
= info: cannot use this in pattern position as it refers to a compiler intrinsic
6 changes: 3 additions & 3 deletions tests/cases/typecheck/enums/adt.hash
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ Vote := enum(Left(usize), Right(usize), Abstain);
ensure := <T> => (t: T) => {};

main := () => {
ensure<Vote>(Vote::Left(1usize));
ensure<Vote>(Vote::Abstain);
ensure<(usize) -> Vote>((x: usize) => Vote::Left(x));
ensure<Vote>(Vote.Left(1usize));
ensure<Vote>(Vote.Abstain);
ensure<(usize) -> Vote>((x: usize) => Vote.Left(x));
};
10 changes: 5 additions & 5 deletions tests/cases/typecheck/enums/basic.hash
Original file line number Diff line number Diff line change
@@ -5,14 +5,14 @@ PrimaryColour := enum(Red, Green, Blue);
ensure := <T> => (t: T) => {};

main := () => {
ensure<PrimaryColour>(PrimaryColour::Red);
ensure<PrimaryColour>(PrimaryColour::Green);
ensure<PrimaryColour>(PrimaryColour::Blue);
ensure<PrimaryColour>(PrimaryColour.Red);
ensure<PrimaryColour>(PrimaryColour.Green);
ensure<PrimaryColour>(PrimaryColour.Blue);

red := PrimaryColour::Red;
red := PrimaryColour.Red;
ensure<PrimaryColour>(red);

many := [PrimaryColour::Red as PrimaryColour, PrimaryColour::Blue as PrimaryColour, PrimaryColour::Green as PrimaryColour];
many := [PrimaryColour.Red as PrimaryColour, PrimaryColour.Blue as PrimaryColour, PrimaryColour.Green as PrimaryColour];
ensure<List<PrimaryColour>>(many);
};

12 changes: 6 additions & 6 deletions tests/cases/typecheck/enums/indexed.hash
Original file line number Diff line number Diff line change
@@ -7,13 +7,13 @@ Nat := enum(

Fin := enum <n: Nat> (
Zero(n: Nat): Fin<n>,
Succ(n: Nat, f: Fin<n>): Fin<{Nat::Succ(n)}>,
Succ(n: Nat, f: Fin<n>): Fin<{Nat.Succ(n)}>,
)

add := (a: Nat, b: Nat) -> Nat => {
match a {
Nat::Zero => b,
Nat::Succ(a) => Nat::Succ(add(a, b))
Nat.Zero => b,
Nat.Succ(a) => Nat.Succ(add(a, b))
}
}

@@ -23,11 +23,11 @@ Lt := enum <n: Nat, m: Nat> (

sub := (n: Nat, m: Nat, p: Lt<m, n>) -> Nat => {
match p {
Lt::Diff(_, d) => d
Lt.Diff(_, d) => d
}
}

Vec := enum <n: Nat, A: Type> (
Nil: Vec<Nat::Zero, A>,
Cons(n: Nat, x: A, xs: Vec<n, A>): Vec<{Nat::Succ(n)}, A>,
Nil: Vec<Nat.Zero, A>,
Cons(n: Nat, x: A, xs: Vec<n, A>): Vec<{Nat.Succ(n)}, A>,
)
18 changes: 9 additions & 9 deletions tests/cases/typecheck/misc/expr.hash
Original file line number Diff line number Diff line change
@@ -21,20 +21,20 @@ Expr := enum <T> (
/// Evaluate an expression of i32.
eval := (e: Expr<i32>) -> i32 => {
match e {
Expr::Const(t) => t,
Expr::BinOp(op, e1, e2) => match op {
BinOp::Add => eval(e1) + eval(e2),
BinOp::Mul => eval(e1) * eval(e2),
BinOp::Sub => eval(e1) - eval(e2),
BinOp::Div => eval(e1) / eval(e2),
Expr.Const(t) => t,
Expr.BinOp(op, e1, e2) => match op {
BinOp.Add => eval(e1) + eval(e2),
BinOp.Mul => eval(e1) * eval(e2),
BinOp.Sub => eval(e1) - eval(e2),
BinOp.Div => eval(e1) / eval(e2),
},
Expr::UnOp(op, e1) => match op {
UnOp::Neg => -eval(e1),
Expr.UnOp(op, e1) => match op {
UnOp.Neg => -eval(e1),
},
}
}

main := () => {
e := Expr::BinOp(BinOp::Add, Expr::Const(41), Expr::Const(1))
e := Expr.BinOp(BinOp.Add, Expr.Const(41), Expr.Const(1))
n := eval(e)
}
6 changes: 3 additions & 3 deletions tests/cases/typecheck/misc/monad.hash
Original file line number Diff line number Diff line change
@@ -38,10 +38,10 @@ option_monad := () => {
b: U -> Option<V>
) -> Option<V> => {
match a {
Option::Some(x) => b(x),
Option::None => Option::None
Option.Some(x) => b(x),
Option.None => Option.None
}
},
ret = <U> => (x: U) -> Option<U> => Option::Some(x)
ret = <U> => (x: U) -> Option<U> => Option.Some(x)
)
}
16 changes: 8 additions & 8 deletions tests/cases/typecheck/misc/nat.hash
Original file line number Diff line number Diff line change
@@ -9,30 +9,30 @@ Nat := enum(
/// Add two natural numbers.
add := (a: Nat, b: Nat) -> Nat => {
match a {
Nat::Zero => b,
Nat::Succ(a) => Nat::Succ(add(a, b))
Nat.Zero => b,
Nat.Succ(a) => Nat.Succ(add(a, b))
}
}

/// If a + 1 = b + 1, then a = b
succ_id := <
a: Nat,
b: Nat,
p: {Nat::Succ(a)} ~ {Nat::Succ(b)}
p: {Nat.Succ(a)} ~ {Nat.Succ(b)}
> -> a ~ b => {
match p {
Equal::Refl(_) => refl()
Equal.Refl(_) => refl()
}
}

/// Nat induction.
nat_induction := (
P: <n: Nat> -> Type,
pzero: P<Nat::Zero>,
psucc: (m: Nat, p: P<m>) -> P<{Nat::Succ(m)}>,
pzero: P<Nat.Zero>,
psucc: (m: Nat, p: P<m>) -> P<{Nat.Succ(m)}>,
) => (n: Nat) -> P<n> => {
match n {
Nat::Zero => pzero,
Nat::Succ(m) => psucc(m, nat_induction(P, pzero, psucc)(m))
Nat.Zero => pzero,
Nat.Succ(m) => psucc(m, nat_induction(P, pzero, psucc)(m))
}
}