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

feat: Adds data structures #64

Merged
merged 21 commits into from
Jan 11, 2020
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c84a7bf
feat: parsing of data structures
Wodann Nov 23, 2019
9a86797
feat(hir): resolve struct names
Wodann Nov 23, 2019
e241e86
improvement(struct): allow trailing semicolon
Wodann Nov 29, 2019
0b10bb1
feat(tuple): add parsing and HIR lowering for tuples
baszalmstra Nov 30, 2019
0ecdd6b
feat(structs): generate type ir for structs
baszalmstra Nov 30, 2019
7976f07
feat(struct): parsing of record literals
baszalmstra Dec 1, 2019
76e9188
feat(struct): type inference of record literals
Wodann Dec 4, 2019
b2aea8b
feat(struct): type inference of tuple literals
Wodann Dec 4, 2019
a6303a1
feat(struct): add lexing of indices, and parsing and type inferencing…
Wodann Dec 5, 2019
9c9758e
feat(struct): add IR generation for record, tuple, and unit struct li…
Wodann Dec 12, 2019
5d34607
feat(struct): add struct to ABI and runtime dispatch table
Wodann Dec 16, 2019
1069b9e
feat(struct): add IR generation for fields
Wodann Dec 17, 2019
d6dda23
misc(mun_hir): expression validator
baszalmstra Jan 10, 2020
afe18e1
feat: diagnostics for uninitialized variable access
baszalmstra Jan 10, 2020
d53ee2c
feat: struct memory type specifiers
baszalmstra Jan 10, 2020
a288b1f
feat: visibility can now include specifiers
baszalmstra Jan 10, 2020
0d41201
refactor: Renamed Source to InFile and added to diagnostics
baszalmstra Jan 10, 2020
e5f1fe8
misc: better diagnostic for uninitialized access
baszalmstra Jan 10, 2020
0e75842
feat(struct): add validity checks and diagnostics for struct literals
Wodann Jan 11, 2020
26621df
feat: initial very leaky implementation of heap allocation
baszalmstra Jan 11, 2020
a2c7ce7
misc: suggestions from review
baszalmstra Jan 11, 2020
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
Next Next commit
feat: parsing of data structures
Wodann authored and baszalmstra committed Jan 11, 2020
commit c84a7bfa923f469d04d12448f9d92fab97367209
51 changes: 51 additions & 0 deletions crates/mun_hir/src/adt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::sync::Arc;

use crate::{
arena::{Arena, RawId},
ids::{AstItemDef, StructId},
type_ref::TypeRef,
AsName, DefDatabase, Name,
};
use mun_syntax::ast::{self, NameOwner, TypeAscriptionOwner};

/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldData {
pub name: Name,
pub type_ref: TypeRef,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LocalStructFieldId(RawId);
impl_arena_id!(LocalStructFieldId);

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

impl StructData {
pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
let src = id.source(db);
let name = src
.ast
.name()
.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
};
Arc::new(StructData { name, fields })
}
}
58 changes: 57 additions & 1 deletion crates/mun_hir/src/code_model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub(crate) mod src;

