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

Add named constructor parsing #164

Merged
merged 2 commits into from
Sep 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions examples/parsing/named_constructor.an
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Explicit names
T with b = "one", a = 1

T with
b = "one"
a = 1

// Implicit names
T with b, a

T with
foo
bar

// Path names
T.U.V with a, b

// Tuples and exotic indentation
T with a = (1, 2)

T with a = 1,
b = 3

T with a = (1, 2),
b = (3, 4), c = 5,
d = 6

T with foo,
bar,
baz

T with
foo = ( 1
, 2
, 3
)
bar = "bar"

// args: --parse
// expected stdout:
// (T with b = "one", a = 1);
// (T with b = "one", a = 1);
// (T with b = b, a = a);
// (T with foo = foo, bar = bar);
// (T.U.V with a = a, b = b);
// (T with a = (',' 1 2));
// (T with a = 1, b = 3);
// (T with a = (',' 1 2), b = (',' 3 4), c = 5, d = 6);
// (T with foo = foo, bar = bar, baz = baz);
// (T with foo = (',' 1 (',' 2 3)), bar = "bar")
10 changes: 4 additions & 6 deletions src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use crate::cache::unsafecache::UnsafeCache;
use crate::error::location::{Locatable, Location};
use crate::nameresolution::NameResolver;
use crate::parser::ast::{Ast, Definition, EffectDefinition, TraitDefinition, TraitImpl, Extern};
use crate::parser::ast::{Ast, Definition, EffectDefinition, Extern, TraitDefinition, TraitImpl};
use crate::types::traits::{ConstraintSignature, RequiredImpl, RequiredTrait, TraitConstraintId};
use crate::types::{GeneralizedType, Kind, LetBindingLevel, TypeBinding};
use crate::types::{Type, TypeInfo, TypeInfoBody, TypeInfoId, TypeVariableId};
Expand Down Expand Up @@ -605,11 +605,9 @@ impl<'a> ModuleCache<'a> {

pub fn follow_typebindings_shallow<'b>(&'b self, typ: &'b Type) -> &'b Type {
match typ {
Type::TypeVariable(id) => {
match &self.type_bindings[id.0] {
TypeBinding::Bound(typ) => self.follow_typebindings_shallow(typ),
TypeBinding::Unbound(_, _) => typ,
}
Type::TypeVariable(id) => match &self.type_bindings[id.0] {
TypeBinding::Bound(typ) => self.follow_typebindings_shallow(typ),
TypeBinding::Unbound(_, _) => typ,
},
other => other,
}
Expand Down
4 changes: 3 additions & 1 deletion src/cranelift_backend/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ fn eq_bool(param1: CraneliftValue, param2: CraneliftValue, builder: &mut Functio
b1_to_i8(builder.ins().icmp(IntCC::Equal, param1, param2), builder)
}

fn transmute<'a>(context: &mut Context<'a>, param: &'a Ast, typ: &crate::hir::Type, builder: &mut FunctionBuilder) -> Value {
fn transmute<'a>(
context: &mut Context<'a>, param: &'a Ast, typ: &crate::hir::Type, builder: &mut FunctionBuilder,
) -> Value {
let value = param.codegen(context, builder);
context.transmute(value, typ, builder)
}
Expand Down
3 changes: 1 addition & 2 deletions src/cranelift_backend/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ impl DynModule {
DynModule::Jit(module) => {
module.finalize_definitions();
},
DynModule::Static(_module) => {
},
DynModule::Static(_module) => {},
}
}

Expand Down
13 changes: 7 additions & 6 deletions src/hir/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool {
match (a, b) {
(Type::Primitive(primitive1), Type::Primitive(primitive2)) => primitive1 == primitive2,
(Type::UserDefined(id1), Type::UserDefined(id2)) => id1 == id2,
(Type::TypeVariable(_), Type::TypeVariable(_))
| (Type::Ref(_), Type::Ref(_)) => true, // Do nothing
(Type::TypeVariable(_), Type::TypeVariable(_)) | (Type::Ref(_), Type::Ref(_)) => true, // Do nothing
(Type::Function(f1), Type::Function(f2)) => {
if f1.parameters.len() != f2.parameters.len() {
return false;
Expand All @@ -113,7 +112,10 @@ fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool {
if field_names1.len() != field_names2.len() {
return false;
}
field_names1.iter().zip(field_names2).all(|((name1, t1), (name2, t2))| name1 == name2 && definition_type_eq(t1, t2))
field_names1
.iter()
.zip(field_names2)
.all(|((name1, t1), (name2, t2))| name1 == name2 && definition_type_eq(t1, t2))
},
(Type::Effects(set1), Type::Effects(set2)) => {
if set1.effects.len() != set2.effects.len() {
Expand All @@ -123,13 +125,12 @@ fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool {
if args1.len() != args2.len() {
return false;
}
id1 == id2 &&
args1.iter().zip(args2).all(|(t1, t2)| definition_type_eq(t1, t2))
id1 == id2 && args1.iter().zip(args2).all(|(t1, t2)| definition_type_eq(t1, t2))
})
},
(othera, otherb) => {
assert_ne!(std::mem::discriminant(othera), std::mem::discriminant(otherb), "ICE: Missing match case");
false
}
},
}
}
10 changes: 6 additions & 4 deletions src/hir/monomorphisation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ impl<'c> Context<'c> {
Assignment(assignment) => self.monomorphise_assignment(assignment),
EffectDefinition(_) => todo!(),
Handle(_) => todo!(),
NamedConstructor(_) => todo!(),
}
}

Expand Down Expand Up @@ -1452,10 +1453,11 @@ impl<'c> Context<'c> {
// This case should only happen when a bottom type is unified with an anonymous field
// type. Default to alphabetically ordered fields, but it should never actually be
// accessed anyway.
Struct(fields, _binding) => {
fields.keys().position(|name| name == field_name)
.expect(&format!("Expected type {} to have a field named '{}'", typ.display(&self.cache), field_name)) as u32
}
Struct(fields, _binding) => fields.keys().position(|name| name == field_name).expect(&format!(
"Expected type {} to have a field named '{}'",
typ.display(&self.cache),
field_name
)) as u32,
_ => unreachable!(
"get_field_index called with type {} that doesn't have a '{}' field",
typ.display(&self.cache),
Expand Down
3 changes: 1 addition & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ fn compile(args: Cli) {

// Phase 6: Codegen
let default_backend = if args.opt_level == '0' { Backend::Cranelift } else { Backend::Llvm };
let backend =
args.backend.unwrap_or(default_backend);
let backend = args.backend.unwrap_or(default_backend);

match backend {
Backend::Cranelift => cranelift_backend::run(filename, hir, &args),
Expand Down
39 changes: 29 additions & 10 deletions src/nameresolution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,9 @@ impl NameResolver {
id
}

fn push_new_type_variable<'c>(&mut self, key: &str, location: Location<'c>, cache: &mut ModuleCache<'c>) -> TypeVariableId {
fn push_new_type_variable<'c>(
&mut self, key: &str, location: Location<'c>, cache: &mut ModuleCache<'c>,
) -> TypeVariableId {
let id = cache.next_type_variable_id(self.let_binding_level);
self.push_existing_type_variable(key, id, location)
}
Expand Down Expand Up @@ -544,12 +546,22 @@ impl NameResolver {
None
}

fn validate_type_application<'c>(&self, constructor: &Type, args: &[Type], location: Location<'c>, cache: &mut ModuleCache<'c>) {
fn validate_type_application<'c>(
&self, constructor: &Type, args: &[Type], location: Location<'c>, cache: &mut ModuleCache<'c>,
) {
let expected = self.get_expected_type_argument_count(constructor, cache);
if args.len() != expected && !matches!(constructor, Type::TypeVariable(_)) {
let plural_s = if expected == 1 { "" } else { "s" };
let is_are = if args.len() == 1 { "is" } else { "are" };
error!(location, "Type {} expects {} argument{}, but {} {} given here", constructor.display(cache), expected, plural_s, args.len(), is_are);
error!(
location,
"Type {} expects {} argument{}, but {} {} given here",
constructor.display(cache),
expected,
plural_s,
args.len(),
is_are
);
}

// Check argument is an integer/float type (issue #146)
Expand All @@ -564,7 +576,7 @@ impl NameResolver {
if !matches!(first_arg, Type::Primitive(PrimitiveType::FloatTag(_)) | Type::TypeVariable(_)) {
error!(location, "Type {} is not a float type", first_arg.display(cache));
}
}
},
_ => (),
}
}
Expand All @@ -590,7 +602,9 @@ impl NameResolver {
/// Re-insert the given type variables into the current scope.
/// Currently used for remembering type variables from type and trait definitions that
/// were created in the declare pass and need to be used later in the define pass.
fn add_existing_type_variables_to_scope(&mut self, existing_typevars: &[String], ids: &[TypeVariableId], location: Location) {
fn add_existing_type_variables_to_scope(
&mut self, existing_typevars: &[String], ids: &[TypeVariableId], location: Location,
) {
// re-insert the typevars into scope.
// These names are guarenteed to not collide since we just pushed a new scope.
assert_eq!(existing_typevars.len(), ids.len());
Expand Down Expand Up @@ -692,11 +706,8 @@ impl<'c> NameResolver {
ast::Type::Function(args, ret, is_varargs, is_closure, _) => {
let parameters = fmap(args, |arg| self.convert_type(cache, arg));
let return_type = Box::new(self.convert_type(cache, ret));
let environment = Box::new(if *is_closure {
cache.next_type_variable(self.let_binding_level)
} else {
Type::UNIT
});
let environment =
Box::new(if *is_closure { cache.next_type_variable(self.let_binding_level) } else { Type::UNIT });
let effects = Box::new(cache.next_type_variable(self.let_binding_level));
let is_varargs = *is_varargs;
Type::Function(FunctionType { parameters, return_type, environment, is_varargs, effects })
Expand Down Expand Up @@ -1631,3 +1642,11 @@ impl<'c> Resolvable<'c> for ast::Handle<'c> {
}
}
}

impl<'c> Resolvable<'c> for ast::NamedConstructor<'c> {
fn declare(&mut self, _resolver: &mut NameResolver, _cache: &mut ModuleCache<'c>) {}

fn define(&mut self, _resolver: &mut NameResolver, _cache: &mut ModuleCache<'c>) {
todo!()
}
}
19 changes: 19 additions & 0 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,18 @@ pub struct Handle<'a> {
pub typ: Option<types::Type>,
}

/// MyStruct with
/// field1 = expr1
/// field2 = expr2
#[derive(Debug, Clone)]
pub struct NamedConstructor<'a> {
pub constructor: Box<Ast<'a>>,
pub args: Vec<(String, Ast<'a>)>,

pub location: Location<'a>,
pub typ: Option<types::Type>,
}

#[derive(Debug, Clone)]
pub enum Ast<'a> {
Literal(Literal<'a>),
Expand All @@ -414,6 +426,7 @@ pub enum Ast<'a> {
Assignment(Assignment<'a>),
EffectDefinition(EffectDefinition<'a>),
Handle(Handle<'a>),
NamedConstructor(NamedConstructor<'a>),
}

unsafe impl<'c> Send for Ast<'c> {}
Expand Down Expand Up @@ -684,6 +697,10 @@ impl<'a> Ast<'a> {
Ast::Handle(Handle { expression: Box::new(expression), branches, location, resumes: vec![], typ: None })
}

pub fn named_constructor(constructor: Ast<'a>, args: Vec<(String, Ast<'a>)>, location: Location<'a>) -> Ast<'a> {
Ast::NamedConstructor(NamedConstructor { constructor: Box::new(constructor), args, location, typ: None })
}

/// This is a bit of a hack.
/// Create a new 'scope' by wrapping body in `match () | () -> body`
pub fn new_scope(body: Ast<'a>, location: Location<'a>) -> Ast<'a> {
Expand Down Expand Up @@ -716,6 +733,7 @@ macro_rules! dispatch_on_expr {
$crate::parser::ast::Ast::Assignment(inner) => $function(inner $(, $($args),* )? ),
$crate::parser::ast::Ast::EffectDefinition(inner) => $function(inner $(, $($args),* )? ),
$crate::parser::ast::Ast::Handle(inner) => $function(inner $(, $($args),* )? ),
$crate::parser::ast::Ast::NamedConstructor(inner) => $function(inner $(, $($args),* )? ),
}
});
}
Expand Down Expand Up @@ -755,6 +773,7 @@ impl_locatable_for!(MemberAccess);
impl_locatable_for!(Assignment);
impl_locatable_for!(EffectDefinition);
impl_locatable_for!(Handle);
impl_locatable_for!(NamedConstructor);

impl<'a> Locatable<'a> for Type<'a> {
fn locate(&self) -> Location<'a> {
Expand Down
8 changes: 1 addition & 7 deletions src/parser/desugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,5 @@ pub fn desugar_if_with_no_else<'a>(condition: Ast<'a>, then: Ast<'a>, location:
let then = Box::new(Ast::sequence(vec![then, Ast::unit_literal(location)], location));
let otherwise = Box::new(Ast::unit_literal(location));

Ast::If(ast::If {
condition: Box::new(condition),
then,
otherwise,
location,
typ: None,
})
Ast::If(ast::If { condition: Box::new(condition), then, otherwise, location, typ: None })
}
37 changes: 36 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ fn term<'a, 'b>(input: Input<'a, 'b>) -> AstResult<'a, 'b> {
Token::Loop => loop_expr(input),
Token::Match => match_expr(input),
Token::Handle => handle_expr(input),
_ => or(&[type_annotation, function_call, function_argument], "term")(input),
_ => or(&[type_annotation, named_constructor_expr, function_call, function_argument], "term")(input),
}
}

