Improve named type parsing; Clean up
This commit is contained in:
parent
177888c962
commit
936b1f5de9
@ -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 {
|
||||
|
@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)?,
|
||||
};
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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((
|
||||
|
@ -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!()
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user