use self::src::HasSource;
use crate::adt::{LocalStructFieldId, StructData};
use crate::diagnostics::DiagnosticSink;
use crate::expr::{Body, BodySourceMap};
use crate::ids::AstItemDef;
@@ -10,7 +11,10 @@ use crate::raw::{DefKind, RawFileItem};
use crate::resolve::{Resolution, Resolver};
use crate::ty::InferenceResult;
use crate::type_ref::{TypeRefBuilder, TypeRefId, TypeRefMap, TypeRefSourceMap};
use crate::{ids::FunctionId, AsName, DefDatabase, FileId, HirDatabase, Name, Ty};
use crate::{
ids::{FunctionId, StructId},
AsName, DefDatabase, FileId, HirDatabase, Name, Ty,
};
use mun_syntax::ast::{NameOwner, TypeAscriptionOwner, VisibilityOwner};
use rustc_hash::FxHashMap;
use std::sync::Arc;
@@ -91,6 +95,11 @@ impl ModuleData {
id: FunctionId::from_ast_id(loc_ctx, ast_id),
}))
}
DefKind::Struct(ast_id) => {
data.definitions.push(ModuleDef::Struct(Struct {
id: StructId::from_ast_id(loc_ctx, ast_id),
}))
}
}
}
};
@@ -107,6 +116,7 @@ impl ModuleData {
pub enum ModuleDef {
Function(Function),
BuiltinType(BuiltinType),
Struct(Struct),
}

impl From<Function> for ModuleDef {
@@ -121,6 +131,12 @@ impl From<BuiltinType> for ModuleDef {
}
}

impl From<Struct> for ModuleDef {
fn from(t: Struct) -> Self {
ModuleDef::Struct(t)
}
}

/// The definitions that have a body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefWithBody {
@@ -309,6 +325,43 @@ impl BuiltinType {
];
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Struct {
pub(crate) id: StructId,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructField {
pub(crate) parent: Struct,
pub(crate) id: LocalStructFieldId,
}

impl Struct {
pub fn module(self, db: &impl DefDatabase) -> Module {
Module {
file_id: self.id.file_id(db),
}
}

pub fn data(self, db: &impl DefDatabase) -> Arc<StructData> {
db.struct_data(self.id)
}

pub fn name(self, db: &impl DefDatabase) -> Name {
self.data(db).name.clone()
}

pub fn fields(self, db: &impl HirDatabase) -> Vec<StructField> {
self.data(db)
.fields
.as_ref()
.into_iter()
.flat_map(|it| it.iter())
.map(|(id, _)| StructField { parent: self, id })
.collect()
}
}

mod diagnostics {
use super::Module;
use crate::diagnostics::{DiagnosticSink, DuplicateDefinition};
@@ -330,6 +383,9 @@ mod diagnostics {
DefKind::Function(id) => {
SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax())
}
DefKind::Struct(id) => {
SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax())
}
}
}

32 changes: 31 additions & 1 deletion crates/mun_hir/src/code_model/src.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::code_model::Function;
use crate::code_model::{Function, Struct, StructField};
use crate::ids::AstItemDef;
use crate::{DefDatabase, FileId, SourceDatabase};
use mun_syntax::{ast, AstNode, SyntaxNode};
@@ -21,6 +21,36 @@ impl HasSource for Function {
}
}

impl HasSource for Struct {
type Ast = ast::StructDef;
fn source(self, db: &impl DefDatabase) -> Source<ast::StructDef> {
self.id.source(db)
}
}

impl HasSource for StructField {
type Ast = ast::RecordFieldDef;

fn source(self, db: &impl DefDatabase) -> Source<ast::RecordFieldDef> {
let src = self.parent.source(db);
let file_id = src.file_id;
let field_sources = if let ast::StructKind::Record(r) = src.ast.kind() {
r.fields().collect()
} else {
Vec::new()
};

let ast = field_sources
.into_iter()
.zip(self.parent.data(db).fields.as_ref().unwrap().iter())
.find(|(_syntax, (id, _))| *id == self.id)
.unwrap()
.0;

Source { file_id, ast }
}
}

