Improve named type parsing; Clean up

This commit is contained in:
Jeff 2024-03-20 08:36:18 -04:00
parent 177888c962
commit 936b1f5de9
9 changed files with 139 additions and 95 deletions

View File

@ -10,7 +10,7 @@ use crate::{
use super::{AbstractNode, Action, Type}; use super::{AbstractNode, Action, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct Identifier(Arc<String>); pub struct Identifier(Arc<String>);
impl Identifier { impl Identifier {

View File

@ -1,5 +1,7 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use clap::error::Result;
use crate::{ use crate::{
abstract_tree::Identifier, abstract_tree::Identifier,
context::Context, context::Context,
@ -22,7 +24,6 @@ pub enum Type {
ListOf(Box<Type>), ListOf(Box<Type>),
ListExact(Vec<Type>), ListExact(Vec<Type>),
Map, Map,
Named(Identifier),
None, None,
Range, Range,
String, String,
@ -75,6 +76,32 @@ impl Type {
return Ok(()); return Ok(());
} }
(
Type::Structure {
name: left_name,
fields: left_fields,
},
Type::Structure {
name: right_name,
fields: right_fields,
},
) => {
if left_name == right_name {
for ((left_field_name, left_type), (right_field_name, right_type)) in
left_fields.iter().zip(right_fields.iter())
{
if left_field_name != right_field_name || left_type.node != right_type.node
{
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
}
}
return Ok(());
}
}
( (
Type::Function { Type::Function {
parameter_types: left_parameters, parameter_types: left_parameters,
@ -89,27 +116,6 @@ impl Type {
return Ok(()); return Ok(());
} }
} }
(Type::Named(left), Type::Named(right)) => {
if left == right {
return Ok(());
}
}
(
Type::Named(named),
Type::Structure {
name: struct_name, ..
},
)
| (
Type::Structure {
name: struct_name, ..
},
Type::Named(named),
) => {
if named == struct_name {
return Ok(());
}
}
_ => {} _ => {}
} }
@ -172,8 +178,7 @@ impl Display for Type {
write!(f, ") : {return_type}") write!(f, ") : {return_type}")
} }
Type::Structure { .. } => todo!(), Type::Structure { name, .. } => write!(f, "{name}"),
Type::Named(name) => write!(f, "{name}"),
} }
} }
} }

View File

@ -1,7 +1,5 @@
use std::{cmp::Ordering, collections::BTreeMap, ops::Range}; use std::{cmp::Ordering, collections::BTreeMap, ops::Range};
use chumsky::container::Container;
use crate::{ use crate::{
context::Context, context::Context,
error::{RuntimeError, ValidationError}, error::{RuntimeError, ValidationError},
@ -145,7 +143,7 @@ impl AbstractNode for ValueNode {
{ {
let types = if let Some(r#type) = context.get_type(name)? { let types = if let Some(r#type) = context.get_type(name)? {
if let Type::Structure { if let Type::Structure {
name, name: _,
fields: types, fields: types,
} = r#type } = r#type
{ {
@ -319,8 +317,16 @@ impl Ord for ValueNode {
name: right_name, name: right_name,
fields: right_fields, fields: right_fields,
}, },
) => todo!(), ) => {
(Structure { name, fields }, _) => todo!(), let name_cmp = left_name.cmp(right_name);
if name_cmp.is_eq() {
left_fields.cmp(right_fields)
} else {
name_cmp
}
}
(Structure { .. }, _) => Ordering::Greater,
} }
} }
} }

View File