Expand All @@ -458,6 +458,41 @@ parser!(function_call loc =
desugar::desugar_explicit_currying(function, args, Ast::function_call, loc)
);

parser!(named_constructor_expr loc =
constructor <- variant;
_ <- expect(Token::With);
args !<- named_constructor_args;
Ast::named_constructor(constructor, args, loc)
);

fn named_constructor_args<'a, 'b>(input: Input<'a, 'b>) -> ParseResult<'a, 'b, Vec<(String, Ast<'b>)>> {
if let Token::Indent = input[0].0 {
named_constructor_block_args(input)
} else {
named_constructor_inline_args(input)
}
}

parser!(named_constructor_block_args loc -> 'b Vec<(String, Ast<'b>)> =
_ <- expect(Token::Indent);
args <- delimited_trailing(named_constructor_arg, expect(Token::Newline), false);
_ !<- expect(Token::Unindent);
args
);

parser!(named_constructor_inline_args loc -> 'b Vec<(String, Ast<'b>)> =
args <- delimited(named_constructor_arg, expect(Token::Comma));
args
);

fn named_constructor_arg<'a, 'b>(input: Input<'a, 'b>) -> ParseResult<'a, 'b, (String, Ast<'b>)> {
let (input, ident, start) = identifier(input)?;
let (input, maybe_expr, end) = maybe(pair(expect(Token::Equal), function_argument))(input)?;
// Desugar bar, baz into bar = bar, baz = baz
let expr = maybe_expr.map_or_else(|| Ast::variable(vec![], ident.clone(), start), |(_, expr)| expr);
Ok((input, (ident, expr), start.union(end)))
}