impl<T> Source<T> {
pub(crate) fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
Source {
8 changes: 8 additions & 0 deletions crates/mun_hir/src/db.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use crate::input::{SourceRoot, SourceRootId};
use crate::name_resolution::Namespace;
use crate::ty::{FnSig, Ty, TypableDef};
use crate::{
adt::StructData,
code_model::{DefWithBody, FnData, Function, ModuleData},
ids,
line_index::LineIndex,
@@ -59,9 +60,16 @@ pub trait DefDatabase: SourceDatabase {
#[salsa::invoke(RawItems::raw_file_items_query)]
fn raw_items(&self, file_id: FileId) -> Arc<RawItems>;

#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: ids::StructId) -> Arc<StructData>;

/// Interns a function definition
#[salsa::interned]
fn intern_function(&self, loc: ids::ItemLoc<ast::FunctionDef>) -> ids::FunctionId;

/// Interns a struct definition
#[salsa::interned]
fn intern_struct(&self, loc: ids::ItemLoc<ast::StructDef>) -> ids::StructId;
}

#[salsa::query_group(HirDatabaseStorage)]
14 changes: 14 additions & 0 deletions crates/mun_hir/src/ids.rs
Original file line number Diff line number Diff line change
@@ -104,3 +104,17 @@ impl AstItemDef<ast::FunctionDef> for FunctionId {
db.lookup_intern_function(self)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructId(salsa::InternId);
impl_intern_key!(StructId);

impl AstItemDef<ast::StructDef> for StructId {
fn intern(db: &impl DefDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
db.intern_struct(loc)
}

fn lookup_intern(self, db: &impl DefDatabase) -> ItemLoc<ast::StructDef> {
db.lookup_intern_struct(self)
}
}
3 changes: 2 additions & 1 deletion crates/mun_hir/src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@

#[macro_use]
mod arena;
mod adt;
mod code_model;
mod db;
pub mod diagnostics;
@@ -60,4 +61,4 @@ use crate::{
source_id::{AstIdMap, FileAstId},
};

pub use self::code_model::{FnData, Function, Module, ModuleDef, Visibility};
pub use self::code_model::{FnData, Function, Module, ModuleDef, Struct, Visibility};
4 changes: 4 additions & 0 deletions crates/mun_hir/src/raw.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ pub(super) struct DefData {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum DefKind {
Function(FileAstId<ast::FunctionDef>),
Struct(FileAstId<ast::StructDef>),
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -54,6 +55,9 @@ impl RawItems {
ast::ModuleItemKind::FunctionDef(it) => {
(DefKind::Function((*ast_id_map).ast_id(&it)), it.name())
}
ast::ModuleItemKind::StructDef(it) => {
(DefKind::Struct((*ast_id_map).ast_id(&it)), it.name())
}
};

// If no name is provided an error is already emitted
7 changes: 6 additions & 1 deletion crates/mun_hir/src/ty.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ mod lower;

use crate::display::{HirDisplay, HirFormatter};
use crate::ty::infer::TypeVarId;
use crate::{Function, HirDatabase};
use crate::{Function, HirDatabase, Struct};
pub(crate) use infer::infer_query;
pub use infer::InferenceResult;
pub(crate) use lower::{fn_sig_for_fn, type_for_def, TypableDef};
@@ -55,6 +55,10 @@ pub enum TypeCtor {
/// The primitive boolean type. Written as `bool`.
Bool,

/// An abstract datatype (structures, tuples, or enumerations)
/// TODO: Add tuples and enumerations
Struct(Struct),

/// The never type `never`.
Never,

@@ -177,6 +181,7 @@ impl HirDisplay for ApplicationTy {
TypeCtor::Float => write!(f, "float"),
TypeCtor::Int => write!(f, "int"),
TypeCtor::Bool => write!(f, "bool"),
TypeCtor::Struct(def) => write!(f, "{}", def.name(f.db)),
TypeCtor::Never => write!(f, "never"),
TypeCtor::FnDef(def) => {
let sig = f.db.fn_signature(def);
32 changes: 31 additions & 1 deletion crates/mun_hir/src/ty/lower.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ use crate::name_resolution::Namespace;
use crate::resolve::{Resolution, Resolver};
use crate::ty::{FnSig, Ty, TypeCtor};
use crate::type_ref::{TypeRef, TypeRefId, TypeRefMap};
use crate::{Function, HirDatabase, ModuleDef, Path};
use crate::{Function, HirDatabase, ModuleDef, Path, Struct};

#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct LowerResult {
@@ -78,6 +78,7 @@ impl Ty {
pub enum TypableDef {
Function(Function),
BuiltinType(BuiltinType),
Struct(Struct),
}

impl From<Function> for TypableDef {
@@ -92,15 +93,28 @@ impl From<BuiltinType> for TypableDef {
}
}

impl From<Struct> for TypableDef {
fn from(f: Struct) -> Self {
TypableDef::Struct(f)
}
}

impl From<ModuleDef> for Option<TypableDef> {
fn from(d: ModuleDef) -> Self {
match d {
ModuleDef::Function(f) => Some(TypableDef::Function(f)),
ModuleDef::BuiltinType(t) => Some(TypableDef::BuiltinType(t)),
ModuleDef::Struct(t) => Some(TypableDef::Struct(t)),
}
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum CallableDef {
Function(Function),
Struct(Struct),
}

/// Build the declared type of an item. This depends on the namespace; e.g. for
/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
/// the constructor function `(usize) -> Foo` which lives in the values
@@ -109,6 +123,8 @@ pub(crate) fn type_for_def(db: &impl HirDatabase, def: TypableDef, ns: Namespace
match (def, ns) {
(TypableDef::Function(f), Namespace::Values) => type_for_fn(db, f),
(TypableDef::BuiltinType(t), Namespace::Types) => type_for_builtin(t),
(TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s),
(TypableDef::Struct(s), Namespace::Types) => type_for_struct(db, s),

// 'error' cases:
(TypableDef::Function(_), Namespace::Types) => Ty::Unknown,
@@ -143,6 +159,20 @@ pub fn fn_sig_for_fn(db: &impl HirDatabase, def: Function) -> FnSig {
FnSig::from_params_and_return(params, ret)
}

/// 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
} else {
unreachable!();
}
}

fn type_for_struct(_db: &impl HirDatabase, def: Struct) -> Ty {
Ty::simple(TypeCtor::Struct(def))
}

pub mod diagnostics {
use crate::type_ref::TypeRefId;

25 changes: 25 additions & 0 deletions crates/mun_hir/src/type_ref.rs
Original file line number Diff line number Diff line change
@@ -21,6 +21,31 @@ pub enum TypeRef {
Error,
}

impl TypeRef {
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
pub fn from_ast(node: ast::TypeRef) -> Self {
match node.kind() {
ast::TypeRefKind::NeverType(..) => TypeRef::Never,
ast::TypeRefKind::PathType(inner) => {
// FIXME: Use `Path::from_src`
inner
.path()
.and_then(Path::from_ast)
.map(TypeRef::Path)
.unwrap_or(TypeRef::Error)
}
}
}

pub fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
if let Some(node) = node {
TypeRef::from_ast(node)
} else {
TypeRef::Error
}
}
}

#[derive(Default, Debug, Eq, PartialEq)]
pub struct TypeRefSourceMap {
type_ref_map: FxHashMap<AstPtr<ast::TypeRef>, TypeRefId>,
6 changes: 5 additions & 1 deletion crates/mun_syntax/src/ast.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,11 @@ mod traits;
use crate::{syntax_node::SyntaxNodeChildren, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken};

pub use self::{
expr_extensions::*, extensions::PathSegmentKind, generated::*, tokens::*, traits::*,
expr_extensions::*,
extensions::{PathSegmentKind, StructKind},
generated::*,
tokens::*,
traits::*,
};

use std::marker::PhantomData;
25 changes: 23 additions & 2 deletions crates/mun_syntax/src/ast/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::ast::NameOwner;
use crate::{
ast::{self, AstNode},
ast::{self, child_opt, AstNode, NameOwner},
T,
};
use crate::{SmolStr, SyntaxNode};
@@ -88,3 +87,25 @@ impl ast::PathSegment {
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind {
Record(ast::RecordFieldDefList),
Unit,
}

impl StructKind {
fn from_node<N: AstNode>(node: &N) -> StructKind {
if let Some(r) = child_opt::<_, ast::RecordFieldDefList>(node) {
StructKind::Record(r)
} else {
StructKind::Unit
}
}
}

impl ast::StructDef {
pub fn kind(&self) -> StructKind {
StructKind::from_node(self)
}
}
101 changes: 100 additions & 1 deletion crates/mun_syntax/src/ast/generated.rs
Original file line number Diff line number Diff line change
@@ -567,7 +567,7 @@ pub struct ModuleItem {
impl AstNode for ModuleItem {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
FUNCTION_DEF => true,
FUNCTION_DEF | STRUCT_DEF => true,
_ => false,
}
}
@@ -585,19 +585,26 @@ impl AstNode for ModuleItem {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleItemKind {
FunctionDef(FunctionDef),
StructDef(StructDef),
}
impl From<FunctionDef> for ModuleItem {
fn from(n: FunctionDef) -> ModuleItem {
ModuleItem { syntax: n.syntax }
}
}
impl From<StructDef> for ModuleItem {
fn from(n: StructDef) -> ModuleItem {
ModuleItem { syntax: n.syntax }
}
}

impl ModuleItem {
pub fn kind(&self) -> ModuleItemKind {
match self.syntax.kind() {
FUNCTION_DEF => {
ModuleItemKind::FunctionDef(FunctionDef::cast(self.syntax.clone()).unwrap())
}
STRUCT_DEF => ModuleItemKind::StructDef(StructDef::cast(self.syntax.clone()).unwrap()),
_ => unreachable!(),
}
}
@@ -1021,6 +1028,68 @@ impl PrefixExpr {
}
}

// RecordFieldDef

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

impl AstNode for RecordFieldDef {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
RECORD_FIELD_DEF => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(RecordFieldDef { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
impl ast::NameOwner for RecordFieldDef {}
impl ast::VisibilityOwner for RecordFieldDef {}
impl ast::DocCommentsOwner for RecordFieldDef {}
impl ast::TypeAscriptionOwner for RecordFieldDef {}
impl RecordFieldDef {}

// RecordFieldDefList

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

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

// RetType

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1165,6 +1234,36 @@ impl Stmt {

impl Stmt {}

// StructDef

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

impl AstNode for StructDef {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
STRUCT_DEF => true,
_ => false,
}
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(StructDef { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
}
impl ast::NameOwner for StructDef {}
impl ast::VisibilityOwner for StructDef {}
impl ast::DocCommentsOwner for StructDef {}
impl StructDef {}

// TypeRef

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23 changes: 22 additions & 1 deletion crates/mun_syntax/src/grammar.ron
Original file line number Diff line number Diff line change
@@ -80,6 +80,7 @@ Grammar(
"let",
"mut",
"class",
"struct",
"never",
"pub"
],
@@ -103,6 +104,10 @@ Grammar(

"PARAM_LIST",
"PARAM",

"STRUCT_DEF",
"RECORD_FIELD_DEF_LIST",
"RECORD_FIELD_DEF",

"PATH_TYPE",
"NEVER_TYPE",
@@ -140,7 +145,7 @@ Grammar(
traits: [ "ModuleItemOwner", "FunctionDefOwner" ],
),
"ModuleItem": (
enum: ["FunctionDef"]
enum: ["FunctionDef", "StructDef"]
),
"Visibility": (),
"FunctionDef": (
@@ -163,6 +168,22 @@ Grammar(
"TypeAscriptionOwner"
],
),
"StructDef": (
traits: [
"NameOwner",
"VisibilityOwner",
"DocCommentsOwner",
]
),
"RecordFieldDefList": (collections: [("fields", "RecordFieldDef")]),
"RecordFieldDef": (
traits: [
"NameOwner",
"VisibilityOwner",
"DocCommentsOwner",
"TypeAscriptionOwner"
]
),
"LetStmt": (
options: [
["pat", "Pat"],
1 change: 1 addition & 0 deletions crates/mun_syntax/src/lib.rs
Original file line number Diff line number Diff line change
@@ -162,6 +162,7 @@ fn api_walkthrough() {
for item in file.items() {
match item.kind() {
ast::ModuleItemKind::FunctionDef(f) => func = Some(f),
ast::ModuleItemKind::StructDef(_) => (),
}
}

1 change: 1 addition & 0 deletions crates/mun_syntax/src/parsing/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod adt;
mod declarations;
mod expressions;
mod params;
50 changes: 50 additions & 0 deletions crates/mun_syntax/src/parsing/grammar/adt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::*;

pub(super) fn struct_def(p: &mut Parser, m: Marker) {
assert!(p.at(T![struct]));
p.bump(T![struct]);

name_recovery(p, declarations::DECLARATION_RECOVERY_SET);
match p.current() {
T![;] => {
p.bump(T![;]);
}
T!['{'] => record_field_def_list(p),
_ => {
p.error("expected ';', pr '{'");
}
}
m.complete(p, STRUCT_DEF);
}

pub(super) fn record_field_def_list(p: &mut Parser) {
assert!(p.at(T!['{']));
let m = p.start();
p.bump(T!['{']);
while !p.at(T!['}']) && !p.at(EOF) {
if p.at(T!['{']) {
error_block(p, "expected field");
continue;
}
record_field_def(p);
if !p.at(T!['}']) {
p.expect(T![,]);
}
}
p.expect(T!['}']);
m.complete(p, RECORD_FIELD_DEF_LIST);
}

fn record_field_def(p: &mut Parser) {
let m = p.start();
opt_visibility(p);
if p.at(IDENT) {
name(p);
p.expect(T![:]);
types::type_(p);
m.complete(p, RECORD_FIELD_DEF);
} else {
m.abandon(p);
p.error_and_bump("expected field declaration");
}
}
17 changes: 16 additions & 1 deletion crates/mun_syntax/src/parsing/grammar/declarations.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::T;

pub(super) const DECLARATION_RECOVERY_SET: TokenSet = token_set![FN_KW, PUB_KW];
pub(super) const DECLARATION_RECOVERY_SET: TokenSet = token_set![FN_KW, PUB_KW, STRUCT_KW];

pub(super) fn mod_contents(p: &mut Parser) {
while !p.at(EOF) {
@@ -34,6 +34,11 @@ pub(super) fn declaration(p: &mut Parser) {
pub(super) fn maybe_declaration(p: &mut Parser, m: Marker) -> Result<(), Marker> {
opt_visibility(p);

let m = match declarations_without_modifiers(p, m) {
Ok(()) => return Ok(()),
Err(m) => m,
};

match p.current() {
T![fn] => {
fn_def(p);
@@ -44,6 +49,16 @@ pub(super) fn maybe_declaration(p: &mut Parser, m: Marker) -> Result<(), Marker>
Ok(())
}

fn declarations_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
match p.current() {
T![struct] => {
adt::struct_def(p, m);
}
_ => return Err(m),
};
Ok(())
}

pub(super) fn fn_def(p: &mut Parser) {
assert!(p.at(T![fn]));
p.bump(T![fn]);
11 changes: 11 additions & 0 deletions crates/mun_syntax/src/syntax_kind/generated.rs
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@ pub enum SyntaxKind {
LET_KW,
MUT_KW,
CLASS_KW,
STRUCT_KW,
NEVER_KW,
PUB_KW,
INT_NUMBER,
@@ -87,6 +88,9 @@ pub enum SyntaxKind {
VISIBILITY,
PARAM_LIST,
PARAM,
STRUCT_DEF,
RECORD_FIELD_DEF_LIST,
RECORD_FIELD_DEF,
PATH_TYPE,
NEVER_TYPE,
LET_STMT,
@@ -174,6 +178,7 @@ macro_rules! T {
(let) => { $crate::SyntaxKind::LET_KW };
(mut) => { $crate::SyntaxKind::MUT_KW };
(class) => { $crate::SyntaxKind::CLASS_KW };
(struct) => { $crate::SyntaxKind::STRUCT_KW };
(never) => { $crate::SyntaxKind::NEVER_KW };
(pub) => { $crate::SyntaxKind::PUB_KW };
}
@@ -213,6 +218,7 @@ impl SyntaxKind {
| LET_KW
| MUT_KW
| CLASS_KW
| STRUCT_KW
| NEVER_KW
| PUB_KW
=> true,
@@ -330,6 +336,7 @@ impl SyntaxKind {
LET_KW => &SyntaxInfo { name: "LET_KW" },
MUT_KW => &SyntaxInfo { name: "MUT_KW" },
CLASS_KW => &SyntaxInfo { name: "CLASS_KW" },
STRUCT_KW => &SyntaxInfo { name: "STRUCT_KW" },
NEVER_KW => &SyntaxInfo { name: "NEVER_KW" },
PUB_KW => &SyntaxInfo { name: "PUB_KW" },
INT_NUMBER => &SyntaxInfo { name: "INT_NUMBER" },
@@ -345,6 +352,9 @@ impl SyntaxKind {
VISIBILITY => &SyntaxInfo { name: "VISIBILITY" },
PARAM_LIST => &SyntaxInfo { name: "PARAM_LIST" },
PARAM => &SyntaxInfo { name: "PARAM" },
STRUCT_DEF => &SyntaxInfo { name: "STRUCT_DEF" },
RECORD_FIELD_DEF_LIST => &SyntaxInfo { name: "RECORD_FIELD_DEF_LIST" },
RECORD_FIELD_DEF => &SyntaxInfo { name: "RECORD_FIELD_DEF" },
PATH_TYPE => &SyntaxInfo { name: "PATH_TYPE" },
NEVER_TYPE => &SyntaxInfo { name: "NEVER_TYPE" },
LET_STMT => &SyntaxInfo { name: "LET_STMT" },
@@ -396,6 +406,7 @@ impl SyntaxKind {
"let" => LET_KW,
"mut" => MUT_KW,
"class" => CLASS_KW,
"struct" => STRUCT_KW,
"never" => NEVER_KW,
"pub" => PUB_KW,
_ => return None,
2 changes: 1 addition & 1 deletion crates/mun_syntax/src/tests/lexer.rs
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ fn keywords() {
lex_snapshot(
r#"
and break do else false for fn if in nil
return true while let mut class
return true while let mut struct class
never loop pub
"#,
)
14 changes: 14 additions & 0 deletions crates/mun_syntax/src/tests/parser.rs
Original file line number Diff line number Diff line change
@@ -60,6 +60,20 @@ fn literals() {
);
}

#[test]
fn structures() {
ok_snapshot_test(
r#"
struct Foo;
struct Foo {}
struct Foo {
a: float,
b: int,
}
"#,
)
}

#[test]
fn unary_expr() {
ok_snapshot_test(
6 changes: 4 additions & 2 deletions crates/mun_syntax/src/tests/snapshots/lexer__keywords.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: crates/mun_syntax/src/tests/lexer.rs
expression: "and break do else false for fn if in nil\nreturn true while let mut class \nnever loop pub"
expression: "and break do else false for fn if in nil\nreturn true while let mut struct class\nnever loop pub"
---
AND_KW 3 "and"
WHITESPACE 1 " "
@@ -32,8 +32,10 @@ LET_KW 3 "let"
WHITESPACE 1 " "
MUT_KW 3 "mut"
WHITESPACE 1 " "
STRUCT_KW 6 "struct"
WHITESPACE 1 " "
CLASS_KW 5 "class"
WHITESPACE 2 " \n"
WHITESPACE 1 "\n"
NEVER_KW 5 "never"
WHITESPACE 1 " "
LOOP_KW 4 "loop"
57 changes: 57 additions & 0 deletions crates/mun_syntax/src/tests/snapshots/parser__structures.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
source: crates/mun_syntax/src/tests/parser.rs
expression: "struct Foo;\nstruct Foo {}\nstruct Foo {\n a: float,\n b: int,\n}"
---
SOURCE_FILE@[0; 66)
STRUCT_DEF@[0; 11)
STRUCT_KW@[0; 6) "struct"
WHITESPACE@[6; 7) " "
NAME@[7; 10)
IDENT@[7; 10) "Foo"
SEMI@[10; 11) ";"
WHITESPACE@[11; 12) "\n"
STRUCT_DEF@[12; 25)
STRUCT_KW@[12; 18) "struct"
WHITESPACE@[18; 19) " "
NAME@[19; 22)
IDENT@[19; 22) "Foo"
WHITESPACE@[22; 23) " "
RECORD_FIELD_DEF_LIST@[23; 25)
L_CURLY@[23; 24) "{"
R_CURLY@[24; 25) "}"
WHITESPACE@[25; 26) "\n"
STRUCT_DEF@[26; 66)
STRUCT_KW@[26; 32) "struct"
WHITESPACE@[32; 33) " "
NAME@[33; 36)
IDENT@[33; 36) "Foo"
WHITESPACE@[36; 37) " "
RECORD_FIELD_DEF_LIST@[37; 66)
L_CURLY@[37; 38) "{"
WHITESPACE@[38; 43) "\n "
RECORD_FIELD_DEF@[43; 51)
NAME@[43; 44)
IDENT@[43; 44) "a"
COLON@[44; 45) ":"
WHITESPACE@[45; 46) " "
PATH_TYPE@[46; 51)
PATH@[46; 51)
PATH_SEGMENT@[46; 51)
NAME_REF@[46; 51)
IDENT@[46; 51) "float"
COMMA@[51; 52) ","
WHITESPACE@[52; 57) "\n "
RECORD_FIELD_DEF@[57; 63)
NAME@[57; 58)
IDENT@[57; 58) "b"
COLON@[58; 59) ":"
WHITESPACE@[59; 60) " "
PATH_TYPE@[60; 63)
PATH@[60; 63)
PATH_SEGMENT@[60; 63)
NAME_REF@[60; 63)
IDENT@[60; 63) "int"
COMMA@[63; 64) ","
WHITESPACE@[64; 65) "\n"
R_CURLY@[65; 66) "}"