diff --git a/base/src/ast.rs b/base/src/ast.rs index 50885a3aff..19a19d0fbc 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -6,6 +6,7 @@ use std::ops::{Deref, DerefMut}; use pos::{self, BytePos, HasSpan, Span, Spanned}; use symbol::Symbol; use types::{self, Alias, AliasData, ArcType, ArgType, Generic, Type, TypeEnv}; +use metadata::{Comment, Metadata}; use ordered_float::NotNaN; pub trait DisplayEnv { @@ -100,6 +101,10 @@ impl HasSpan for AstType { } } +pub trait Commented { + fn comment(&self) -> Option<&Comment>; +} + impl Commented for AstType { fn comment(&self) -> Option<&Comment> { self._typ.0.as_ref() @@ -147,22 +152,6 @@ impl AstType { } } -pub trait Commented { - fn comment(&self) -> Option<&Comment>; -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum CommentType { - Block, - Line, -} - -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct Comment { - pub typ: CommentType, - pub content: String, -} - #[derive(Clone, Eq, PartialEq, Debug)] pub struct TypedIdent { pub typ: ArcType, @@ -260,7 +249,7 @@ pub type SpannedAstType = Spanned>, BytePos>; #[derive(Clone, PartialEq, Debug)] pub struct ExprField { - pub comment: Option, + pub metadata: Metadata, pub name: Spanned, pub value: Option, } @@ -339,7 +328,7 @@ pub enum Expr { #[derive(Clone, PartialEq, Debug)] pub struct TypeBinding { - pub comment: Option, + pub metadata: Metadata, pub name: Spanned, pub alias: SpannedAlias, pub finalized_alias: Option>>, @@ -375,7 +364,7 @@ impl Argument { #[derive(Clone, PartialEq, Debug)] pub struct ValueBinding { - pub comment: Option, + pub metadata: Metadata, pub name: SpannedPattern, pub typ: Option>, pub resolved_type: ArcType, @@ -639,11 +628,11 @@ pub fn walk_mut_ast_type<'a, V: ?Sized + MutVisitor<'a>>( } v.visit_ast_type(&mut rest._typ.1); } - Type::Ident(_) => (), - Type::Variable(_) => (), - Type::Generic(_) => (), - Type::Alias(_) => (), - Type::Skolem(_) => (), + Type::Ident(_) + | Type::Variable(_) + | Type::Generic(_) + | Type::Alias(_) + | Type::Skolem(_) => (), } } @@ -654,8 +643,8 @@ pub trait Visitor<'a> { walk_expr(self, e); } - fn visit_pattern(&mut self, e: &'a SpannedPattern) { - walk_pattern(self, &e.value); + fn visit_pattern(&mut self, p: &'a SpannedPattern) { + walk_pattern(self, &p.value); } fn visit_typ(&mut self, _: &'a ArcType) {} diff --git a/base/src/metadata.rs b/base/src/metadata.rs index 576ead9bd7..e5bf1bd0a2 100644 --- a/base/src/metadata.rs +++ b/base/src/metadata.rs @@ -18,61 +18,62 @@ impl MetadataEnv for () { } } +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] +pub enum CommentType { + Block, + Line, +} + +#[derive(Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] +pub struct Comment { + pub typ: CommentType, + pub content: String, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] +pub struct Attribute { + pub name: String, + pub arguments: Option, +} + #[derive(Clone, Debug, Default, Eq, PartialEq)] #[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] pub struct Metadata { - pub comment: Option, + pub comment: Option, + pub attributes: Vec, pub module: BTreeMap, } impl Metadata { pub fn has_data(&self) -> bool { - self.comment.is_some() || !self.module.is_empty() + self.comment.is_some() || !self.module.is_empty() || !self.attributes.is_empty() } pub fn merge(mut self, other: Metadata) -> Metadata { + self.merge_with(other); + self + } + + pub fn merge_with(&mut self, other: Metadata) { if self.comment.is_none() { self.comment = other.comment; } if self.module.is_empty() { self.module = other.module; } - self + self.attributes.extend(other.attributes); } - pub fn get_attribute(&self, attribute: &str) -> Option<&str> { + pub fn get_attribute(&self, name: &str) -> Option<&str> { self.attributes() - .find(|&(name, _)| name == attribute) - .map(|t| t.1.unwrap_or("")) - } - - pub fn attributes(&self) -> Attributes { - attributes(self.comment.as_ref().map_or("", |s| s)) - } -} - -pub struct Attributes<'a> { - comment: ::std::str::Lines<'a>, -} - -impl<'a> Iterator for Attributes<'a> { - type Item = (&'a str, Option<&'a str>); - - fn next(&mut self) -> Option { - while let Some(line) = self.comment.next() { - if line.starts_with('@') { - let mut split = line[1..].splitn(2, ' '); - if let Some(x) = split.next().map(|key| (key, split.next())) { - return Some(x); - } - } - } - None + .find(|attribute| attribute.name == name) + .map(|t| t.arguments.as_ref().map_or("", |s| s)) } -} -pub fn attributes(comment: &str) -> Attributes { - Attributes { - comment: comment.lines(), + pub fn attributes(&self) -> impl Iterator { + self.attributes.iter() } } diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 7a84c5ccff..722b38e7be 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -9,10 +9,11 @@ use pretty::{Arena, DocAllocator, DocBuilder}; use smallvec::SmallVec; -use ast::{Comment, Commented, EmptyEnv, IdentEnv}; +use ast::{Commented, EmptyEnv, IdentEnv}; use fnv::FnvMap; use kind::{ArcKind, Kind, KindEnv}; use merge::merge; +use metadata::Comment; use pos::{BytePos, HasSpan, Span}; use source::Source; use symbol::{Symbol, SymbolRef}; diff --git a/base/src/types/pretty_print.rs b/base/src/types/pretty_print.rs index f5ed90beaf..06892f8c74 100644 --- a/base/src/types/pretty_print.rs +++ b/base/src/types/pretty_print.rs @@ -5,7 +5,8 @@ use std::ops::Deref; use pretty::{Arena, DocAllocator, DocBuilder}; -use ast::{is_operator_char, Comment, CommentType, Commented}; +use ast::{is_operator_char, Commented}; +use metadata::{Comment, CommentType}; use pos::{BytePos, HasSpan, Span}; use source::Source; diff --git a/book/src/metadata.md b/book/src/metadata.md index 323b3116fc..e6bb508b9e 100644 --- a/book/src/metadata.md +++ b/book/src/metadata.md @@ -14,7 +14,7 @@ let add2 x = x + 2 add1 // Looking up the metadata of this variable yields the documentation of `add1` // It can't be statically determined which branch the `if` takes (since constant folding do not -// take place). Thus `addN` do not get any metadta from either `add1` or `add2` +// take place). Thus `addN` do not get any metadata from either `add1` or `add2` let addN = if True then add1 else add2 addN ``` @@ -24,40 +24,44 @@ addN In addtion to documentation comments gluon also has a special notion of attributes that get propagated in the same manner. These are specified using the following syntax. -```f# -/// @ +``` +Attribute : #[ AttributeContents ] + +AttributeContents : + #[ IDENTIFIER ] + | #[ IDENTIFIER ( TOKENS* ) ] ``` -### @infix +### #[infix(..)] ```f# -/// @infix (left|right) +#[infix(, )] ``` -The `@infix` attribute is used to specified the fixity and precedence of infix operators. This lets us specify that multiplication binds tighter that addition. +The `#[infix]` attribute is used to specified the fixity and precedence of infix operators. This lets us specify that multiplication binds tighter that addition. ```f# -/// @infix left 6 -let (+) ?num : [num a] -> a -> a -> a = num.(+) -/// @infix left 7 -let (*) ?num : [num a] -> a -> a -> a = num.(*) +#[infix(left, 6)] +let (+) ?num : [Num a] -> a -> a -> a = num.(+) +#[infix(left, 7)] +let (*) ?num : [Num a] -> a -> a -> a = num.(*) ``` -### @implicit +### #[implicit] ```f# -/// @implicit +#[implicit] ``` -The `@implicit` attribute is used to mark value bindings or type bindings as usable for implicit resolution. If specified on a value binding then only that specific binding can be used on implicit resolution. If specified on a type binding then all bindings that has that type can be used in implicit resolution. +The `#[implicit]` attribute is used to mark value bindings or type bindings as usable for implicit resolution. If specified on a value binding then only that specific binding can be used on implicit resolution. If specified on a type binding then all bindings that has that type can be used in implicit resolution. ``` // Can be used as an implicit argument -/// @implicit +#[implicit] let binding : MyType = .. -/// @implicit +#[implicit] type Eq a = { (==) : a -> a -> Bool } // Can be used as an implicit argument diff --git a/book/src/syntax-and-semantics.md b/book/src/syntax-and-semantics.md index 8d2112c354..1d30445ceb 100644 --- a/book/src/syntax-and-semantics.md +++ b/book/src/syntax-and-semantics.md @@ -461,14 +461,14 @@ Sometimes, there is a need to overload a name with multiple differing implementa This different looking argument is an implicit argument which means that you do not need to pass a value for this argument, instead, the compiler will try to find a value with a type that matches the type signature. So if you were to call `1 == 2` the compiler will see that the type variable `a` has been unified to `Int`. Then when the implicit argument is resolved it will look for a value with the type `Eq Int`. -Since searching all possible bindings currently in scope would introduce to many ambiguity errors the compiler does not search all bindings when trying to determine an implicit argument. Instead, whether a binding is considered for implicit resolution is controlled by the `@implicit` attribute. When marking a `let` binding as `@implicit` and this binding is in scope it will be considered as a candidate for all implicit arguments. The `@implicit` attribute can also be set on a `type` binding in which case it applied to all `let` bindings which has the type declared by the `type` binding. +Since searching all possible bindings currently in scope would introduce to many ambiguity errors the compiler does not search all bindings when trying to determine an implicit argument. Instead, whether a binding is considered for implicit resolution is controlled by the `#[implicit]` attribute. When marking a `let` binding as `#[implicit]` and this binding is in scope it will be considered as a candidate for all implicit arguments. The `#[implicit]` attribute can also be set on a `type` binding in which case it applied to all `let` bindings which has the type declared by the `type` binding. ```f#,rust -/// @implicit +#[implicit] type Test = | Test () let f y: [a] -> a -> a = y let i = Test () -// `i` gets selected as the implicit argument since `@implicit` is marked on the type and `i : Test` +// `i` gets selected as the implicit argument since `#[implicit]` is marked on the type and `i : Test` () ``` @@ -494,7 +494,8 @@ If you only use implicit functions as explained above then it might just seem li ```f#,rust let list @ { List } = import! std.list // Make a custom equality function which returns true regardless of the elements of the list -let { (==) = (===) } = list.eq ?{ (==) = \x y -> True } +#[infix(left, 4)] +let (===) = (list.eq ?{ (==) = \x y -> True }).(==) Cons 1 (Cons 2 Nil) === Cons 3 (Cons 4 Nil) ``` diff --git a/check/src/metadata.rs b/check/src/metadata.rs index cb1164ffab..f32dd73c32 100644 --- a/check/src/metadata.rs +++ b/check/src/metadata.rs @@ -25,18 +25,12 @@ pub fn metadata( fn new_binding(&mut self, metadata: Metadata, bind: &ValueBinding) { match bind.name.value { Pattern::As(ref id, _) => { - let metadata = bind.comment.as_ref().map_or(metadata, |comment| Metadata { - comment: Some(comment.content.clone()), - module: BTreeMap::new(), - }); + let metadata = bind.metadata.clone().merge(metadata); self.stack_var(id.clone(), metadata.clone()); self.new_pattern(metadata, &bind.name); } Pattern::Ident(ref id) => { - let metadata = bind.comment.as_ref().map_or(metadata, |comment| Metadata { - comment: Some(comment.content.clone()), - module: BTreeMap::new(), - }); + let metadata = bind.metadata.clone().merge(metadata); self.stack_var(id.name.clone(), metadata); } Pattern::Constructor(..) @@ -139,17 +133,13 @@ pub fn metadata( } None => self.metadata(&field.name.value).cloned(), }; - let field_metadata = field.comment.clone().map(|comment| Metadata { - comment: Some(comment.content), - module: BTreeMap::new(), - }); - let maybe_metadata = match (field_metadata, maybe_metadata) { - (Some(l), Some(r)) => Some(l.merge(r)), - (None, Some(x)) | (Some(x), None) => Some(x), - (None, None) => None, + let field_metadata = field.metadata.clone(); + let maybe_metadata = match maybe_metadata { + Some(r) => field_metadata.merge(r), + None => field_metadata, }; - if let Some(metadata) = maybe_metadata { - module.insert(String::from(field.name.value.as_ref()), metadata); + if maybe_metadata.has_data() { + module.insert(String::from(field.name.value.as_ref()), maybe_metadata); } } for field in types { @@ -160,8 +150,8 @@ pub fn metadata( } } Metadata { - comment: None, - module: module, + module, + ..Metadata::default() } } Expr::LetBindings(ref bindings, ref expr) => { @@ -186,12 +176,12 @@ pub fn metadata( for bind in bindings { let mut type_metadata = Self::metadata_of_type(bind.alias.value.unresolved_type()); - if let Some(ref comment) = bind.comment { - let mut metadata = type_metadata.unwrap_or_default(); - metadata.comment = Some(comment.content.clone()); - type_metadata = Some(metadata); - } - if let Some(metadata) = type_metadata { + let metadata = type_metadata.map_or_else( + || bind.metadata.clone(), + |m| m.merge(bind.metadata.clone()), + ); + + if metadata.has_data() { // FIXME Shouldn't need to insert this metadata twice self.stack_var(bind.alias.value.name.clone(), metadata.clone()); self.stack_var(bind.name.value.clone(), metadata); @@ -225,7 +215,7 @@ pub fn metadata( let field_metadata = match field.typ.comment() { Some(comment) => { let mut metadata = field_metadata.unwrap_or_default(); - metadata.comment = Some(comment.content.clone()); + metadata.comment = Some(comment.clone()); Some(metadata) } None => field_metadata, @@ -238,8 +228,8 @@ pub fn metadata( None } else { Some(Metadata { - comment: None, module, + ..Metadata::default() }) } } diff --git a/check/tests/fail.rs b/check/tests/fail.rs index 9e6c6351f9..9246ca1809 100644 --- a/check/tests/fail.rs +++ b/check/tests/fail.rs @@ -433,7 +433,7 @@ The type `()` lacks the following fields: x fn unable_to_resolve_implicit_error_message() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Eq a = { } type Test a = | Test a diff --git a/check/tests/implicits.rs b/check/tests/implicits.rs index 0c70d16daf..dbefede7ca 100644 --- a/check/tests/implicits.rs +++ b/check/tests/implicits.rs @@ -27,7 +27,7 @@ fn single_implicit_arg() { let text = r#" let f ?x y: [Int] -> Int -> Int = x -/// @implicit +#[implicit] let i = 123 f 42 "#; @@ -60,7 +60,7 @@ fn single_implicit_implicit_arg() { let _ = ::env_logger::try_init(); let text = r#" let f y : [Int] -> Int -> Int = y -/// @implicit +#[implicit] let i = 123 f 42 "#; @@ -69,7 +69,7 @@ f 42 assert_eq!(result, Ok(Type::int())); assert_eq!( r#"let f ?implicit_arg y : [Int] -> Int -> Int = y -/// @implicit +#[implicit] let i = 123 f ?i 42"#, format::pretty_expr(text, &expr).trim() @@ -95,9 +95,9 @@ fn multiple_implicit_args() { let text = r#" let f ?x ?y z w: [Int] -> [String] -> String -> Int -> Int = x -/// @implicit +#[implicit] let i = 123 -/// @implicit +#[implicit] let x = "abc" f x 42 "#; @@ -112,7 +112,7 @@ fn just_a_implicit_arg() { let text = r#" let f ?x: [Int] -> Int = x -/// @implicit +#[implicit] let i = 123 f "#; @@ -127,9 +127,9 @@ fn function_implicit_arg() { let text = r#" let f ?eq l r: [a -> a -> Bool] -> a -> a -> Bool = eq l r -/// @implicit +#[implicit] let eq_int l r : Int -> Int -> Bool = True -/// @implicit +#[implicit] let eq_string l r : String -> String -> Bool = True f 1 2 f "" "" @@ -146,9 +146,9 @@ fn infix_implicit_arg() { let text = r#" let (==) ?eq l r: [a -> a -> Bool] -> a -> a -> Bool = eq l r -/// @implicit +#[implicit] let eq_int l r : Int -> Int -> Bool = True -/// @implicit +#[implicit] let eq_string l r : String -> String -> Bool = True "" == "" "#; @@ -183,10 +183,10 @@ fn implicit_from_record_field() { let text = r#" let f ?eq l r: [a -> a -> Bool] -> a -> a -> Bool = eq l r -/// @implicit +#[implicit] let eq_int l r : Int -> Int -> Bool = True let eq_string @ { ? } = - /// @implicit + #[implicit] let eq l r : String -> String -> Bool = True { eq } f 1 2 @@ -202,7 +202,7 @@ f "" "" fn implicit_on_type() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Test = | Test () let f ?x y: [a] -> a -> a = x let i = Test () @@ -227,7 +227,7 @@ f (Test ()) fn implicit_with_implicit_arguments() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Test a = | Test a type List a = | Nil | Cons a (List a) @@ -278,7 +278,7 @@ f (Cons 1 Nil) fn catch_infinite_loop() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Test a = | Test a type List a = | Nil | Cons a (List a) @@ -303,10 +303,10 @@ g 1 fn implicit_ord() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Eq a = { (==) : a -> a -> Bool } -/// @implicit +#[implicit] type Ord a = { eq : Eq a, compare : a -> a -> () } type Option a = | None | Some a @@ -332,7 +332,7 @@ let ord : [Ord a] -> Ord (Option a) = fn forward_implicit_parameter() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Test a = | Test a let f ?x y : [Test a] -> () -> Test a = x let g ?x y : [Test a] -> a -> Test a = f () @@ -379,9 +379,9 @@ fn implicit_as_function_argument() { let _ = ::env_logger::try_init(); let text = r#" let (==) ?eq l r: [a -> a -> Bool] -> a -> a -> Bool = eq l r -/// @implicit +#[implicit] let eq_int l r : Int -> Int -> Bool = True -/// @implicit +#[implicit] let eq_string l r : String -> String -> Bool = True let f eq l r : (a -> a -> Bool) -> a -> a -> Bool = eq l r @@ -419,12 +419,12 @@ f (==) 1 2 fn applicative_resolve_implicit() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Functor f = { map : forall a b . (a -> b) -> f a -> f b } -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { functor : Functor f, apply : forall a b . f (a -> b) -> f a -> f b, @@ -442,12 +442,12 @@ let (<*) ?app l r : [Applicative f] -> f a -> f b -> f a = app.functor.map (\x _ fn select_functor_from_applicative() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Functor f = { map : forall a b . (a -> b) -> f a -> f b } -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { functor : Functor f, apply : forall a b . f (a -> b) -> f a -> f b, @@ -468,7 +468,7 @@ fn wrap_call_selection() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { wrap : forall a . a -> f a, } @@ -493,7 +493,7 @@ fn unknown_implicit_arg_type() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { wrap : forall a . a -> f a, } @@ -518,7 +518,7 @@ fn dont_insert_extra_implicit_arg_type() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { wrap : forall a . a -> f a, } @@ -545,7 +545,7 @@ fn dont_insert_implicit_with_unresolved_arguments() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Alternative f = { empty : forall a . f a } @@ -567,7 +567,7 @@ fn resolve_implicit_for_fold_m() { let text = r#" type List a = | Nil | Cons a (List a) -/// @implicit +#[implicit] type Foldable (f : Type -> Type) = { } @@ -595,7 +595,7 @@ f fn resolve_implicit_which_is_generic() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Semigroup a = { append : a -> a -> a } @@ -622,17 +622,17 @@ Nil <> Nil fn resolve_implicit_semigroup() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Semigroup a = { append : a -> a -> a } -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { apply : forall a b . f (a -> b) -> f a -> f b, } -/// @implicit +#[implicit] type Eq a = { (==) : a -> a -> Bool } type List a = | Nil | Cons a (List a) @@ -672,7 +672,7 @@ let applicative : Applicative List = fn resolve_generic_type_multiple_times() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { } @@ -704,7 +704,7 @@ let get : State s s = any () fn implicit_list_without_inner_type_determined() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] type Test a = | Test a type List a = | Nil | Cons a (List a) diff --git a/check/tests/metadata.rs b/check/tests/metadata.rs index b1f538e579..cd580a8896 100644 --- a/check/tests/metadata.rs +++ b/check/tests/metadata.rs @@ -9,6 +9,7 @@ extern crate gluon_parser as parser; use base::ast::SpannedExpr; use base::metadata::{Metadata, MetadataEnv}; use base::symbol::{Symbol, SymbolRef}; +use base::metadata::{Comment, CommentType}; fn metadata(env: &MetadataEnv, expr: &mut SpannedExpr) -> Metadata { check::metadata::metadata(env, expr).0 @@ -24,6 +25,13 @@ impl MetadataEnv for MockEnv { } } +fn line_comment(s: &str) -> Comment { + Comment { + typ: CommentType::Line, + content: s.into(), + } +} + #[test] fn propagate_metadata_let_in() { let _ = env_logger::try_init(); @@ -41,8 +49,8 @@ id assert_eq!( metadata, Metadata { - comment: Some("The identity function".into()), - module: Default::default(), + comment: Some(line_comment("The identity function")), + ..Metadata::default() } ); } @@ -64,8 +72,8 @@ let id x = x assert_eq!( metadata.module.get("id"), Some(&Metadata { - comment: Some("The identity function".into()), - module: Default::default(), + comment: Some(line_comment("The identity function")), + ..Metadata::default() }) ); } @@ -87,8 +95,8 @@ type Test = Int assert_eq!( metadata.module.get("Test"), Some(&Metadata { - comment: Some("A test type".into()), - module: Default::default(), + comment: Some(line_comment("A test type")), + ..Metadata::default() }) ); } @@ -111,8 +119,8 @@ fn propagate_metadata_record_field_comment() { assert_eq!( metadata.module.get("id"), Some(&Metadata { - comment: Some("The identity function".into()), - module: Default::default(), + comment: Some(line_comment("The identity function")), + ..Metadata::default() }) ); } @@ -136,8 +144,8 @@ x.id assert_eq!( metadata, Metadata { - comment: Some("The identity function".into()), - module: Default::default(), + comment: Some(line_comment("The identity function")), + ..Metadata::default() } ); } @@ -164,8 +172,8 @@ type Test = { .get("Test") .and_then(|metadata| metadata.module.get("x")), Some(&Metadata { - comment: Some("A field".into()), - module: Default::default(), + comment: Some(line_comment("A field")), + ..Metadata::default() }) ); } diff --git a/completion/tests/completion.rs b/completion/tests/completion.rs index c030d15655..0e9fc664b0 100644 --- a/completion/tests/completion.rs +++ b/completion/tests/completion.rs @@ -11,11 +11,22 @@ extern crate gluon_parser as parser; use base::metadata::Metadata; use base::pos::{BytePos, Span}; use base::types::{ArcType, Field, Type}; +use base::metadata::{Comment, CommentType}; #[allow(unused)] mod support; use support::{intern, loc, typ, MockEnv}; +fn line_comment(s: S) -> Comment +where + S: Into, +{ + Comment { + typ: CommentType::Line, + content: s.into(), + } +} + fn find_span_type(s: &str, pos: BytePos) -> Result<(Span, ArcType), ()> { let env = MockEnv::new(); @@ -257,10 +268,12 @@ x Span::new(loc(text, 1, 4), loc(text, 1, 9)), Type::record( vec![], - vec![Field { - name: intern("x"), - typ: Type::int(), - }], + vec![ + Field { + name: intern("x"), + typ: Type::int(), + }, + ], ), )); assert_eq!(result, expected); @@ -288,16 +301,12 @@ fn in_record() { fn record_constructor_field() { let _ = env_logger::try_init(); - let result = find_type( - r#"{ test = 123 }"#, - BytePos::from(4), - ); + let result = find_type(r#"{ test = 123 }"#, BytePos::from(4)); let expected = Ok(typ("Int")); assert_eq!(result, expected); } - #[test] fn function_arg() { let _ = env_logger::try_init(); @@ -357,7 +366,7 @@ abc let result = get_metadata(text, BytePos::from(41)); let expected = Some(Metadata { - comment: Some("test".to_string()), + comment: Some(line_comment("test".to_string())), ..Metadata::default() }); assert_eq!(result, expected); @@ -375,7 +384,7 @@ let (+++) x y = 1 let result = get_metadata(text, BytePos::from(32)); let expected = Some(Metadata { - comment: Some("test".to_string()), + comment: Some(line_comment("test".to_string())), ..Metadata::default() }); assert_eq!(result, expected); @@ -396,7 +405,7 @@ module.abc let result = get_metadata(text, BytePos::from(81)); let expected = Some(Metadata { - comment: Some("test".to_string()), + comment: Some(line_comment("test".to_string())), ..Metadata::default() }); assert_eq!(result, expected); @@ -415,7 +424,7 @@ ab let result = suggest_metadata(text, BytePos::from(36), "abc"); let expected = Some(Metadata { - comment: Some("test".to_string()), + comment: Some(line_comment("test".to_string())), ..Metadata::default() }); assert_eq!(result, expected); @@ -436,7 +445,7 @@ module.ab let result = suggest_metadata(text, BytePos::from(81), "abc"); let expected = Some(Metadata { - comment: Some("test".to_string()), + comment: Some(line_comment("test".to_string())), ..Metadata::default() }); assert_eq!(result, expected); diff --git a/doc/src/lib.rs b/doc/src/lib.rs index e3807dfa6e..1309ef968b 100644 --- a/doc/src/lib.rs +++ b/doc/src/lib.rs @@ -65,7 +65,7 @@ pub fn record(typ: &ArcType, meta: &Metadata) -> Record { typ: field.typ.unresolved_type().to_string(), comment: meta.module .get(AsRef::::as_ref(&field.name)) - .and_then(|meta| meta.comment.as_ref().map(|s| &s[..])) + .and_then(|meta| meta.comment.as_ref().map(|s| &s.content[..])) .unwrap_or("") .to_string(), }) @@ -78,7 +78,7 @@ pub fn record(typ: &ArcType, meta: &Metadata) -> Record { comment: meta.module .get(AsRef::::as_ref(&field.name)) - .and_then(|meta| meta.comment.as_ref().map(|s| &s[..])) + .and_then(|meta| meta.comment.as_ref().map(|s| &s.content[..])) .unwrap_or("") .to_string(), }) diff --git a/format/src/pretty_print.rs b/format/src/pretty_print.rs index c32bb43633..77bc14f517 100644 --- a/format/src/pretty_print.rs +++ b/format/src/pretty_print.rs @@ -6,6 +6,7 @@ use pretty::{Arena, DocAllocator, DocBuilder}; use self::types::pretty_print as pretty_types; use base::ast::{Do, Expr, Pattern, SpannedExpr, SpannedPattern, ValueBinding}; +use base::metadata::Attribute; use base::kind::Kind; use base::pos::{self, BytePos, HasSpan, Span, Spanned}; use base::source; @@ -211,7 +212,8 @@ where "=" ]; chain![arena; - pretty_types::doc_comment(arena, bind.comment.as_ref()), + pretty_types::doc_comment(arena, bind.metadata.comment.as_ref()), + self.pretty_attributes(&bind.metadata.attributes), self.hang(decl, &bind.expr).group() ] }; @@ -264,7 +266,8 @@ where Expr::TypeBindings(ref binds, ref body) => { let prefixes = iter::once("type").chain(iter::repeat("and")); chain![arena; - pretty_types::doc_comment(arena, binds.first().unwrap().comment.as_ref()), + pretty_types::doc_comment(arena, binds.first().unwrap().metadata.comment.as_ref()), + self.pretty_attributes(&binds.first().unwrap().metadata.attributes), arena.concat(binds.iter().zip(prefixes).map(|(bind, prefix)| { let typ = bind.alias.value.unresolved_type(); let typ = match **typ { @@ -537,6 +540,29 @@ where } } + fn pretty_attributes(&self, attributes: J) -> DocBuilder<'a, Arena<'a>> + where + J: IntoIterator, + { + let arena = self.arena; + arena.concat(attributes.into_iter().map(|attribute| { + chain![arena; + "#[", + &attribute.name[..], + match attribute.arguments { + Some(ref arguments) => chain![arena; + "(", + &arguments[..], + ")" + ], + None => arena.nil(), + }, + "]", + arena.newline() + ] + })) + } + fn pretty_pattern(&self, pattern: &'a SpannedPattern) -> DocBuilder<'a, Arena<'a>> { self.pretty_pattern_(pattern, Prec::Top) } diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index 9dfc67371b..bed1520151 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -1,10 +1,12 @@ use itertools::Itertools; -use base::ast::{Alternative, Argument, Array, AstType, Do, Comment, Expr, ExprField, Lambda, Literal, Pattern, +use base::ast::{Alternative, Argument, Array, AstType, Do, Expr, ExprField, Lambda, Literal, Pattern, PatternField, SpannedExpr, SpannedIdent, TypeBinding, TypedIdent, ValueBinding}; use base::kind::{ArcKind, Kind}; use base::pos::{self, BytePos, Spanned}; use base::types::{AliasData, ArcType, ArgType, BuiltinType, Field, Generic, Type, TypeCache}; +use base::metadata::{Attribute, Metadata, Comment}; + use std::str::FromStr; use ::new_ident; @@ -13,7 +15,7 @@ use ordered_float::NotNaN; use {Error, ErrorEnv, FieldExpr, FieldPattern, MutIdentEnv}; -grammar<'input, 'env, Id>(type_cache: &TypeCache>, env: MutIdentEnv<'env, Id>, errors: ErrorEnv<'env, 'input>) +grammar<'input, 'env, Id>(input: &'input ::ParserSource, type_cache: &TypeCache>, env: MutIdentEnv<'env, Id>, errors: ErrorEnv<'env, 'input>) where Id: Clone; extern { @@ -62,6 +64,8 @@ extern { "]" => Token::RBracket, ")" => Token::RParen, + "#[" => Token::AttributeOpen, + "block open" => Token::OpenBlock, "block close" => Token::CloseBlock, "block separator" => Token::Semi, @@ -72,7 +76,6 @@ extern { SingleComma: () = { "," => (), - // Necessar => errors.push(<>.error) }; @@ -115,6 +118,43 @@ DocComment: Comment = } }; +Any: () = { + <"identifier">, + <"operator">, + <"string literal">, + <"char literal">, + <"int literal">, + <"byte literal">, + <"float literal">, + + <",">, + <"=">, + + "(" AttributeContents ")", + "[" AttributeContents "]", + "{" AttributeContents "}", +}; + +AttributeContents: () = { + Any* +}; + +AttributeArguments: Option = { + "(" AttributeContents ")" => + Some(input.src()[(start.to_usize() - input.start_index().to_usize())..(end.to_usize() - input.start_index().to_usize())].to_string()), + => None, +}; + +Attribute: Attribute = { + "#[" "]" => Attribute { name: name.into(), arguments, }, +}; + +Metadata: Metadata = { + => Metadata { comment: Some(comment), .. Metadata::default() }, + => Metadata { comment: Some(comment), attributes, .. Metadata::default() }, + => Metadata { attributes, .. Metadata::default() }, +}; + // Kinds AtomicKind: ArcKind = { @@ -177,7 +217,7 @@ TypeBinding: TypeBinding = { .collect(); TypeBinding { - comment: None, + metadata: Metadata::default(), name: id.clone(), alias: pos::spanned( row_span, @@ -196,7 +236,7 @@ TypeBinding: TypeBinding = { > "=" > => { TypeBinding { - comment: None, + metadata: Metadata::default(), name: id.clone(), alias: pos::spanned(body.span, AliasData::new(id.value.clone(), params, body.value)), finalized_alias: None, @@ -411,16 +451,17 @@ Alternative: Alternative = { }; FieldExpr: FieldExpr = { - > "=" => { - FieldExpr::Value(comment, id, Some(body)) + > "=" => { + FieldExpr::Value(metadata.unwrap_or_default(), id, Some(body)) }, - > => { + > => { + let metadata = metadata.unwrap_or_default(); let id = pos::spanned(id_str.span, env.from_str(id_str.value)); if id_str.value.starts_with(char::is_uppercase) { - FieldExpr::Type(comment, id, None) + FieldExpr::Type(metadata, id, None) } else { - FieldExpr::Value(comment, id, None) + FieldExpr::Value(metadata, id, None) } }, }; @@ -439,9 +480,9 @@ ValueArgument: Argument = { }; ValueBinding: ValueBinding = { - > )?> "=" => + > )?> "=" => ValueBinding { - comment: comment, + metadata: Metadata::default(), name: name, typ: typ, resolved_type: type_cache.hole(), @@ -449,9 +490,9 @@ ValueBinding: ValueBinding = { expr: body, }, - > )?> "=" => + > )?> "=" => ValueBinding { - comment, + metadata: Metadata::default(), name: name.map(|name| new_ident(type_cache, name)).map(Pattern::Ident), typ: typ, resolved_type: type_cache.hole(), @@ -498,13 +539,13 @@ AtomicExpr: Expr = { for field in fields { match field { - FieldExpr::Type(comment, id, typ) => types.push(ExprField { - comment: comment, + FieldExpr::Type(metadata, id, typ) => types.push(ExprField { + metadata, name: id, value: typ }), - FieldExpr::Value(comment, id, expr) => values.push(ExprField { - comment: comment, + FieldExpr::Value(metadata, id, expr) => values.push(ExprField { + metadata, name: id, value: expr }), @@ -560,16 +601,16 @@ InfixExpr = { }; AndValueBinding: ValueBinding = - "and" => { + "and" => { let mut binding = binding; - binding.comment = comment; + binding.metadata = metadata.unwrap_or_default(); binding }; AndTypeBinding: TypeBinding = - "and" => { + "and" => { let mut binding = binding; - binding.comment = comment; + binding.metadata = metadata.unwrap_or_default(); binding }; @@ -579,22 +620,22 @@ Expr: Expr = { "if" "then" "else" => Expr::IfElse(Box::new(pred), Box::new(if_true), Box::new(if_false)), - "match" "with" => - Expr::Match(Box::new(input), arms), + "match" "with" => + Expr::Match(Box::new(body), arms), - "let" SkipExtraTokens "in" => { + "let" SkipExtraTokens "in" => { let mut first = first; + first.metadata = metadata.unwrap_or_default(); let mut bindings = bindings; - first.comment = comment; bindings.insert(0, first); Expr::LetBindings(bindings, Box::new(body)) }, - "type" SkipExtraTokens "in" => { + "type" SkipExtraTokens "in" => { let mut first = first; + first.metadata = metadata.unwrap_or_default(); let mut bindings = bindings; - first.comment = comment; bindings.insert(0, first); Expr::TypeBindings(bindings, Box::new(body)) diff --git a/parser/src/infix.rs b/parser/src/infix.rs index c1bfac1738..08e20daf07 100644 --- a/parser/src/infix.rs +++ b/parser/src/infix.rs @@ -2,7 +2,7 @@ //! associative with the same precedence. Therefore we need to rebalance them //! after the fact. -use base::ast::{walk_mut_expr, Expr, IdentEnv, Literal, MutVisitor, SpannedExpr, SpannedIdent}; +use base::ast::{walk_mut_expr, Expr, IdentEnv, MutVisitor, SpannedExpr, SpannedIdent}; use base::error::Errors; use base::fnv::FnvMap; use base::pos::{self, BytePos, Spanned}; @@ -240,16 +240,17 @@ where fn visit_expr(&mut self, e: &mut SpannedExpr) { if let Expr::Infix { .. } = e.value { - let dummy = pos::spanned2( - BytePos::from(0), - BytePos::from(0), - Expr::Literal(Literal::Int(0)), - ); + let dummy = pos::spanned(e.span, Expr::Error(None)); let expr = mem::replace(e, dummy); match reparse(expr, self.symbols, &self.operators) { Ok(expr) => { *e = expr; } + // Undefined fixity errors are reported at the definition site + Err(Spanned { + value: Error::UndefinedFixity(_), + .. + }) => (), Err(err) => self.errors.push(err), } } @@ -278,7 +279,7 @@ impl fmt::Display for Error { lhs_meta, lhs_name, rhs_meta, rhs_name ) } - UndefinedFixity(ref op) => write!(f, "No fixity specified for `{}`. Fixity must be specified with the `@infix` attribute", op), + UndefinedFixity(ref op) => write!(f, "No fixity specified for `{}`. Fixity must be specified with the `#[infix]` attribute", op), InvalidFixity => write!( f, "Only `left` or `right` is valid associativity specifications" diff --git a/parser/src/layout.rs b/parser/src/layout.rs index 2b3c6695aa..4a53a93ba0 100644 --- a/parser/src/layout.rs +++ b/parser/src/layout.rs @@ -48,6 +48,8 @@ enum Context { MatchClause, /// In a lambda function Lambda, + /// In an attribute + Attribute, } #[derive(Debug)] @@ -265,7 +267,10 @@ where if token_closes_context(&token.value, offside.context) { match offside.context { Context::If => (), - Context::Brace | Context::Bracket | Context::Paren => return Ok(token), + Context::Brace + | Context::Bracket + | Context::Paren + | Context::Attribute => return Ok(token), Context::Block { .. } if token.value == Token::CloseBlock => { if let Some(offside) = self.indent_levels.last_mut() { // The enclosing block should not emit a block separator for the next @@ -348,7 +353,7 @@ where } (Context::Block { emit_semi: false }, Ordering::Equal) => { match token.value { - Token::DocComment { .. } | Token::OpenBlock => (), + Token::AttributeOpen | Token::DocComment { .. } | Token::OpenBlock => (), _ => { // If it is the first token in a sequence we dont want to emit a // separator @@ -431,6 +436,7 @@ where Token::LBrace => Some(Context::Brace), Token::LBracket => Some(Context::Bracket), Token::LParen => Some(Context::Paren), + Token::AttributeOpen => Some(Context::Attribute), _ => None, }; if let Some(context) = push_context { @@ -503,6 +509,7 @@ fn token_closes_context(token: &Token, context: Context) -> bool { | (&Token::CloseBlock, Context::Block { .. }) | (&Token::In, Context::Let) | (&Token::In, Context::Type) + | (&Token::RBracket, Context::Attribute) | (_, Context::Block { .. }) => true, (_, _) => false, } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index c07bb9797f..583d6e750b 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -20,7 +20,7 @@ use std::cell::RefCell; use std::fmt; use std::hash::Hash; -use base::ast::{Comment, Do, Expr, IdentEnv, SpannedExpr, SpannedPattern, TypedIdent, ValueBinding}; +use base::ast::{Do, Expr, IdentEnv, SpannedExpr, SpannedPattern, TypedIdent, ValueBinding}; use base::error::{AsDiagnostic, Errors}; use base::fnv::FnvMap; use base::metadata::Metadata; @@ -276,12 +276,8 @@ pub enum FieldPattern { } pub enum FieldExpr { - Type(Option, Spanned, Option>), - Value( - Option, - Spanned, - Option>, - ), + Type(Metadata, Spanned, Option>), + Value(Metadata, Spanned, Option>), } // Hack around LALRPOP's limited type syntax @@ -316,6 +312,18 @@ pub trait ParserSource { fn start_index(&self) -> BytePos; } +impl<'a, S> ParserSource for &'a S +where + S: ?Sized + ParserSource, +{ + fn src(&self) -> &str { + (**self).src() + } + fn start_index(&self) -> BytePos { + (**self).start_index() + } +} + impl ParserSource for str { fn src(&self) -> &str { self @@ -349,7 +357,7 @@ where let mut parse_errors = Errors::new(); let result = - grammar::TopExprParser::new().parse(type_cache, symbols, &mut parse_errors, layout); + grammar::TopExprParser::new().parse(&input, type_cache, symbols, &mut parse_errors, layout); // If there is a tokenizer error it may still exist in the result iterator wrapper. // If that is the case we return that error instead of the unexpected EOF error that lalrpop @@ -405,8 +413,13 @@ where let type_cache = TypeCache::default(); - let result = - grammar::LetOrExprParser::new().parse(&type_cache, symbols, &mut parse_errors, layout); + let result = grammar::LetOrExprParser::new().parse( + &input, + &type_cache, + symbols, + &mut parse_errors, + layout, + ); // If there is a tokenizer error it may still exist in the result iterator wrapper. // If that is the case we return that error instead of the unexpected EOF error that lalrpop @@ -453,49 +466,109 @@ pub fn reparse_infix( where Id: Clone + Eq + Hash + AsRef + ::std::fmt::Debug, { + use base::ast::{is_operator_char, walk_pattern, Pattern, Visitor}; + let mut errors = Errors::new(); - let op_table = OpTable::new(metadata.iter().filter_map(|(symbol, meta)| { - fn parse_infix(s: &str) -> Result { - let mut iter = s.splitn(2, " "); - let fixity = match iter.next().ok_or(InfixError::InvalidFixity)? { - "left" => Fixity::Left, - "right" => Fixity::Right, - _ => { - return Err(InfixError::InvalidFixity); - } - }; - let precedence = iter.next() - .and_then(|s| s.parse().ok()) - .and_then(|precedence| { - if precedence >= 0 { - Some(precedence) - } else { - None + + struct CheckInfix<'b, Id> + where + Id: 'b, + { + metadata: &'b FnvMap, + errors: &'b mut Errors>, + op_table: &'b mut OpTable, + } + + impl<'b, Id> CheckInfix<'b, Id> + where + Id: Clone + Eq + Hash + AsRef, + { + fn insert_infix(&mut self, id: &Id, span: Span) { + match self.metadata + .get(id) + .and_then(|meta| meta.get_attribute("infix")) + { + Some(infix_attribute) => { + fn parse_infix(s: &str) -> Result { + let mut iter = s.splitn(2, ","); + let fixity = match iter.next().ok_or(InfixError::InvalidFixity)?.trim() { + "left" => Fixity::Left, + "right" => Fixity::Right, + _ => { + return Err(InfixError::InvalidFixity); + } + }; + let precedence = iter.next() + .and_then(|s| s.trim().parse().ok()) + .and_then(|precedence| { + if precedence >= 0 { + Some(precedence) + } else { + None + } + }) + .ok_or(InfixError::InvalidPrecedence)?; + Ok(OpMeta { fixity, precedence }) + } + + match parse_infix(infix_attribute) { + Ok(op_meta) => { + self.op_table.operators.insert(id.clone(), op_meta); + } + Err(err) => { + self.errors.push(pos::spanned(span, err.into())); + } } - }) - .ok_or(InfixError::InvalidPrecedence)?; - Ok(OpMeta { fixity, precedence }) + } + + None => if id.as_ref().starts_with(is_operator_char) { + self.errors.push(pos::spanned( + span, + InfixError::UndefinedFixity(id.as_ref().into()).into(), + )) + }, + } } - meta.get_attribute("infix") - .and_then(|s| match parse_infix(s) { - Ok(op_meta) => Some((symbol.clone(), op_meta)), - Err(err) => { - errors.push(pos::spanned(Default::default(), err)); - None + } + impl<'a, 'b, Id> Visitor<'a> for CheckInfix<'b, Id> + where + Id: Clone + Eq + Hash + AsRef + 'a, + { + type Ident = Id; + + fn visit_pattern(&mut self, pattern: &'a SpannedPattern) { + match pattern.value { + Pattern::Ident(ref id) => { + self.insert_infix(&id.name, pattern.span); + } + Pattern::Record { ref fields, .. } => { + for field in fields.iter().filter(|field| field.value.is_none()) { + self.insert_infix(&field.name.value, field.name.span); + } } - }) - })); + _ => (), + } + walk_pattern(self, &pattern.value); + } + } + + let mut op_table = OpTable::new(None); + CheckInfix { + metadata, + errors: &mut errors, + op_table: &mut op_table, + }.visit_expr(expr); let mut reparser = Reparser::new(op_table, symbols); match reparser.reparse(expr) { Err(reparse_errors) => { - errors.extend(reparse_errors); + errors.extend(reparse_errors.into_iter().map(|err| err.map(Error::from))); } Ok(_) => {} } if errors.has_errors() { - Err(errors.into_iter().map(|err| err.map(Error::from)).collect()) + Err(errors) } else { Ok(()) } diff --git a/parser/src/token.rs b/parser/src/token.rs index 368c90e31a..cd653d6871 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -1,4 +1,5 @@ -use base::ast::{is_operator_char, Comment, CommentType}; +use base::ast::is_operator_char; +use base::metadata::{Comment, CommentType}; use base::pos::{self, BytePos, Column, Line, Location, Spanned}; use std::fmt; use std::str::Chars; @@ -54,6 +55,8 @@ pub enum Token<'input> { CloseBlock, Semi, + AttributeOpen, + EOF, // Required for the layout algorithm } @@ -107,6 +110,8 @@ impl<'input> fmt::Display for Token<'input> { CloseBlock => "CloseBlock", Semi => "Semi", + AttributeOpen => "#[", + EOF => "EOF", }; s.fmt(f) @@ -577,6 +582,15 @@ impl<'input> Iterator for Tokenizer<'input> { } } + '#' if self.test_lookahead(|ch| ch == '[') => { + self.bump(); + Some(Ok(pos::spanned2( + start, + self.next_loc(), + Token::AttributeOpen, + ))) + } + ch if is_ident_start(ch) => Some(Ok(self.identifier(start))), ch if is_digit(ch) || (ch == '-' && self.test_lookahead(is_digit)) => { Some(self.numeric_literal(start)) @@ -622,7 +636,7 @@ fn i64_from_hex(hex: &str, is_positive: bool) -> Result { #[cfg(test)] mod test { - use base::ast::Comment; + use base::metadata::Comment; use base::pos::{self, BytePos, Column, Line, Location, Spanned}; use codespan::{ByteOffset, ColumnOffset}; diff --git a/parser/tests/attributes.rs b/parser/tests/attributes.rs new file mode 100644 index 0000000000..8b068c6de7 --- /dev/null +++ b/parser/tests/attributes.rs @@ -0,0 +1,38 @@ +extern crate env_logger; +extern crate gluon_base as base; +extern crate gluon_parser as parser; + +#[macro_use] +mod support; + +use support::*; + +#[test] +fn any_tokens() { + let _ = ::env_logger::try_init(); + let text = r#" +#[test(ident "string" 42 = 'a' + )] +let (+) x y = error "" +{ } +"#; + parse_clear_span!(text); +} + +#[test] +fn bindings() { + let _ = ::env_logger::try_init(); + let text = r#" +#[infix(left, 6)] +let (+) x y = error "" +#[implicit] +type Test = Int + +{ + #[abc()] + Test, + #[test] + t = \_ -> True +} +"#; + parse_clear_span!(text); +} diff --git a/parser/tests/basic.rs b/parser/tests/basic.rs index d0dcdaabae..92a23fbd75 100644 --- a/parser/tests/basic.rs +++ b/parser/tests/basic.rs @@ -12,6 +12,7 @@ mod support; use base::ast::*; use base::pos::{self, BytePos, Span, Spanned}; use base::types::{Field, Type}; +use base::metadata::*; use support::*; #[test] @@ -124,13 +125,13 @@ fn type_mutually_recursive() { ); let binds = vec![ TypeBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(intern("Test")), alias: alias(intern("Test"), Vec::new(), test), finalized_alias: None, }, TypeBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(intern("Test2")), alias: alias(intern("Test2"), Vec::new(), test2), finalized_alias: None, @@ -311,7 +312,7 @@ fn let_pattern() { no_loc(Expr::LetBindings( vec![ ValueBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(Pattern::Record { typ: Type::hole(), types: Vec::new(), @@ -469,10 +470,13 @@ id no_loc(Expr::LetBindings( vec![ ValueBinding { - comment: Some(Comment { - typ: CommentType::Line, - content: "The identity function".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Line, + content: "The identity function".into(), + }), + ..Metadata::default() + }, name: no_loc(Pattern::Ident(TypedIdent::new(intern("id")))), typ: None, resolved_type: Type::hole(), @@ -500,7 +504,7 @@ id no_loc(Expr::LetBindings( vec![ ValueBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(Pattern::Ident(TypedIdent::new(intern("id")))), typ: None, resolved_type: Type::hole(), @@ -508,10 +512,13 @@ id expr: id("x"), }, ValueBinding { - comment: Some(Comment { - typ: CommentType::Line, - content: "The identity function".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Line, + content: "The identity function".into(), + }), + ..Metadata::default() + }, name: no_loc(Pattern::Ident(TypedIdent::new(intern("id2")))), typ: None, resolved_type: Type::hole(), @@ -538,10 +545,13 @@ id type_decls( vec![ TypeBinding { - comment: Some(Comment { - typ: CommentType::Block, - content: "Test type".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Block, + content: "Test type".into(), + }), + ..Metadata::default() + }, name: no_loc(intern("Test")), alias: alias(intern("Test"), Vec::new(), typ("Int")), finalized_alias: None, @@ -572,10 +582,13 @@ id type_decls( vec![ TypeBinding { - comment: Some(Comment { - typ: CommentType::Block, - content: "Test type".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Block, + content: "Test type".into(), + }), + ..Metadata::default() + }, name: no_loc(intern("Test")), alias: alias(intern("Test"), Vec::new(), typ("Int")), finalized_alias: None, @@ -603,10 +616,13 @@ id type_decls( vec![ TypeBinding { - comment: Some(Comment { - typ: CommentType::Line, - content: "Merge\nconsecutive\nline comments.".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Line, + content: "Merge\nconsecutive\nline comments.".into(), + }), + ..Metadata::default() + }, name: no_loc(intern("Test")), alias: alias(intern("Test"), Vec::new(), typ("Int")), finalized_alias: None, @@ -669,7 +685,7 @@ x no_loc(Expr::LetBindings( vec![ ValueBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(Pattern::Ident(TypedIdent::new(intern("x")))), typ: Some(Type::app(typ("->"), collect![typ("Int"), typ("Int")])), resolved_type: Type::hole(), @@ -762,20 +778,26 @@ fn doc_comment_on_record_field() { typ: Type::hole(), types: vec![ ExprField { - comment: Some(Comment { - typ: CommentType::Block, - content: "test".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Block, + content: "test".into(), + }), + ..Metadata::default() + }, name: no_loc("Test".into()), value: None, }, ], exprs: vec![ ExprField { - comment: Some(Comment { - typ: CommentType::Line, - content: "x binding".into(), - }), + metadata: Metadata { + comment: Some(Comment { + typ: CommentType::Line, + content: "x binding".into(), + }), + ..Metadata::default() + }, name: no_loc("x".into()), value: Some(int(1)), }, @@ -820,7 +842,7 @@ fn parse_let_or_expr() { Ok(x) => assert_eq!( x, Err(ValueBinding { - comment: None, + metadata: Metadata::default(), name: pos::spanned2( // Add one to each position since codespan return 1-indexed positions 5.into(), diff --git a/parser/tests/support/mod.rs b/parser/tests/support/mod.rs index d8d41be9a0..79f5bb9974 100644 --- a/parser/tests/support/mod.rs +++ b/parser/tests/support/mod.rs @@ -9,6 +9,7 @@ use base::ast::{walk_mut_alias, walk_mut_ast_type, walk_mut_expr, walk_mut_patte use base::error::Errors; use base::pos::{self, BytePos, Span, Spanned}; use base::kind::Kind; +use base::metadata::Metadata; use base::types::{Alias, AliasData, ArcType, Field, Generic, Type}; use parser::{parse_string, Error, ParseErrors}; use parser::infix::{Fixity, OpMeta, OpTable, Reparser}; @@ -196,7 +197,7 @@ pub fn let_a(s: &str, args: &[&str], e: SpExpr, b: SpExpr) -> SpExpr { no_loc(Expr::LetBindings( vec![ ValueBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(Pattern::Ident(TypedIdent::new(intern(s)))), typ: None, resolved_type: Type::hole(), @@ -278,7 +279,7 @@ pub fn type_decl( type_decls( vec![ TypeBinding { - comment: None, + metadata: Metadata::default(), name: no_loc(name.clone()), alias: no_loc(AliasData::new(name, args, typ)), finalized_alias: None, @@ -305,7 +306,7 @@ pub fn record_a( types: types .into_iter() .map(|(name, value)| ExprField { - comment: None, + metadata: Metadata::default(), name: no_loc(name), value: value, }) @@ -313,7 +314,7 @@ pub fn record_a( exprs: fields .into_iter() .map(|(name, value)| ExprField { - comment: None, + metadata: Metadata::default(), name: no_loc(name), value: value, }) diff --git a/repl/src/repl.rs b/repl/src/repl.rs index c3e8b3d8b8..5dd807d48c 100644 --- a/repl/src/repl.rs +++ b/repl/src/repl.rs @@ -94,7 +94,7 @@ fn find_info(args: WithVM) -> IO> { .ok() .and_then(|metadata| metadata.comment.as_ref()); if let Some(comment) = maybe_comment { - for line in comment.lines() { + for line in comment.content.lines() { write!(&mut buffer, "\n/// {}", line).unwrap(); } } @@ -285,7 +285,8 @@ fn eval_line_( Ok(x) => x, Err((_, err)) => { let code_map = compiler.code_map().clone(); - return FutureValue::sync(Err((compiler, InFile::new(code_map, err).into()))).boxed(); + return FutureValue::sync(Err((compiler, InFile::new(code_map, err).into()))) + .boxed(); } } }; diff --git a/src/compiler_pipeline.rs b/src/compiler_pipeline.rs index 30c94c5c85..01f9fb747c 100644 --- a/src/compiler_pipeline.rs +++ b/src/compiler_pipeline.rs @@ -572,7 +572,7 @@ where _: Extra, ) -> Result> { use vm::compiler::Compiler; - debug!("Compile `{}`", filename); + info!("Compile `{}`", filename); let mut module = { let env = thread.get_env(); diff --git a/src/import.rs b/src/import.rs index bcb01f1c0e..87e977bd76 100644 --- a/src/import.rs +++ b/src/import.rs @@ -93,6 +93,7 @@ static STD_LIBS: &[(&str, &str)] = &std_libs!( "monad", "monoid", "semigroup", + "reference", ); // When testing we use the files as-is in the repository to avoid recompiling after they are diff --git a/src/lib.rs b/src/lib.rs index fd47942de3..f50f38cf15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -689,7 +689,7 @@ impl VmBuilder { add_extern_module(&vm, "std.array.prim", ::vm::primitives::load_array); add_extern_module(&vm, "std.lazy", ::vm::lazy::load); - add_extern_module(&vm, "std.reference", ::vm::reference::load); + add_extern_module(&vm, "std.reference.prim", ::vm::reference::load); add_extern_module(&vm, "std.channel", ::vm::channel::load_channel); add_extern_module(&vm, "std.thread.prim", ::vm::channel::load_thread); diff --git a/std/applicative.glu b/std/applicative.glu index 575c97c8e0..1513f92d8c 100644 --- a/std/applicative.glu +++ b/std/applicative.glu @@ -2,7 +2,7 @@ let { Functor } = import! std.functor -/// @implicit +#[implicit] type Applicative (f : Type -> Type) = { functor : Functor f, /// Like `functor.map`, but this time the supplied function is embedded in `f` @@ -27,11 +27,11 @@ type Applicative (f : Type -> Type) = { let wrap ?app : [Applicative f] -> a -> f a = app.wrap let apply ?app : [Applicative f] -> f (a -> b) -> f a -> f b = app.apply -/// @infix left 4 +#[infix(left, 4)] let (<*>) ?app : [Applicative f] -> f (a -> b) -> f a -> f b = app.apply -/// @infix left 4 +#[infix(left, 4)] let (<*) ?app l r : [Applicative f] -> f a -> f b -> f a = app.functor.map (\x _ -> x) l <*> r -/// @infix left 4 +#[infix(left, 4)] let (*>) ?app l r : [Applicative f] -> f a -> f b -> f b = app.functor.map (\_ x -> x) l <*> r let map2 ?app f a b : [Applicative f] -> (a -> b -> c) @@ -42,7 +42,7 @@ let map2 ?app f a b : [Applicative f] -> (a -> b -> c) let map3 ?app f a b c : [Applicative f] -> (a -> b -> c -> d) -> f a -> f b -> f c -> f d = (app.functor.map f a) <*> b <*> c -/// @implicit +#[implicit] type Alternative f = { applicative : Applicative f, or : forall a . f a -> f a -> f a, @@ -51,7 +51,7 @@ type Alternative f = { let empty ?alt : [Alternative f] -> f a = alt.empty let or ?alt : [Alternative f] -> f a -> f a -> f a = alt.or -/// @infix left 3 +#[infix(left, 3)] let (<|>) ?alt : [Alternative f] -> f a -> f a -> f a = alt.or diff --git a/std/cmp.glu b/std/cmp.glu index cf15b458d5..7bbdafb081 100644 --- a/std/cmp.glu +++ b/std/cmp.glu @@ -3,40 +3,40 @@ let { Bool, Ordering } = import! std.types /// `Eq a` defines equality (==) on `a` -/// @implicit +#[implicit] type Eq a = { (==) : a -> a -> Bool } -/// @infix left 4 +#[infix(left, 4)] let (==) ?eq : [Eq a] -> a -> a -> Bool = eq.(==) -/// @infix left 4 +#[infix(left, 4)] let (/=) ?eq l r : [Eq a] -> a -> a -> Bool = if (eq.(==) l r) then False else True /// `Ord a` defines an ordering on `a` -/// @implicit +#[implicit] type Ord a = { eq : Eq a, compare : a -> a -> Ordering } -/// @infix left 4 +#[infix(left, 4)] let (<=) ?ord l r : [Ord a] -> a -> a -> Bool = match ord.compare l r with | LT -> True | EQ -> True | GT -> False -/// @infix left 4 +#[infix(left, 4)] let (<) ?ord l r : [Ord a] -> a -> a -> Bool = match ord.compare l r with | LT -> True | EQ -> False | GT -> False -/// @infix left 4 +#[infix(left, 4)] let (>) ?ord l r : [Ord a] -> a -> a -> Bool = match ord.compare l r with | LT -> False | EQ -> False | GT -> True -/// @infix left 4 +#[infix(left, 4)] let (>=) ?ord l r : [Ord a] -> a -> a -> Bool = match ord.compare l r with | LT -> False diff --git a/std/foldable.glu b/std/foldable.glu index fb042b3d8c..0badf79acb 100644 --- a/std/foldable.glu +++ b/std/foldable.glu @@ -4,7 +4,7 @@ let { Monoid } = import! std.monoid let { Monad } = import! std.monad let { Eq, (==) } = import! std.cmp -/// @implicit +#[implicit] type Foldable (f : Type -> Type) = { foldr : forall a b . (a -> b -> b) -> b -> f a -> b, foldl : forall a b . (b -> a -> b) -> b -> f a -> b diff --git a/std/function.glu b/std/function.glu index 2ac4fead6f..5ab45d0327 100644 --- a/std/function.glu +++ b/std/function.glu @@ -41,19 +41,19 @@ let const : a -> b -> a = applicative.wrap let flip f x y : (a -> b -> c) -> b -> a -> c = f y x /// Backward function application, where `f <| x == f x` -/// @infix right 0 +#[infix(right, 0)] let (<|) f x : (a -> b) -> a -> b = f x /// Forward function application, where `x |> f == f x` -/// @infix left 0 +#[infix(left, 0)] let (|>) x f : a -> (a -> b) -> b = f x /// Right-to-left function composition -/// @infix right 9 +#[infix(right, 9)] let (<<) : (b -> c) -> (a -> b) -> a -> c = category.compose /// Left-to-right function composition -/// @infix left 9 +#[infix(left, 9)] let (>>) : (a -> b) -> (b -> c) -> a -> c = flip category.compose { diff --git a/std/functor.glu b/std/functor.glu index 02aca67c4d..df352294fb 100644 --- a/std/functor.glu +++ b/std/functor.glu @@ -7,7 +7,7 @@ /// /// * `map id == id` /// * `map (f << g) == map f << map g` -/// @implicit +#[implicit] type Functor f = { /// Apply the supplied function to the contents of `f a`, converting it to /// an `f b` diff --git a/std/monad.glu b/std/monad.glu index 076307214f..01106a7c0b 100644 --- a/std/monad.glu +++ b/std/monad.glu @@ -3,7 +3,7 @@ let { Applicative } = import! std.applicative /// A generalised interface for imperatively sequencing actions -/// @implicit +#[implicit] type Monad (m : Type -> Type) = { applicative : Applicative m, /// This can be seen as akin to sequential variable binding in an @@ -36,9 +36,9 @@ type Monad (m : Type -> Type) = { } let flat_map ?m : [Monad m] -> (a -> m b) -> m a -> m b = m.flat_map -/// @infix right 1 +#[infix(right, 1)] let (=<<) ?m : [Monad m] -> (a -> m b) -> m a -> m b = m.flat_map -/// @infix left 1 +#[infix(left, 1)] let (>>=) ?m x f : [Monad m] -> m a -> (a -> m b) -> m b = m.flat_map f x let join ?m mm : [Monad m] -> m (m a) -> m a = mm >>= (\x -> x) diff --git a/std/monoid.glu b/std/monoid.glu index 5974caf8fc..cd09e8592f 100644 --- a/std/monoid.glu +++ b/std/monoid.glu @@ -6,7 +6,7 @@ let { Semigroup } = import! std.semigroup /// /// * `forall x . append x empty == x` /// * `forall x . append empty x == x` -/// @implicit +#[implicit] type Monoid a = { semigroup : Semigroup a, /// # Note diff --git a/std/parser.glu b/std/parser.glu index e1eee2b3ca..13f363303c 100644 --- a/std/parser.glu +++ b/std/parser.glu @@ -91,6 +91,7 @@ let update_position c position : Char -> Position -> Position = position + char.len_utf8 c /// Returns `message` as what was expected by `p` +#[infix(left, 0)] let () p message : Parser a -> String -> Parser a = parser (\stream -> match p stream with diff --git a/std/prelude.glu b/std/prelude.glu index 815da79b7b..627fc1791a 100644 --- a/std/prelude.glu +++ b/std/prelude.glu @@ -25,7 +25,7 @@ let monoid_Ordering : Monoid Ordering = { /// the following additional laws must hold: /// /// * `forall x . append (inverse x) x = empty = append x (inverse x)` -/// @implicit +#[implicit] type Group a = { monoid : Monoid a, /// The inverse operation @@ -34,7 +34,7 @@ type Group a = { /// The basic operation on numbers. /// Defined for both the primitive type `Int` and `Float` -/// @implicit +#[implicit] type Num a = { ord : Ord a, (+) : a -> a -> a, @@ -44,16 +44,16 @@ type Num a = { negate : a -> a } -/// @infix left 6 +#[infix(left, 6)] let (+) ?num : [Num a] -> a -> a -> a = num.(+) -/// @infix left 6 +#[infix(left, 6)] let (-) ?num : [Num a] -> a -> a -> a = num.(-) -/// @infix left 7 +#[infix(left, 7)] let (*) ?num : [Num a] -> a -> a -> a = num.(*) -/// @infix left 7 +#[infix(left, 7)] let (/) ?num : [Num a] -> a -> a -> a = num.(/) -/// @implicit +#[implicit] type Category (cat : Type -> Type -> Type) = { id : forall a . cat a a, compose : forall a b c . cat b c -> cat a b -> cat a c @@ -62,17 +62,19 @@ type Category (cat : Type -> Type -> Type) = { let id ?cat : forall cat a . [Category cat] -> cat a a = cat.id let compose ?cat : forall a b c . [Category cat] -> cat b c -> cat a b -> cat a c = cat.compose /// Right-to-left composition. Alias for `compose`. +#[infix(right, 9)] let (<<) ?cat : forall a b c . [Category cat] -> cat b c -> cat a b -> cat a c = cat.compose /// Left-to-right composition. Alias for `compose`, but with the arguments flipped. +#[infix(left, 9)] let (>>) ?cat f g : forall a b c . [Category cat] -> cat a b -> cat b c -> cat a c = cat.compose g f /// `Show a` represents a conversion function from `a` to a readable string. -/// @implicit +#[implicit] type Show a = { show : forall a . a -> String } let show ?s : [Show a] -> a -> String = s.show -/// @implicit +#[implicit] type Traversable t = { functor : Functor t, foldable : Foldable t, diff --git a/std/reference.glu b/std/reference.glu new file mode 100644 index 0000000000..96cefa58bf --- /dev/null +++ b/std/reference.glu @@ -0,0 +1,8 @@ +let reference = import! std.reference.prim +#[infix(right, 9)] +let (<-) = reference.(<-) +{ + (<-), + .. + reference +} diff --git a/std/semigroup.glu b/std/semigroup.glu index fac08ff38e..e430e8c9c0 100644 --- a/std/semigroup.glu +++ b/std/semigroup.glu @@ -4,7 +4,7 @@ /// This means the following laws must hold: /// /// * `forall x . append x (append y z) == append (append x y) z` -/// @implicit +#[implicit] type Semigroup a = { /// # Note /// @@ -13,7 +13,7 @@ type Semigroup a = { } let append ?s : [Semigroup a] -> a -> a -> a = s.append -/// @infix left 4 +#[infix(left, 4)] let (<>) ?s : [Semigroup a] -> a -> a -> a = s.append { diff --git a/std/stream.glu b/std/stream.glu index eb39017503..15973f71b9 100644 --- a/std/stream.glu +++ b/std/stream.glu @@ -1,5 +1,4 @@ let { Functor, Applicative } = import! std.prelude -let int @ { num = { (+) } } = import! std.int let list @ { List } = import! std.list let { Bool } = import! std.bool let { Option } = import! std.option diff --git a/tests/error.rs b/tests/error.rs index 45328f2bb9..a33dd68344 100644 --- a/tests/error.rs +++ b/tests/error.rs @@ -1,9 +1,12 @@ extern crate env_logger; extern crate gluon; +use gluon::{base, parser}; + mod support; use gluon::{Compiler, Error}; +use gluon::compiler_pipeline::*; use gluon::check::typecheck::TypeError; use gluon::vm::Error as VMError; @@ -63,3 +66,35 @@ fn panics_contain_stacktrace() { _ => panic!("Expected error with stacktrace {:?}", result), } } + +#[test] +fn undefined_infix() { + let _ = ::env_logger::try_init(); + + use parser::{InfixError, ParseErrors}; + use base::pos::{self, BytePos}; + + let expr = r#" + let (+) x y = 1 + 1 + 2 + "#; + + let vm = support::make_vm(); + let result = expr.reparse_infix( + &mut Compiler::new().implicit_prelude(false), + &vm, + "test", + expr, + ); + match result { + Err((_, Error::Parse(err))) => { + let error = parser::Error::Infix(InfixError::UndefinedFixity("+".to_string())); + let span = pos::span(BytePos::from(10), BytePos::from(13)); + assert_eq!( + err.errors(), + ParseErrors::from(vec![pos::spanned(span, error)]) + ); + } + _ => panic!(), + } +} diff --git a/tests/pass/lazy.glu b/tests/pass/lazy.glu index 09137636ba..5225ab059f 100644 --- a/tests/pass/lazy.glu +++ b/tests/pass/lazy.glu @@ -2,8 +2,6 @@ let { run, Test, assert_eq, test, ? } = import! std.test let { (<|) } = import! std.function let prelude = import! std.prelude let { Applicative, (*>), wrap } = import! std.applicative -let int = import! std.int -let { (+), (-), (*) } = int.num let { lazy, force } = import! std.lazy let l = lazy (\_ -> 123 + 57) diff --git a/tests/pattern_match.rs b/tests/pattern_match.rs index 4be80101d6..4ff3bd1a64 100644 --- a/tests/pattern_match.rs +++ b/tests/pattern_match.rs @@ -54,6 +54,7 @@ let string_prim = import! std.string.prim test_expr!{ let_record_pattern, r#" let string_prim = import! std.string.prim +#[infix(left, 6)] let (+) x y = x #Int+ y in let a = { x = 10, y = "abc" } diff --git a/tests/vm.rs b/tests/vm.rs index b38f5574ac..b97774e38a 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -39,11 +39,12 @@ in f(22) test_expr!{ add_operator, r" -/// @infix left 6 +#[infix(left, 6)] let (+) = \x y -> x #Int+ y in 1 + 2 + 3 ", 6i32 } + test_expr!{ divide_int, r" 120 #Int/ 4 ", @@ -56,6 +57,16 @@ r" 120.0 #Float/ 4.0 30.0f64 } +test_expr!{ infix_propagates, +r" +#[infix(left, 6)] +let (+) = \x y -> x #Int+ y +let { (+) = (++) } = { (+) } +1 ++ 2 ++ 3 +", +6i32 +} + #[test] fn record() { let _ = ::env_logger::try_init(); @@ -401,9 +412,9 @@ f 0 (\r -> { x = r #Int+ 1 }) fn overloaded_bindings() { let _ = ::env_logger::try_init(); let text = r#" -/// @implicit +#[implicit] let add_int x y = x #Int+ y -/// @implicit +#[implicit] let add_float x y = x #Float+ y let add ?f: [a -> a -> a] -> a -> a -> a = f @@ -457,7 +468,7 @@ test_expr!{ function_with_implicit_argument_from_record, r#" let f ?t x: [Int] -> () -> Int = t let x @ { ? } = - /// @implicit + #[implicit] let test = 1 { test } f () @@ -474,7 +485,7 @@ true test_expr!{ implicit_argument_selection1, r#" -/// @implicit +#[implicit] type Test = | Test () let f y: [a] -> a -> () = () let i = Test () @@ -486,9 +497,9 @@ f (Test ()) test_expr!{ prelude implicit_argument_selection2, r#" let string = import! std.string -let { append = (++) } = string.semigroup -/// @infix left 6 -let (++) = (++) +let { append } = string.semigroup +#[infix(left, 6)] +let (++) = append let equality l r : [Eq a] -> a -> a -> String = if l == r then " == " else " != " diff --git a/vm/src/reference.rs b/vm/src/reference.rs index 6035c246d8..d445bcac33 100644 --- a/vm/src/reference.rs +++ b/vm/src/reference.rs @@ -88,7 +88,9 @@ fn make_ref(a: WithVM>) -> Reference { } mod std { - pub use reference; + pub mod reference { + pub use reference as prim; + } } pub fn load(vm: &Thread) -> Result { @@ -98,9 +100,9 @@ pub fn load(vm: &Thread) -> Result { ExternModule::new( vm, record!{ - (store "<-") => named_primitive!(2, "std.reference.(<-)", std::reference::set), - load => named_primitive!(1, "std.reference.load", std::reference::get), - (ref_ "ref") => named_primitive!(1, "std.reference.ref", std::reference::make_ref), + (store "<-") => named_primitive!(2, "std.reference.prim.(<-)", std::reference::prim::set), + load => named_primitive!(1, "std.reference.prim.load", std::reference::prim::get), + (ref_ "ref") => named_primitive!(1, "std.reference.prim.ref", std::reference::prim::make_ref), }, ) }