parser!(pattern_function_call loc =
function <- pattern_function_argument;
args <- many1(pattern_function_argument);
Expand Down
7 changes: 7 additions & 0 deletions src/parser/pretty_printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,10 @@ impl<'a> Display for ast::Handle<'a> {
write!(f, ")")
}
}

impl<'a> Display for ast::NamedConstructor<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let args = fmap(self.args.iter(), |(name, expr)| format!("{name} = {expr}"));
write!(f, "({} with {})", self.constructor, args.join(", "))
}
}
8 changes: 6 additions & 2 deletions src/types/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,9 +922,13 @@ impl Case {
constructor_type.instantiate(vec![], cache).0
},
Some(Literal(LiteralKind::Integer(_, Some(kind)))) => Type::int(*kind),
Some(Literal(LiteralKind::Integer(_, None))) => Type::polymorphic_int(typechecker::next_type_variable_id(cache)),
Some(Literal(LiteralKind::Integer(_, None))) => {
Type::polymorphic_int(typechecker::next_type_variable_id(cache))
},
Some(Literal(LiteralKind::Float(_, Some(kind)))) => Type::float(*kind),
Some(Literal(LiteralKind::Float(_, None))) => Type::polymorphic_float(typechecker::next_type_variable_id(cache)),
Some(Literal(LiteralKind::Float(_, None))) => {
Type::polymorphic_float(typechecker::next_type_variable_id(cache))
},
Some(Literal(LiteralKind::String(_))) => Type::UserDefined(STRING_TYPE),
Some(Literal(LiteralKind::Char(_))) => Type::Primitive(PrimitiveType::CharType),
Some(Literal(LiteralKind::Bool(_))) => unreachable!(),
Expand Down
Loading