enumerations

This commit is contained in:
nothendev 2023-05-05 14:21:24 +03:00
parent 68a58e6a53
commit 9f36b7ef7f
11 changed files with 547 additions and 71 deletions

View file

@ -1,19 +1,21 @@
Type Byte = U8; Alias Byte = U8;
Type Int = U32; Alias Int = U32;
Type String = Vector<Byte>; Alias String = Vector<Byte>;
Enumurate Boolean { Enumeration Boolean {
False = 0, False = 0,
True = 1, True = 1,
} }
Union Option<T>{ Enumeration Nothing {}
None,
Some<T> Enumeration Option<T> {
None,
Some(T)
} }
Structure Version { Structure Version {
major: Byte, major: Byte,
minor: Byte, minor: Byte,
patch: Byte, patch: Byte,
}; }

View file

@ -5,7 +5,25 @@ Constant Hi = "WHY???/\n";
Alias Yo = Byte; Alias Yo = Byte;
Constant Version = Make Version { Constant Version = Make Version {
major: 1, major: 1
minor: 0, minor: 0
patch: 0 patch: 0
}; };
Interface Iface {
Function hello Takes(Int Boolean) Returns(Int);
}
Function a_free_function Returns(Boolean);
Structure Hello {
world: Boolean prompt: Option<String>
}
Enumeration Reality {
Dead(Boolean Boolean),
Alive {
health: Int
dying: Boolean
}
}

View file

