Skip to content

Commit

Permalink
feat(tuple): add parsing and HIR lowering for tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Nov 30, 2019
1 parent bbcdce8 commit 4db8e46
Show file tree
Hide file tree
Showing 16 changed files with 452 additions and 169 deletions.
67 changes: 50 additions & 17 deletions crates/mun_hir/src/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,42 @@ use crate::{
};
use mun_syntax::ast::{self, NameOwner, TypeAscriptionOwner};

/// A single field of an enum variant or struct
/// A single field of a record
/// ```mun
/// struct Foo {
/// a: int, // <- this
/// }
/// ```
/// or
/// ```mun
/// struct Foo(
/// int, // <- this
/// )
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldData {
pub struct StructFieldData {
pub name: Name,
pub type_ref: TypeRef,
}

/// An identifier for a struct's or tuple's field
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LocalStructFieldId(RawId);
impl_arena_id!(LocalStructFieldId);
pub struct StructFieldId(RawId);
impl_arena_id!(StructFieldId);

/// A struct's fields' data (record, tuple, or unit struct)
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StructKind {
Record,
Tuple,
Unit,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
pub name: Name,
pub fields: Option<Arc<Arena<LocalStructFieldId, FieldData>>>,
pub fields: Arena<StructFieldId, StructFieldData>,
pub kind: StructKind,
}

impl StructData {
Expand All @@ -34,18 +55,30 @@ impl StructData {
.map(|n| n.as_name())
.unwrap_or_else(Name::missing);

let fields = if let ast::StructKind::Record(r) = src.ast.kind() {
let fields = r
.fields()
.map(|fd| FieldData {
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
})
.collect();
Some(Arc::new(fields))
} else {
None
let (fields, kind) = match src.ast.kind() {
ast::StructKind::Record(r) => {
let fields = r
.fields()
.map(|fd| StructFieldData {
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
})
.collect();
(fields, StructKind::Record)
}
ast::StructKind::Tuple(t) => {
let fields = t
.fields()
.enumerate()
.map(|(index, fd)| StructFieldData {
name: Name::new_tuple_field(index),
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
})
.collect();
(fields, StructKind::Tuple)
}
ast::StructKind::Unit => (Arena::default(), StructKind::Unit),
};
Arc::new(StructData { name, fields })
Arc::new(StructData { name, fields, kind })
}
}
8 changes: 3 additions & 5 deletions crates/mun_hir/src/code_model.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub(crate) mod src;

use self::src::HasSource;
use crate::adt::{LocalStructFieldId, StructData};
use crate::adt::{StructData, StructFieldId};
use crate::diagnostics::DiagnosticSink;
use crate::expr::{Body, BodySourceMap};
use crate::ids::AstItemDef;
Expand Down Expand Up @@ -311,7 +311,7 @@ pub struct Struct {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructField {
pub(crate) parent: Struct,
pub(crate) id: LocalStructFieldId,
pub(crate) id: StructFieldId,
}

impl Struct {
Expand All @@ -332,9 +332,7 @@ impl Struct {
pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> {
self.data(db)
.fields
.as_ref()
.into_iter()
.flat_map(|it| it.iter())
.iter()
.map(|(id, _)| StructField { parent: self, id })
.collect()
}
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_hir/src/code_model/src.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl HasSource for StructField {

let ast = field_sources
.into_iter()
.zip(self.parent.data(db).fields.as_ref().unwrap().iter())
.zip(self.parent.data(db).fields.iter())
.find(|(_syntax, (id, _))| *id == self.id)
.unwrap()
.0;
Expand Down
67 changes: 50 additions & 17 deletions crates/mun_hir/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,60 @@ use std::fmt;

/// `Name` is a wrapper around string, which is used in hir for both references
/// and declarations.
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name {
text: SmolStr,
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name(Repr);

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Repr {
Text(SmolStr),
TupleField(usize),
}

impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.text, f)
match &self.0 {
Repr::Text(text) => fmt::Display::fmt(&text, f),
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}

impl fmt::Debug for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.text, f)
impl Name {
/// Note: this is private to make creating name from random string hard.
/// Hopefully, this should allow us to integrate hygiene cleaner in the
/// future, and to switch to interned representation of names.
const fn new_text(text: SmolStr) -> Name {
Name(Repr::Text(text))
}
}

impl Name {
const fn new(text: SmolStr) -> Name {
Name { text }
pub(crate) fn new_tuple_field(idx: usize) -> Name {
Name(Repr::TupleField(idx))
}

/// Shortcut to create inline plain text name
const fn new_inline_ascii(len: usize, text: &[u8]) -> Name {
Name::new_text(SmolStr::new_inline_from_ascii(len, text))
}

/// Resolve a name from the text of token.
fn resolve(raw_text: &SmolStr) -> Name {
let raw_start = "r#";
if raw_text.as_str().starts_with(raw_start) {
Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
} else {
Name::new_text(raw_text.clone())
}
}

pub(crate) fn missing() -> Name {
Name::new("[missing name]".into())
Name::new_text("[missing name]".into())
}

pub(crate) fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
Repr::TupleField(idx) => Some(idx),
_ => None,
}
}
}

Expand All @@ -36,16 +66,19 @@ pub(crate) trait AsName {

impl AsName for ast::NameRef {
fn as_name(&self) -> Name {
Name::new(self.text().clone())
match self.as_tuple_field() {
Some(idx) => Name::new_tuple_field(idx),
None => Name::resolve(self.text()),
}
}
}

impl AsName for ast::Name {
fn as_name(&self) -> Name {
Name::new(self.text().clone())
Name::resolve(self.text())
}
}

pub(crate) const FLOAT: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"float"));
pub(crate) const INT: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"int"));
pub(crate) const BOOLEAN: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"bool"));
pub(crate) const FLOAT: Name = Name::new_inline_ascii(5, b"float");
pub(crate) const INT: Name = Name::new_inline_ascii(3, b"int");
pub(crate) const BOOLEAN: Name = Name::new_inline_ascii(4, b"bool");
5 changes: 3 additions & 2 deletions crates/mun_hir/src/ty/lower.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) use self::diagnostics::LowerDiagnostic;
use crate::adt::StructKind;
use crate::code_model::BuiltinType;
use crate::name_resolution::Namespace;
use crate::resolve::{Resolution, Resolver};
Expand Down Expand Up @@ -162,8 +163,8 @@ pub fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
/// Build the type of a struct constructor.
fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty {
let struct_data = db.struct_data(def.id);
if struct_data.fields.is_none() {
type_for_struct(db, def) // Unit struct
if struct_data.kind == StructKind::Unit {
type_for_struct(db, def)
} else {
unreachable!();
}
Expand Down
10 changes: 5 additions & 5 deletions crates/mun_hir/src/ty/snapshots/tests__struct_declaration.snap
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
source: crates/mun_hir/src/ty/tests.rs
expression: "struct Foo;\nstruct Bar {}\nstruct Baz {\n f: float,\n i: int,\n}\n\nfn main() {\n let foo: Foo;\n let bar: Bar;\n let baz: Baz;\n}"
expression: "struct Foo;\nstruct Bar {\n f: float,\n i: int,\n}\nstruct Baz(float, int);\n\n\nfn main() {\n let foo: Foo;\n let bar: Bar;\n let baz: Baz;\n}"
---
[78; 135) '{ ...Baz; }': nothing
[88; 91) 'foo': Foo
[106; 109) 'bar': Bar
[124; 127) 'baz': Baz
[89; 146) '{ ...Baz; }': nothing
[99; 102) 'foo': Foo
[117; 120) 'bar': Bar
[135; 138) 'baz': Baz
5 changes: 3 additions & 2 deletions crates/mun_hir/src/ty/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,12 @@ fn struct_declaration() {
infer_snapshot(
r#"
struct Foo;
struct Bar {}
struct Baz {
struct Bar {
f: float,
i: int,
}
struct Baz(float, int);
fn main() {
let foo: Foo;
Expand Down
16 changes: 15 additions & 1 deletion crates/mun_syntax/src/ast/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
ast::{self, child_opt, AstNode, NameOwner},
T,
SyntaxKind, T,
};
use crate::{SmolStr, SyntaxNode};
use text_unit::TextRange;
Expand All @@ -15,6 +15,17 @@ impl ast::NameRef {
pub fn text(&self) -> &SmolStr {
text_of_first_token(self.syntax())
}

pub fn as_tuple_field(&self) -> Option<usize> {
self.syntax().children_with_tokens().find_map(|c| {
if c.kind() == SyntaxKind::INT_NUMBER {
c.as_token()
.and_then(|tok| tok.text().as_str().parse().ok())
} else {
None
}
})
}
}

impl ast::FunctionDef {
Expand Down Expand Up @@ -91,13 +102,16 @@ impl ast::PathSegment {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind {
Record(ast::RecordFieldDefList),
Tuple(ast::TupleFieldDefList),
Unit,
}

impl StructKind {
fn from_node<N: AstNode>(node: &N) -> StructKind {
if let Some(r) = child_opt::<_, ast::RecordFieldDefList>(node) {
StructKind::Record(r)
} else if let Some(t) = child_opt::<_, ast::TupleFieldDefList>(node) {
StructKind::Tuple(t)
} else {
StructKind::Unit
}
Expand Down
63 changes: 63 additions & 0 deletions crates/mun_syntax/src/ast/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,69 @@ impl ast::VisibilityOwner for StructDef {}
impl ast::DocCommentsOwner for StructDef {}
impl StructDef {}

// TupleFieldDef

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TupleFieldDef {
pub(crate) syntax: SyntaxNode,
}

impl AstNode for TupleFieldDef {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
TUPLE_FIELD_DEF => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(TupleFieldDef { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
impl ast::VisibilityOwner for TupleFieldDef {}
impl TupleFieldDef {
pub fn type_ref(&self) -> Option<TypeRef> {
super::child_opt(self)
}
}

// TupleFieldDefList

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TupleFieldDefList {
pub(crate) syntax: SyntaxNode,
}

impl AstNode for TupleFieldDefList {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
TUPLE_FIELD_DEF_LIST => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(TupleFieldDefList { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
impl TupleFieldDefList {
pub fn fields(&self) -> impl Iterator<Item = TupleFieldDef> {
super::children(self)
}
}

// TypeRef

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
Loading

0 comments on commit 4db8e46

Please sign in to comment.