@ -79,13 +79,7 @@ impl Context {
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> { pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
if let Some(value_data) = self.inner.read()?.get(identifier) { if let Some(value_data) = self.inner.read()?.get(identifier) {
let r#type = match value_data { let r#type = match value_data {
ValueData::Type(r#type) => { ValueData::Type(r#type) => r#type.clone(),
if let Type::Named(name) = r#type {
return self.get_type(name);
}
r#type.clone()
}
ValueData::Value(value) => value.r#type(self)?, ValueData::Value(value) => value.r#type(self)?,
}; };

View File

@ -13,10 +13,12 @@ pub enum Error {
Parse { Parse {
expected: String, expected: String,
span: (usize, usize), span: (usize, usize),
reason: String,
}, },
Lex { Lex {
expected: String, expected: String,
span: (usize, usize), span: (usize, usize),
reason: String,
}, },
Runtime { Runtime {
error: RuntimeError, error: RuntimeError,
@ -29,11 +31,15 @@ pub enum Error {
} }
impl Error { impl Error {
pub fn build_report(self, source: &str) -> Vec<u8> { pub fn build_report(self, source: &str) -> Result<Vec<u8>, io::Error> {
let (mut builder, validation_error, error_position) = match self { let (mut builder, validation_error, error_position) = match self {
Error::Parse { expected, span } => { Error::Parse {
let message = if expected.is_empty() { expected,
"Invalid character.".to_string() span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid token.".to_string()
} else { } else {
format!("Expected {expected}.") format!("Expected {expected}.")
}; };
@ -44,31 +50,37 @@ impl Error {
"input", "input",
span.1, span.1,
) )
.with_message(description)
.with_label( .with_label(
Label::new(("input", span.0..span.1)) Label::new(("input", span.0..span.1))
.with_message(message) .with_message(reason)
.with_color(Color::Red), .with_color(Color::Red),
), ),
None, None,
span.into(), span.into(),
) )
} }
Error::Lex { expected, span } => { Error::Lex {
let message = if expected.is_empty() { expected,
"Invalid token.".to_string() span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid character.".to_string()
} else { } else {
format!("Expected {expected}.") format!("Expected {expected}.")
}; };
( (
Report::build( Report::build(
ReportKind::Custom("Dust Error", Color::White), ReportKind::Custom("Lexing Error", Color::White),
"input", "input",
span.1, span.1,
) )
.with_message(description)
.with_label( .with_label(
Label::new(("input", span.0..span.1)) Label::new(("input", span.0..span.1))
.with_message(message) .with_message(reason)
.with_color(Color::Red), .with_color(Color::Red),
), ),
None, None,
@ -188,9 +200,9 @@ impl Error {
builder builder
.finish() .finish()
.write_for_stdout(sources([("input", source)]), &mut output); .write_for_stdout(sources([("input", source)]), &mut output)?;
output Ok(output)
} }
} }
@ -199,6 +211,7 @@ impl From<Rich<'_, char>> for Error {
Error::Lex { Error::Lex {
expected: error.expected().map(|error| error.to_string()).collect(), expected: error.expected().map(|error| error.to_string()).collect(),
span: (error.span().start(), error.span().end()), span: (error.span().start(), error.span().end()),
reason: error.reason().to_string(),
} }
} }
} }
@ -208,6 +221,7 @@ impl<'src> From<Rich<'_, Token<'src>>> for Error {
Error::Parse { Error::Parse {
expected: error.expected().map(|error| error.to_string()).collect(), expected: error.expected().map(|error| error.to_string()).collect(),
span: (error.span().start(), error.span().end()), span: (error.span().start(), error.span().end()),
reason: error.reason().to_string(),
} }
} }
} }

View File