@ -17,20 +17,60 @@ pub struct IDLModule {
#[derive(Debug)] #[derive(Debug)]
pub enum Item { pub enum Item {
_Interface(ItemInterface), Interface(ItemInterface),
Alias(ItemAlias), Alias(ItemAlias),
Constant(ItemConstant), Constant(ItemConstant),
Function(Function),
Structure(ItemStructure),
Enumeration(ItemEnumeration)
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Function { pub struct Function {
pub name: String, pub name: String,
pub takes: Vec<Type>, pub takes: Vec<Type>,
pub returns: Type pub returns: Type,
} }
// why #[derive(Debug)]
pub type Type = String; pub struct Type {
pub name: String,
pub arguments: TypeArguments,
}
impl Type {
pub fn infer() -> Self {
Self {
name: String::from(INFER_TYPE),
arguments: TypeArguments::None
}
}
}
pub const NOTHING_TYPE: &str = "Nothing";
pub const INFER_TYPE: &str = "_";
impl Default for Type {
fn default() -> Self {
Self {
name: String::from(NOTHING_TYPE),
arguments: TypeArguments::None,
}
}
}
#[derive(Debug, Default)]
pub enum TypeArguments {
/// TypeName
#[default]
None,
/// TypeName<T1, T2, T3, TN>
AngleBracketed(Vec<Box<Type>>),
}
pub fn nothing() -> Type {
Type::default()
}
#[derive(Debug)] #[derive(Debug)]
pub struct ItemInterface { pub struct ItemInterface {
@ -38,16 +78,44 @@ pub struct ItemInterface {
pub functions: Vec<Function>, pub functions: Vec<Function>,
} }
#[derive(Debug)]
pub struct ItemStructure {
pub name: String,
pub fields: HashMap<String, Type>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct ItemAlias { pub struct ItemAlias {
pub name: String, pub name: String,
pub referree: String, pub referree: Type,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ItemConstant { pub struct ItemConstant {
pub name: String, pub name: String,
pub expr: Expr pub expr: Expr,
}
#[derive(Debug)]
pub struct ItemEnumeration {
pub name: String,
pub arguments: TypeArguments,
pub variants: Vec<EnumerationVariant>
}
#[derive(Debug)]
pub struct EnumerationVariant {
pub name: String,
pub content: EnumerationContent
}
#[derive(Debug, Default)]
pub enum EnumerationContent {
#[default]
None,
Tuple(Vec<Type>),
Structure(HashMap<String, Type>),
Value(NumberLiteral)
} }
#[derive(Debug)] #[derive(Debug)]
@ -59,20 +127,20 @@ pub struct UseDecl {
pub enum Expr { pub enum Expr {
Literal(Literal), Literal(Literal),
_IdentAccess(String), _IdentAccess(String),
Make(Box<ExprMake>) Make(Box<ExprMake>),
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ExprMake { pub struct ExprMake {
pub name: String, pub name: String,
pub params: HashMap<String, Expr> pub params: HashMap<String, Expr>,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Literal { pub enum Literal {
String(String), String(String),
Number(NumberLiteral), Number(NumberLiteral),
Char(char) Char(char),
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,6 +1,6 @@
use std::{ use std::{
fmt::Display, fmt::Display,
ops::{Add, Range, AddAssign}, ops::{Add, AddAssign, Range},
}; };
use logos::Logos; use logos::Logos;
@ -51,7 +51,10 @@ pub enum Token {
#[regex(r#"(-)?\d+"#, |lex| lex.slice().parse().ok())] #[regex(r#"(-)?\d+"#, |lex| lex.slice().parse().ok())]
NumberLiteral(i64), NumberLiteral(i64),
#[regex(r"(ptr|u8|i8|u16|i16|u32|i32|u64|i64|f32|f64)", |lex| NumberSuffix::lexer(lex.slice()).next().and_then(Result::ok))] #[regex(
r"(ptr|u8|i8|u16|i16|u32|i32|u64|i64|f32|f64)",
|lex| NumberSuffix::lexer(lex.slice()).next().and_then(Result::ok)
)]
NumberSuffix(NumberSuffix), NumberSuffix(NumberSuffix),
#[regex(r#"[a-zA-Z_][a-zA-Z\d_]*"#, |lex| Ident::lexer(lex.slice()).next().and_then(Result::ok))] #[regex(r#"[a-zA-Z_][a-zA-Z\d_]*"#, |lex| Ident::lexer(lex.slice()).next().and_then(Result::ok))]
@ -73,10 +76,18 @@ pub enum Ident {
Structure, Structure,
#[token("Alias")] #[token("Alias")]
Alias, Alias,
#[token("Enumeration")]
Enumeration,
#[token("Use")] #[token("Use")]
Use, Use,
#[token("Make")] #[token("Make")]
Make, Make,
#[token("Takes")]
Takes,
#[token("Returns")]
Returns,
#[token("_")]
Underscore,
#[regex(r"[a-zA-Z_][a-zA-Z\d_]*", |lex| lex.slice().parse().ok())] #[regex(r"[a-zA-Z_][a-zA-Z\d_]*", |lex| lex.slice().parse().ok())]
Other(String), Other(String),
} }

View file

@ -36,9 +36,15 @@ fn main() {
parser::ParserError::UnexpectedEOF => Label::primary( parser::ParserError::UnexpectedEOF => Label::primary(
(), (),
(codespan_file.source().len() - 1)..codespan_file.source().len(), (codespan_file.source().len() - 1)..codespan_file.source().len(),
).with_message("Unexpected end of file here"), )
parser::ParserError::Unexpected(expected, Spanned(got, span)) => Label::primary((), span.0).with_message(format!("Unexpected `{got}`, expected {expected}")), .with_message("Unexpected end of file here"),
parser::ParserError::PleaseStopParsingUse => unreachable!() parser::ParserError::Unexpected(expected, Spanned(got, span)) => {
Label::primary((), span.0)
.with_message(format!("Unexpected `{got}`, expected {expected}"))
}
parser::ParserError::PleaseStopParsingUse => unsafe {
std::hint::unreachable_unchecked()
},
}; };
let diagnostic = codespan_reporting::diagnostic::Diagnostic::error() let diagnostic = codespan_reporting::diagnostic::Diagnostic::error()
.with_message(msg) .with_message(msg)
@ -52,6 +58,8 @@ fn main() {
.unwrap(); .unwrap();
} }
} }
} else {
eprintln!("No file given. Aborting.");
} }
} }

View file

@ -0,0 +1,133 @@
use std::collections::HashMap;
use crate::{
ast::{EnumerationContent, EnumerationVariant, ItemEnumeration, Type},
lexer::{Ident, Spanned, Token},
};
use super::{Parser, ParserError};
impl<'a> Parser<'a> {
pub fn ask_enumeration(&mut self) -> Result<Spanned<ItemEnumeration>, ParserError> {
let Spanned(_, span) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Enumeration)),
"the `Enumeration` keyword",
)?;
let Spanned(Type { name, arguments }, _) = self.ask_type()?;
self.get_real(
|token| matches!(token, Token::LeftCurly),
"an opening curly brace",
)?;
let mut variants = vec![];
loop {
match self.tokens.peek()?.0 {
Token::Ident(Ident::Other(_)) => {
let Spanned(variant_name, _) = self.ask_ident()?;
let mut content = EnumerationContent::None;
loop {
match self.tokens.peek()?.0 {
Token::LeftParen => {
self.eat();
let mut tuple = vec![];
loop {
match self.tokens.peek()?.0 {
Token::Ident(Ident::Other(_)) => {
tuple.push(self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 {
self.eat();
};
}
Token::RightParen => {
self.eat();
break;
}
_ => {
return Err(
self.expected("closing parentheses or a type")
)
}
}
}
content = EnumerationContent::Tuple(tuple);
}
Token::LeftCurly => {
self.eat();
let mut structure = HashMap::<String, Type>::new();
loop {
match self.tokens.peek()?.0 {
Token::Ident(Ident::Other(_)) => {
let Spanned(field_name, _) = self.ask_ident()?;
self.get_real(
|token| matches!(token, Token::Colon),
"a colon",
)?;
structure.insert(field_name, self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 {
self.eat();
};
}
Token::RightCurly => {
self.eat();
break;
}
_ => {
return Err(
self.expected("closing parentheses or a type")
)
}
}
}
content = EnumerationContent::Structure(structure);
}
Token::Equals => {
self.eat();
content = EnumerationContent::Value(self._ask_number_literal()?.0);
}
_ => break,
}
}
if let Spanned(Token::Comma, _) = self.tokens.peek()? {
self.eat();
}
variants.push(EnumerationVariant {
name: variant_name,
content,
});
}
Token::RightCurly => break,
_ => return Err(self.expected("a closing curly brace or a variant")),
}
}
if let Spanned(Token::RightCurly, _) = self.tokens.peek()? {
self.eat();
return Ok(Spanned(
ItemEnumeration {
name,
arguments,
variants,
},
span + self.tokens.span(),
));
};
Err(self.expected("???"))
}
}

View file

@ -17,26 +17,20 @@ impl<'a> Parser<'a> {
} }
Token::Ident(Ident::Make) => { Token::Ident(Ident::Make) => {
self.eat(); self.eat();
self._ask_struct_init()? self._ask_struct_init()?.map(Box::new).map(Expr::Make)
.map(Box::new)
.map(Expr::Make)
} }
_ => return Err(self.expected("an expression")), _ => return Err(self.expected("an expression")),
}) })
} }
fn _ask_literal(&mut self) -> Result<Spanned<Literal>, ParserError> { pub fn _ask_number_literal(&mut self) -> Result<Spanned<NumberLiteral>, ParserError> {
let Spanned(token, mut span) = self.tokens.next()?; match self.tokens.next()? {
Ok(match token { Spanned(Token::NumberLiteral(number), mut span) => {
Token::StringLiteral(string) => Spanned(Literal::String(string), span),
Token::CharLiteral(chr) => Spanned(Literal::Char(chr), span),
Token::NumberLiteral(number) => {
let lit = if let Spanned(Token::NumberSuffix(_), sp) = self.tokens.peek()? { let lit = if let Spanned(Token::NumberSuffix(_), sp) = self.tokens.peek()? {
span += sp; span += sp;
use NumberLiteral::*; use NumberLiteral::*;
Literal::Number( match unwrap_match!(
match unwrap_match!(
self.tokens.next()?, Spanned(Token::NumberSuffix(suffering), _) => suffering) // eat suffix self.tokens.next()?, Spanned(Token::NumberSuffix(suffering), _) => suffering) // eat suffix
{ {
NumberSuffix::Ptr => Ptr(number as usize), NumberSuffix::Ptr => Ptr(number as usize),
@ -49,14 +43,26 @@ impl<'a> Parser<'a> {
NumberSuffix::U64 => U64(number as u64), NumberSuffix::U64 => U64(number as u64),
NumberSuffix::I64 => I64(number), NumberSuffix::I64 => I64(number),
_ => return Err(self.expected("a non-floating number suffix")) _ => return Err(self.expected("a non-floating number suffix"))
}, }
)
} else { } else {
Literal::Number(NumberLiteral::Infer(number)) NumberLiteral::Infer(number)
}; };
Spanned(lit, span) Ok(Spanned(lit, span))
} }
_ => Err(self.expected("a number literal")),
}
}
pub fn _ask_literal(&mut self) -> Result<Spanned<Literal>, ParserError> {
if let Spanned(Token::NumberLiteral(_), _) = self.tokens.peek()? {
return Ok(self._ask_number_literal()?.map(Literal::Number));
};
let Spanned(token, span) = self.tokens.next()?;
Ok(match token {
Token::StringLiteral(string) => Spanned(Literal::String(string), span),
Token::CharLiteral(chr) => Spanned(Literal::Char(chr), span),
_ => return Err(self.expected("a literal")), _ => return Err(self.expected("a literal")),
}) })
} }
@ -90,6 +96,6 @@ impl<'a> Parser<'a> {
return Ok(Spanned(ExprMake { name, params }, nSp + ccSp)); return Ok(Spanned(ExprMake { name, params }, nSp + ccSp));
}; };
Err(self.expected("something")) Err(self.expected("closing curly braces"))
} }
} }

