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};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct Identifier(Arc<String>);
impl Identifier {

View File

@ -1,5 +1,7 @@
use std::fmt::{self, Display, Formatter};
use clap::error::Result;
use crate::{
abstract_tree::Identifier,
context::Context,
@ -22,7 +24,6 @@ pub enum Type {
ListOf(Box<Type>),
ListExact(Vec<Type>),
Map,
Named(Identifier),
None,
Range,
String,
@ -75,6 +76,32 @@ impl Type {
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 {
parameter_types: left_parameters,
@ -89,27 +116,6 @@ impl Type {
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}")
}
Type::Structure { .. } => todo!(),
Type::Named(name) => write!(f, "{name}"),
Type::Structure { name, .. } => write!(f, "{name}"),
}
}
}

View File

@ -1,7 +1,5 @@
use std::{cmp::Ordering, collections::BTreeMap, ops::Range};
use chumsky::container::Container;
use crate::{
context::Context,
error::{RuntimeError, ValidationError},
@ -145,7 +143,7 @@ impl AbstractNode for ValueNode {
{
let types = if let Some(r#type) = context.get_type(name)? {
if let Type::Structure {
name,
name: _,
fields: types,
} = r#type
{
@ -319,8 +317,16 @@ impl Ord for ValueNode {
name: right_name,
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> {
if let Some(value_data) = self.inner.read()?.get(identifier) {
let r#type = match value_data {
ValueData::Type(r#type) => {
if let Type::Named(name) = r#type {
return self.get_type(name);
}
r#type.clone()
}
ValueData::Type(r#type) => r#type.clone(),
ValueData::Value(value) => value.r#type(self)?,
};

View File

@ -13,10 +13,12 @@ pub enum Error {
Parse {
expected: String,
span: (usize, usize),
reason: String,
},
Lex {
expected: String,
span: (usize, usize),
reason: String,
},
Runtime {
error: RuntimeError,
@ -29,11 +31,15 @@ pub enum 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 {
Error::Parse { expected, span } => {
let message = if expected.is_empty() {
"Invalid character.".to_string()
Error::Parse {
expected,
span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid token.".to_string()
} else {
format!("Expected {expected}.")
};
@ -44,31 +50,37 @@ impl Error {
"input",
span.1,
)
.with_message(description)
.with_label(
Label::new(("input", span.0..span.1))
.with_message(message)
.with_message(reason)
.with_color(Color::Red),
),
None,
span.into(),
)
}
Error::Lex { expected, span } => {
let message = if expected.is_empty() {
"Invalid token.".to_string()
Error::Lex {
expected,
span,
reason,
} => {
let description = if expected.is_empty() {
"Invalid character.".to_string()
} else {
format!("Expected {expected}.")
};
(
Report::build(
ReportKind::Custom("Dust Error", Color::White),
ReportKind::Custom("Lexing Error", Color::White),
"input",
span.1,
)
.with_message(description)
.with_label(
Label::new(("input", span.0..span.1))
.with_message(message)
.with_message(reason)
.with_color(Color::Red),
),
None,
@ -188,9 +200,9 @@ impl Error {
builder
.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 {
expected: error.expected().map(|error| error.to_string()).collect(),
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 {
expected: error.expected().map(|error| error.to_string()).collect(),
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::*};
@ -32,6 +32,8 @@ pub fn parser<'src>() -> impl Parser<
extra::Err<Rich<'src, Token<'src>, SimpleSpan>>,
> {
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! {
Token::Identifier(text) => {
@ -61,6 +63,7 @@ pub fn parser<'src>() -> impl Parser<
}
.map_with(|value, state| Expression::Value(value).with_position(state.span()));
let custom_types = _custom_types.clone();
let r#type = recursive(|r#type| {
let function_type = r#type
.clone()
@ -106,13 +109,22 @@ pub fn parser<'src>() -> impl Parser<
just(Token::Keyword("range")).to(Type::Range),
just(Token::Keyword("str")).to(Type::String),
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()));
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 block = positioned_statement
.clone()
@ -454,15 +466,22 @@ pub fn parser<'src>() -> impl Parser<
structure_field_definition
.separated_by(just(Token::Control(Control::Comma)))
.allow_trailing()
.collect()
.collect::<Vec<(Identifier, WithPosition<Type>)>>()
.delimited_by(
just(Token::Control(Control::CurlyOpen)),
just(Token::Control(Control::CurlyClose)),
),
)
.map_with(|(name, fields), state| {
Statement::StructureDefinition(StructureDefinition::new(name, fields))
.with_position(state.span())
.map_with(move |(name, fields), state| {
let definition = StructureDefinition::new(name.clone(), fields.clone());
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((

View File

@ -79,43 +79,7 @@ impl Value {
}
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
let r#type = match self.0.as_ref() {
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)
self.0.r#type(context)
}
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 PartialOrd for ValueInner {
@ -397,7 +403,7 @@ impl BuiltInFunction {
BuiltInFunction::IntParse => {
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();
todo!()

View File

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

View File

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