@ -1,4 +1,4 @@
use std::{cell::RefCell, collections::HashMap}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use chumsky::{input::SpannedInput, pratt::*, prelude::*}; use chumsky::{input::SpannedInput, pratt::*, prelude::*};
@ -32,6 +32,8 @@ pub fn parser<'src>() -> impl Parser<
extra::Err<Rich<'src, Token<'src>, SimpleSpan>>, extra::Err<Rich<'src, Token<'src>, SimpleSpan>>,
> { > {
let identifiers: RefCell<HashMap<&str, Identifier>> = RefCell::new(HashMap::new()); let identifiers: RefCell<HashMap<&str, Identifier>> = RefCell::new(HashMap::new());
let _custom_types: Rc<RefCell<HashMap<Identifier, Type>>> =
Rc::new(RefCell::new(HashMap::new()));
let identifier = select! { let identifier = select! {
Token::Identifier(text) => { Token::Identifier(text) => {
@ -61,6 +63,7 @@ pub fn parser<'src>() -> impl Parser<
} }
.map_with(|value, state| Expression::Value(value).with_position(state.span())); .map_with(|value, state| Expression::Value(value).with_position(state.span()));
let custom_types = _custom_types.clone();
let r#type = recursive(|r#type| { let r#type = recursive(|r#type| {
let function_type = r#type let function_type = r#type
.clone() .clone()
@ -106,13 +109,22 @@ pub fn parser<'src>() -> impl Parser<
just(Token::Keyword("range")).to(Type::Range), just(Token::Keyword("range")).to(Type::Range),
just(Token::Keyword("str")).to(Type::String), just(Token::Keyword("str")).to(Type::String),
just(Token::Keyword("list")).to(Type::List), just(Token::Keyword("list")).to(Type::List),
identifier.clone().map(|identifier| Type::Named(identifier)), identifier.clone().try_map(move |identifier, span| {
custom_types
.borrow()
.get(&identifier)
.cloned()
.ok_or_else(|| {
Rich::custom(span, format!("There is no type named {identifier}."))
})
}),
)) ))
}) })
.map_with(|r#type, state| r#type.with_position(state.span())); .map_with(|r#type, state| r#type.with_position(state.span()));
let type_specification = just(Token::Control(Control::Colon)).ignore_then(r#type.clone()); let type_specification = just(Token::Control(Control::Colon)).ignore_then(r#type.clone());
let custom_types = _custom_types.clone();
let positioned_statement = recursive(|positioned_statement| { let positioned_statement = recursive(|positioned_statement| {
let block = positioned_statement let block = positioned_statement
.clone() .clone()
@ -454,15 +466,22 @@ pub fn parser<'src>() -> impl Parser<
structure_field_definition structure_field_definition
.separated_by(just(Token::Control(Control::Comma))) .separated_by(just(Token::Control(Control::Comma)))
.allow_trailing() .allow_trailing()
.collect() .collect::<Vec<(Identifier, WithPosition<Type>)>>()
.delimited_by( .delimited_by(
just(Token::Control(Control::CurlyOpen)), just(Token::Control(Control::CurlyOpen)),
just(Token::Control(Control::CurlyClose)), just(Token::Control(Control::CurlyClose)),
), ),
) )
.map_with(|(name, fields), state| { .map_with(move |(name, fields), state| {
Statement::StructureDefinition(StructureDefinition::new(name, fields)) let definition = StructureDefinition::new(name.clone(), fields.clone());
.with_position(state.span()) let r#type = Type::Structure {
name: name.clone(),
fields,
};
custom_types.as_ref().borrow_mut().insert(name, r#type);
Statement::StructureDefinition(definition).with_position(state.span())
}); });
choice(( choice((

View File

@ -79,43 +79,7 @@ impl Value {
} }
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> { pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
let r#type = match self.0.as_ref() { self.0.r#type(context)
ValueInner::Boolean(_) => Type::Boolean,
ValueInner::Float(_) => Type::Float,
ValueInner::Integer(_) => Type::Integer,
ValueInner::List(values) => {
let mut types = Vec::with_capacity(values.len());
for value in values {
types.push(value.r#type(context)?);
}
Type::ListExact(types)
}
ValueInner::Map(_) => Type::Map,
ValueInner::Range(_) => Type::Range,
ValueInner::String(_) => Type::String,
ValueInner::Function(function) => match function {
Function::Parsed(parsed_function) => Type::Function {
parameter_types: parsed_function
.parameters
.iter()
.map(|(_, r#type)| r#type.node.clone())
.collect(),
return_type: Box::new(parsed_function.return_type.node.clone()),
},
Function::BuiltIn(built_in_function) => built_in_function.r#type(),
},
ValueInner::Structure { name, .. } => {
if let Some(r#type) = context.get_type(name)? {
r#type
} else {
return Err(ValidationError::TypeNotFound(name.clone()));
}
}
};
Ok(r#type)
} }
pub fn as_boolean(&self) -> Option<bool> { pub fn as_boolean(&self) -> Option<bool> {
@ -232,6 +196,48 @@ pub enum ValueInner {
}, },
} }
impl ValueInner {
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
let r#type = match self {
ValueInner::Boolean(_) => Type::Boolean,
ValueInner::Float(_) => Type::Float,
ValueInner::Integer(_) => Type::Integer,
ValueInner::List(values) => {
let mut types = Vec::with_capacity(values.len());
for value in values {
types.push(value.r#type(context)?);
}
Type::ListExact(types)
}
ValueInner::Map(_) => Type::Map,
ValueInner::Range(_) => Type::Range,
ValueInner::String(_) => Type::String,
ValueInner::Function(function) => match function {
Function::Parsed(parsed_function) => Type::Function {
parameter_types: parsed_function
.parameters
.iter()
.map(|(_, r#type)| r#type.node.clone())
.collect(),
return_type: Box::new(parsed_function.return_type.node.clone()),
},
Function::BuiltIn(built_in_function) => built_in_function.r#type(),
},
ValueInner::Structure { name, .. } => {
if let Some(r#type) = context.get_type(name)? {
r#type
} else {
return Err(ValidationError::TypeNotFound(name.clone()));
}
}
};
Ok(r#type)
}
}
impl Eq for ValueInner {} impl Eq for ValueInner {}
impl PartialOrd for ValueInner { impl PartialOrd for ValueInner {
@ -397,7 +403,7 @@ impl BuiltInFunction {
BuiltInFunction::IntParse => { BuiltInFunction::IntParse => {
let string = arguments.get(0).unwrap(); let string = arguments.get(0).unwrap();
if let ValueInner::String(string) = string.inner().as_ref() { if let ValueInner::String(_string) = string.inner().as_ref() {
// let integer = string.parse(); // let integer = string.parse();
todo!() todo!()

View File

@ -87,7 +87,7 @@ pub fn run_shell(context: Context) {
Ok(None) => {} Ok(None) => {}
Err(errors) => { Err(errors) => {
for error in errors { for error in errors {
let report = error.build_report(&buffer); let report = error.build_report(&buffer).unwrap();
stderr().write_all(&report).unwrap(); stderr().write_all(&report).unwrap();
} }

View File

@ -58,7 +58,7 @@ fn main() {
} }
Err(errors) => { Err(errors) => {
for error in errors { for error in errors {
let report = error.build_report(&source); let report = error.build_report(&source).unwrap();
stderr().write_all(&report).unwrap(); stderr().write_all(&report).unwrap();
} }