View file

@ -0,0 +1,140 @@
use crate::{
ast::{nothing, Function, ItemInterface},
lexer::{Ident, Span, Spanned, Token},
};
use super::{Parser, ParserError};
impl<'a> Parser<'a> {
pub fn ask_interface(&mut self) -> Result<Spanned<ItemInterface>, ParserError> {
// Interface
let Spanned(_, mut span) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Interface)),
"the `Interface` keyword",
)?;
// InterfaceName
let Spanned(name, _) = self.ask_ident()?;
// {
self.get_real(
|token| matches!(token, Token::LeftCurly),
"opening curly brackets",
)?;
let mut functions = vec![];
loop {
match self.tokens.peek()? {
Spanned(Token::RightCurly, end) => {
self.eat();
span += end;
break;
}
Spanned(Token::Ident(Ident::Function), _) => functions.push(self.ask_function()?.0),
_ => return Err(self.expected("A function or closing curly braces")),
}
}
Ok(Spanned(ItemInterface { name, functions }, span))
}
pub fn ask_function(&mut self) -> Result<Spanned<Function>, ParserError> {
let Spanned(_, bsp) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Function)),
"the `Function` keyword",
)?;
let Spanned(name, _) = self.ask_ident()?;
let Spanned(next, esp) = self.tokens.next()?;
match next {
Token::Ident(Ident::Takes) => {
self.get_real(
|token| matches!(token, Token::LeftParen),
"Opening parentheses",
)?;
let mut takes = vec![];
let mut returns = nothing();
loop {
let Spanned(peeked, _) = self.tokens.peek()?;
match peeked {
Token::Ident(_) => {
takes.push(self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 {
self.eat();
};
}
Token::RightParen => {
self.eat();
break;
}
_ => return Err(self.expected("closing parentheses or a type name")),
}
}
match self.tokens.next()?.0 {
Token::Semicolon => {}
Token::Ident(Ident::Returns) => {
self.get_real(
|token| matches!(token, Token::LeftParen),
"Opening parentheses",
)?;
let Spanned(returns_, _) = self.ask_type()?;
returns = returns_;
self.get_real(
|token| matches!(token, Token::RightParen),
"Closing parentheses",
)?;
self.semi()?;
}
_ => return Err(self.expected("a semicolon or a Returns clause")),
}
Ok(Spanned(
Function {
name,
takes,
returns,
},
bsp + Span(self.tokens.lexer.span()),
))
}
Token::Ident(Ident::Returns) => {
self.get_real(
|token| matches!(token, Token::LeftParen),
"Opening parentheses",
)?;
let Spanned(returns, _) = self.ask_type()?;
self.get_real(
|token| matches!(token, Token::RightParen),
"Closing parentheses",
)?;
Ok(Spanned(
Function {
name,
takes: Vec::new(),
returns,
},
bsp + self.semi()?,
))
}
Token::Semicolon => Ok(Spanned(
Function {
name,
takes: Vec::new(),
returns: nothing(),
},
bsp + esp,
)),
_ => Err(self.expected("a Takes clause, a Returns clause or a semicolon")),
}
}
}

View file

@ -1,11 +1,13 @@
mod enumeration;
mod expr; mod expr;
mod interface; mod interface;
mod structure; mod structure;
mod types;
use logos::{Lexer, Logos}; use logos::{Lexer, Logos};
use crate::{ use crate::{
ast::{IDLModule, Item, ItemAlias, ItemConstant, ItemInterface, ModulePath, UseDecl}, ast::{IDLModule, Item, ItemAlias, ItemConstant, ModulePath, UseDecl},
lexer::{Ident, Span, Spanned, Token}, lexer::{Ident, Span, Spanned, Token},
}; };
use std::iter::Iterator; use std::iter::Iterator;
@ -52,7 +54,11 @@ impl<'a> TokenIterator<'a> {
} }
pub fn current(&self) -> Spanned<String> { pub fn current(&self) -> Spanned<String> {
Spanned(self.lexer.slice().to_owned(), Span(self.lexer.span())) Spanned(self.lexer.slice().to_owned(), self.span())
}
pub fn span(&self) -> Span {
Span(self.lexer.span())
} }
} }
@ -94,11 +100,16 @@ impl<'a> Parser<'a> {
} }
fn ask_ident(&mut self) -> Result<Spanned<String>, ParserError> { fn ask_ident(&mut self) -> Result<Spanned<String>, ParserError> {
Ok(crate::unwrap_match!( Ok(
self.get_real(|token| matches!(token, Token::Ident(Ident::Other(_))), "an identifier")?, match self.get_real(
Spanned(Token::Ident(Ident::Other(ident)), span) => |token| matches!(token, Token::Ident(Ident::Other(_) | Ident::Underscore)),
Spanned(ident, span) "an identifier",
)) )? {
Spanned(Token::Ident(Ident::Other(ident)), span) => Spanned(ident, span),
Spanned(Token::Ident(Ident::Underscore), span) => Spanned("_".to_owned(), span),
_ => unreachable!(),
},
)
} }
fn ask_modpath( fn ask_modpath(
@ -115,18 +126,18 @@ impl<'a> Parser<'a> {
Spanned(Token::Ident(Ident::Other(ident)), span_span) Spanned(Token::Ident(Ident::Other(ident)), span_span)
if !in_path_seg && waiting_next_seg => if !in_path_seg && waiting_next_seg =>
{ {
span = span + span_span; span += span_span;
segments.push(ident); segments.push(ident);
in_path_seg = true; in_path_seg = true;
waiting_next_seg = false; waiting_next_seg = false;
} }
Spanned(Token::Dot, span_span) if in_path_seg && !waiting_next_seg => { Spanned(Token::Dot, span_span) if in_path_seg && !waiting_next_seg => {
span = span + span_span; span += span_span;
waiting_next_seg = true; waiting_next_seg = true;
in_path_seg = false; in_path_seg = false;
} }
v if end(&v.0) && (in_path_seg || !waiting_next_seg) => { v if end(&v.0) && (in_path_seg || !waiting_next_seg) => {
span = span + v.1; span += v.1;
break; break;
} }
_ => return Err(self.expected("a path segment")), _ => return Err(self.expected("a path segment")),
@ -136,22 +147,6 @@ impl<'a> Parser<'a> {
Ok(Spanned(ModulePath { segments }, span)) Ok(Spanned(ModulePath { segments }, span))
} }
fn _ask_interface(&mut self) -> Result<Spanned<ItemInterface>, ParserError> {
let Spanned(_, kSp) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Interface)),
"`Interface`",
)?;
let Spanned(ident, iSp) = self.ask_ident()?;
Ok(Spanned::new(
ItemInterface {
name: ident,
functions: vec![],
},
[kSp, iSp, self.semi()?],
))
}
fn ask_alias(&mut self) -> Result<Spanned<ItemAlias>, ParserError> { fn ask_alias(&mut self) -> Result<Spanned<ItemAlias>, ParserError> {
let Spanned(_, kSp) = self.get_real( let Spanned(_, kSp) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Alias)), |token| matches!(token, Token::Ident(Ident::Alias)),
@ -160,7 +155,7 @@ impl<'a> Parser<'a> {
let Spanned(name, nSp) = self.ask_ident()?; let Spanned(name, nSp) = self.ask_ident()?;
let Spanned(_, eqSp) = self.get_real(|token| matches!(token, Token::Equals), "`=`")?; let Spanned(_, eqSp) = self.get_real(|token| matches!(token, Token::Equals), "`=`")?;
let Spanned(referree, rSp) = self.ask_ident()?; let Spanned(referree, rSp) = self.ask_type()?;
Ok(Spanned::new( Ok(Spanned::new(
ItemAlias { name, referree }, ItemAlias { name, referree },
@ -177,7 +172,10 @@ impl<'a> Parser<'a> {
let Spanned(_, eqSp) = self.get_real(|token| matches!(token, Token::Equals), "`=`")?; let Spanned(_, eqSp) = self.get_real(|token| matches!(token, Token::Equals), "`=`")?;
let Spanned(expr, exprSp) = self.ask_expr()?; let Spanned(expr, exprSp) = self.ask_expr()?;
Ok(Spanned::new(ItemConstant { name, expr }, [kSp, nSp, eqSp, exprSp, self.semi()?])) Ok(Spanned::new(
ItemConstant { name, expr },
[kSp, nSp, eqSp, exprSp, self.semi()?],
))
} }
fn ask_item(&mut self) -> Result<Spanned<Item>, ParserError> { fn ask_item(&mut self) -> Result<Spanned<Item>, ParserError> {
@ -186,10 +184,13 @@ impl<'a> Parser<'a> {
Err(self.expected("a keyword, not just an identifier"))? Err(self.expected("a keyword, not just an identifier"))?
} }
Token::Ident(keyword) => match keyword { Token::Ident(keyword) => match keyword {
//Ident::Interface => self.ask_interface()?.map(Item::Interface), Ident::Interface => self.ask_interface()?.map(Item::Interface),
Ident::Structure => self.ask_structure()?.map(Item::Structure),
Ident::Alias => self.ask_alias()?.map(Item::Alias), Ident::Alias => self.ask_alias()?.map(Item::Alias),
Ident::Constant => self.ask_constant()?.map(Item::Constant), Ident::Constant => self.ask_constant()?.map(Item::Constant),
_ => Err(self.expected("`Alias` or `Constant`"))?, Ident::Function => self.ask_function()?.map(Item::Function),
Ident::Enumeration => self.ask_enumeration()?.map(Item::Enumeration),
_ => Err(self.expected("an item denoting keyword (Interface, Structure, Alias, Constant, Function, Enumeration)"))?,
}, },
_ => Err(self.expected("a keyword"))?, _ => Err(self.expected("a keyword"))?,
}) })

View file

@ -0,0 +1,44 @@
use std::collections::HashMap;
use crate::{lexer::{Ident, Spanned, Token}, ast::{Type, ItemStructure}};
use super::{Parser, ParserError};
impl<'a> Parser<'a> {
pub fn ask_structure(&mut self) -> Result<Spanned<ItemStructure>, ParserError> {
let Spanned(_, span) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Structure)),
"the `Structure` keyword",
)?;
let Spanned(name, _) = self.ask_ident()?;
let Spanned(_, _) = self.get_real(
|token| matches!(token, Token::LeftCurly),
"an opening curly brace (`{`)",
)?;
let mut fields = HashMap::<String, Type>::new();
loop {
match self.tokens.peek()?.0 {
Token::Ident(_) => {
let Spanned(ident, _) = self.ask_ident().unwrap();
self.get_real(|token| matches!(token, Token::Colon), "a colon")?;
let Spanned(value, _) = self.ask_type()?;
fields.insert(ident, value);
if let Token::Comma = self.tokens.peek()?.0 {
self.eat();
};
}
Token::RightCurly => break,
_ => return Err(self.expected("an identifier or a closing curly brace (`}`)")),
}
}
if let Spanned(Token::RightCurly, end) = self.tokens.next()? {
return Ok(Spanned(ItemStructure { name, fields }, span + end));
};
Err(self.expected("closing curly braces"))
}
}

View file

@ -0,0 +1,45 @@
use crate::{
ast::{Type, TypeArguments, INFER_TYPE},
lexer::{Spanned, Token},
};
use super::{Parser, ParserError};
impl<'a> Parser<'a> {
pub fn ask_type(&mut self) -> Result<Spanned<Type>, ParserError> {
let Spanned(name, span) = self.ask_ident()?;
if name == INFER_TYPE {
return Ok(Spanned(Type::infer(), span));
}
let mut arguments = TypeArguments::None;
if let Spanned(crate::lexer::Token::LeftArrow, _) = self.tokens.peek()? {
self.eat(); // eat `<`
let mut args = vec![];
args.push(Box::new(self.ask_type()?.0));
if let Spanned(Token::Comma, _) = self.tokens.peek()? {
self.eat(); // skip comma, allow trailing comma
};
loop {
match self.tokens.peek()? {
Spanned(Token::Ident(_), _) => args.push(Box::new(self.ask_type()?.0)),
Spanned(Token::RightArrow, _) => {
self.eat();
break;
}
_ => return Err(self.expected("closing angle brackets or a type name")),
}
}
arguments = TypeArguments::AngleBracketed(args);
};
Ok(Spanned(Type { name, arguments }, span + self.tokens.span()))
}
}