Overhaul project structure
This commit is contained in:
parent
473f0ee075
commit
cc188a233b
913
Cargo.lock
generated
913
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
|||||||
use std::{
|
|
||||||
sync::{mpsc::channel, Arc},
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use dust_lang::*;
|
|
||||||
|
|
||||||
fn run_fibnacci(interpreter: &Interpreter, i: u8) -> Value {
|
|
||||||
// These double brackets are not Dust syntax, it's just an escape sequence for Rust's format!
|
|
||||||
// macro.
|
|
||||||
let source = Arc::from(format!(
|
|
||||||
"
|
|
||||||
fib = fn (i: int) -> int {{
|
|
||||||
if i <= 1 {{
|
|
||||||
i
|
|
||||||
}} else {{
|
|
||||||
fib(i - 1) + fib(i - 2)
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
|
|
||||||
fib({i})"
|
|
||||||
));
|
|
||||||
|
|
||||||
interpreter
|
|
||||||
.run(Arc::from(i.to_string()), source)
|
|
||||||
.unwrap() // Panic if there are errors.
|
|
||||||
.unwrap() // Panic if the no value is returned.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let interpreter = Interpreter::new();
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
|
|
||||||
for i in 1..10 {
|
|
||||||
let interpreter = interpreter.clone();
|
|
||||||
let tx = tx.clone();
|
|
||||||
|
|
||||||
println!("Spawning thread for fib({})", i);
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
let value = run_fibnacci(&interpreter, i);
|
|
||||||
|
|
||||||
tx.send(value).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give the threads half a second to finish.
|
|
||||||
while let Ok(value) = rx.recv_timeout(Duration::from_millis(500)) {
|
|
||||||
println!("{}", value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
// It's very easy to get nice-looking error messages from the Dust's top-level error type.
|
|
||||||
|
|
||||||
use std::{io::stderr, sync::Arc};
|
|
||||||
|
|
||||||
use ariadne::sources;
|
|
||||||
use dust_lang::Interpreter;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let interpreter = Interpreter::new();
|
|
||||||
|
|
||||||
// First, we'll run some bad code.
|
|
||||||
let error = interpreter
|
|
||||||
.run(
|
|
||||||
Arc::from("bad code"),
|
|
||||||
Arc::from(
|
|
||||||
"
|
|
||||||
x = 1 + 'a'
|
|
||||||
y: float = 'hello'
|
|
||||||
",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
for report in error.build_reports() {
|
|
||||||
report
|
|
||||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
use std::{
|
|
||||||
borrow::Borrow,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type, TypeConstructor};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct As {
|
|
||||||
expression: Expression,
|
|
||||||
constructor: TypeConstructor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl As {
|
|
||||||
pub fn new(expression: Expression, constructor: TypeConstructor) -> Self {
|
|
||||||
Self {
|
|
||||||
expression,
|
|
||||||
constructor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for As {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.expression
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
match self.constructor {
|
|
||||||
TypeConstructor::Raw(_) => {}
|
|
||||||
_ => todo!("Create an error for this occurence"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let expression_position = self.expression.position();
|
|
||||||
let evaluation = self.expression.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(expression_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let r#type = self.constructor.construct(context)?;
|
|
||||||
let (from_value, to_type): (&ValueInner, Type) = (value.inner().borrow(), r#type);
|
|
||||||
|
|
||||||
let converted = match (from_value, to_type) {
|
|
||||||
(_, Type::String) => Value::string(value.to_string()),
|
|
||||||
_ => todo!("Create an error for this occurence"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(Evaluation::Return(converted)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.constructor.construct(context).map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for As {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{} as {}", self.expression, self.constructor)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,231 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
value::ValueInner,
|
|
||||||
Context, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
AbstractNode, Evaluation, SourcePosition, Statement, Type, TypeConstructor, WithPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Assignment {
|
|
||||||
identifier: WithPosition<Identifier>,
|
|
||||||
constructor: Option<TypeConstructor>,
|
|
||||||
operator: AssignmentOperator,
|
|
||||||
statement: Box<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum AssignmentOperator {
|
|
||||||
Assign,
|
|
||||||
AddAssign,
|
|
||||||
SubAssign,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Assignment {
|
|
||||||
pub fn new(
|
|
||||||
identifier: WithPosition<Identifier>,
|
|
||||||
constructor: Option<TypeConstructor>,
|
|
||||||
operator: AssignmentOperator,
|
|
||||||
statement: Statement,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
identifier,
|
|
||||||
constructor,
|
|
||||||
operator,
|
|
||||||
statement: Box::new(statement),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Assignment {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
let r#type = if let Some(constructor) = &self.constructor {
|
|
||||||
constructor.construct(context)?
|
|
||||||
} else if let Some(r#type) = self.statement.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::CannotAssignToNone(
|
|
||||||
self.statement.last_evaluated_statement().position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
context.set_type(self.identifier.node.clone(), r#type, scope)?;
|
|
||||||
self.statement
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
let statement_type = self.statement.expected_type(context)?;
|
|
||||||
|
|
||||||
if statement_type.is_none() {
|
|
||||||
return Err(ValidationError::CannotAssignToNone(
|
|
||||||
self.statement.last_evaluated_statement().position(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(expected_type_constructor), Some(actual_type)) =
|
|
||||||
(&self.constructor, statement_type)
|
|
||||||
{
|
|
||||||
let expected_type = expected_type_constructor.construct(context)?;
|
|
||||||
|
|
||||||
expected_type
|
|
||||||
.check(&actual_type)
|
|
||||||
.map_err(|conflict| ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: self.statement.last_evaluated_statement().position(),
|
|
||||||
expected_position: Some(expected_type_constructor.position()),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let evaluation = self.statement.evaluate(context, manage_memory, scope)?;
|
|
||||||
let right = match evaluation {
|
|
||||||
Some(Evaluation::Return(value)) => value,
|
|
||||||
evaluation => return Ok(evaluation),
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.operator {
|
|
||||||
AssignmentOperator::Assign => {
|
|
||||||
context.set_value(self.identifier.node, right, scope)?;
|
|
||||||
}
|
|
||||||
AssignmentOperator::AddAssign => {
|
|
||||||
let left_option = if manage_memory {
|
|
||||||
context.use_value(&self.identifier.node)?
|
|
||||||
} else {
|
|
||||||
context.get_value(&self.identifier.node)?
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(left) = left_option {
|
|
||||||
let new_value = match (left.inner().as_ref(), right.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Value::integer(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let sum = left + right;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let sum = left + *right as f64;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let sum = *left as f64 + right;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(self.identifier.position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
context.set_value(self.identifier.node, new_value, scope)?;
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::VariableNotFound {
|
|
||||||
identifier: self.identifier.node,
|
|
||||||
position: self.identifier.position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssignmentOperator::SubAssign => {
|
|
||||||
let left_option = if manage_memory {
|
|
||||||
context.use_value(&self.identifier.node)?
|
|
||||||
} else {
|
|
||||||
context.get_value(&self.identifier.node)?
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(left) = left_option {
|
|
||||||
let new_value = match (left.inner().as_ref(), right.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let difference = left.saturating_sub(*right);
|
|
||||||
|
|
||||||
Value::integer(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let difference = left - right;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let difference = left - *right as f64;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let difference = *left as f64 - right;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(self.identifier.position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
context.set_value(self.identifier.node, new_value, scope)?;
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::VariableNotFound {
|
|
||||||
identifier: self.identifier.node,
|
|
||||||
position: self.identifier.position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Assignment {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let Assignment {
|
|
||||||
identifier,
|
|
||||||
constructor,
|
|
||||||
operator,
|
|
||||||
statement,
|
|
||||||
} = self;
|
|
||||||
write!(f, "{} ", identifier.node)?;
|
|
||||||
|
|
||||||
if let Some(constructor) = constructor {
|
|
||||||
write!(f, ": {constructor} ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match operator {
|
|
||||||
AssignmentOperator::Assign => write!(f, "="),
|
|
||||||
AssignmentOperator::AddAssign => write!(f, "+="),
|
|
||||||
AssignmentOperator::SubAssign => write!(f, "-="),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
write!(f, " {statement}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Statement, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct AsyncBlock {
|
|
||||||
statements: Vec<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncBlock {
|
|
||||||
pub fn new(statements: Vec<Statement>) -> Self {
|
|
||||||
Self { statements }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for AsyncBlock {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
for statement in &self.statements {
|
|
||||||
statement.define_and_validate(_context, manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_context: &Context,
|
|
||||||
_: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let final_result = Mutex::new(Ok(None));
|
|
||||||
let statement_count = self.statements.len();
|
|
||||||
let error_option = self.statements.into_par_iter().enumerate().find_map_any(
|
|
||||||
|(index, statement)| -> Option<RuntimeError> {
|
|
||||||
let result = statement.evaluate(_context, false, scope);
|
|
||||||
|
|
||||||
if let Err(error) = result {
|
|
||||||
return Some(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if index == statement_count - 1 {
|
|
||||||
// It is safe to unwrap here because only one thread uses the Mutex
|
|
||||||
*final_result.lock().unwrap() = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(error) = error_option {
|
|
||||||
Err(error)
|
|
||||||
} else {
|
|
||||||
final_result.into_inner()?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.statements.last().unwrap().expected_type(_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AsyncBlock {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "async {{")?;
|
|
||||||
|
|
||||||
for statement in &self.statements {
|
|
||||||
write!(f, "{statement}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Statement, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Block {
|
|
||||||
statements: Vec<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Block {
|
|
||||||
pub fn new(statements: Vec<Statement>) -> Self {
|
|
||||||
Self { statements }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first_statement(&self) -> &Statement {
|
|
||||||
self.statements.first().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_statement(&self) -> &Statement {
|
|
||||||
self.statements.last().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Block {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
for statement in &self.statements {
|
|
||||||
statement.define_and_validate(_context, _manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let mut previous = None;
|
|
||||||
|
|
||||||
for statement in self.statements {
|
|
||||||
previous = statement.evaluate(_context, _manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(previous)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.last_statement().expected_type(_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Block {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{{ ")?;
|
|
||||||
|
|
||||||
for statement in &self.statements {
|
|
||||||
write!(f, "{statement} ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::{Expression, ValueNode, WithPos},
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn run_returns_value_of_final_statement() {
|
|
||||||
let block = Block::new(vec![
|
|
||||||
Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::Integer(1).with_position((0, 0)),
|
|
||||||
)),
|
|
||||||
Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::Integer(2).with_position((0, 0)),
|
|
||||||
)),
|
|
||||||
Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::Integer(42).with_position((0, 0)),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
block
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0))
|
|
||||||
.unwrap(),
|
|
||||||
Some(Evaluation::Return(Value::integer(42)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expected_type_returns_type_of_final_statement() {
|
|
||||||
let block = Block::new(vec![
|
|
||||||
Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::String("42".to_string()).with_position((0, 0)),
|
|
||||||
)),
|
|
||||||
Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::Integer(42).with_position((0, 0)),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
block.expected_type(&Context::new()),
|
|
||||||
Ok(Some(Type::Integer))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,305 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
fs::read_to_string,
|
|
||||||
io::stdin,
|
|
||||||
thread::sleep,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::from_str;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
value::ValueInner,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Type;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum BuiltInFunction {
|
|
||||||
Length,
|
|
||||||
ReadLine,
|
|
||||||
ReadFile,
|
|
||||||
Sleep,
|
|
||||||
WriteLine,
|
|
||||||
JsonParse,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltInFunction {
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::Length => Length::r#type(),
|
|
||||||
BuiltInFunction::ReadLine => ReadLine::r#type(),
|
|
||||||
BuiltInFunction::ReadFile => ReadFile::r#type(),
|
|
||||||
BuiltInFunction::Sleep => Sleep::r#type(),
|
|
||||||
BuiltInFunction::WriteLine => WriteLine::r#type(),
|
|
||||||
BuiltInFunction::JsonParse => JsonParse::r#type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::Length => Length::call(context, manage_memory),
|
|
||||||
BuiltInFunction::ReadLine => ReadLine::call(context, manage_memory),
|
|
||||||
BuiltInFunction::ReadFile => ReadFile::call(context, manage_memory),
|
|
||||||
BuiltInFunction::Sleep => Sleep::call(context, manage_memory),
|
|
||||||
BuiltInFunction::WriteLine => WriteLine::call(context, manage_memory),
|
|
||||||
BuiltInFunction::JsonParse => JsonParse::call(context, manage_memory),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for BuiltInFunction {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let display = match self {
|
|
||||||
BuiltInFunction::Length => "__LENGTH__",
|
|
||||||
BuiltInFunction::ReadLine => "__READ_LINE__",
|
|
||||||
BuiltInFunction::ReadFile => "__READ_FILE__",
|
|
||||||
BuiltInFunction::Sleep => "__SLEEP__",
|
|
||||||
BuiltInFunction::WriteLine => "__WRITE_LINE__",
|
|
||||||
BuiltInFunction::JsonParse => "__JSON_PARSE__",
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{display}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait FunctionLogic {
|
|
||||||
fn r#type() -> Type;
|
|
||||||
fn call(context: &Context, manage_memory: bool) -> Result<Option<Value>, RuntimeError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct Length;
|
|
||||||
|
|
||||||
impl FunctionLogic for Length {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters: Some(vec![(
|
|
||||||
Identifier::from("list"),
|
|
||||||
Type::ListOf(Box::new(Type::Any)),
|
|
||||||
)]),
|
|
||||||
return_type: Some(Box::new(Type::Integer)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(context: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let value = if let Some(value) = context.get_value(&Identifier::from("input"))? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("input does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let list = if let ValueInner::List(list) = value.inner().as_ref() {
|
|
||||||
list
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("list is not a list"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(Value::integer(list.len() as i64)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct ReadFile;
|
|
||||||
|
|
||||||
impl FunctionLogic for ReadFile {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters: Some(vec![(Identifier::from("path"), Type::String)]),
|
|
||||||
return_type: Some(Box::new(Type::String)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(context: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let value = if let Some(value) = context.get_value(&Identifier::from("path"))? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("path does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let path = if let ValueInner::String(string) = value.inner().as_ref() {
|
|
||||||
string
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("path is not a string"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let file_content = read_to_string(path)?;
|
|
||||||
|
|
||||||
Ok(Some(Value::string(file_content)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct ReadLine;
|
|
||||||
|
|
||||||
impl FunctionLogic for ReadLine {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters: None,
|
|
||||||
return_type: Some(Box::new(Type::String)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(_: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let mut user_input = String::new();
|
|
||||||
|
|
||||||
stdin().read_line(&mut user_input)?;
|
|
||||||
|
|
||||||
Ok(Some(Value::string(user_input.trim_end_matches('\n'))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct Sleep;
|
|
||||||
|
|
||||||
impl FunctionLogic for Sleep {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters: Some(vec![(Identifier::from("milliseconds"), Type::Integer)]),
|
|
||||||
return_type: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(context: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let value = if let Some(value) = context.get_value(&Identifier::from("milliseconds"))? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("milliseconds does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let milliseconds = if let ValueInner::Integer(integer) = value.inner().as_ref() {
|
|
||||||
integer
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("milliseconds is not an integer"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(*milliseconds as u64));
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct WriteLine;
|
|
||||||
|
|
||||||
impl FunctionLogic for WriteLine {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters: Some(vec![(Identifier::from("output"), Type::String)]),
|
|
||||||
return_type: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(context: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let value = if let Some(value) = context.get_value(&Identifier::from("output"))? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("output does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let output = if let ValueInner::String(string) = value.inner().as_ref() {
|
|
||||||
string
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("output is not a string"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{output}");
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
struct JsonParse;
|
|
||||||
|
|
||||||
impl FunctionLogic for JsonParse {
|
|
||||||
fn r#type() -> Type {
|
|
||||||
let type_t = Type::Generic {
|
|
||||||
identifier: Identifier::from("T"),
|
|
||||||
concrete_type: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: Some(vec![Identifier::from("T")]),
|
|
||||||
value_parameters: Some(vec![(Identifier::from("input"), type_t.clone())]),
|
|
||||||
return_type: Some(Box::new(type_t)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(context: &Context, _: bool) -> Result<Option<Value>, RuntimeError> {
|
|
||||||
let target_type = if let Some(r#type) = context.get_type(&Identifier::from("T"))? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("T does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let value = if let Some(value) = context.get_value(&Identifier::from("input"))? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("input does not exist"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let input = if let ValueInner::String(string) = value.inner().as_ref() {
|
|
||||||
string
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::BuiltInFunctionFailure("input is not a string"),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
fn parse_value(input: &str, r#type: Type) -> Result<Value, RuntimeError> {
|
|
||||||
let value = match r#type {
|
|
||||||
Type::Any => from_str::<Value>(input)?,
|
|
||||||
Type::Boolean => Value::boolean(from_str::<bool>(input)?),
|
|
||||||
Type::Enum { .. } => todo!(),
|
|
||||||
Type::Float => Value::float(from_str::<f64>(input)?),
|
|
||||||
Type::Function { .. } => todo!(),
|
|
||||||
Type::Generic { concrete_type, .. } => {
|
|
||||||
if let Some(r#type) = concrete_type {
|
|
||||||
parse_value(input, *r#type)?
|
|
||||||
} else {
|
|
||||||
todo!("Create an error for this occurence");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Type::Integer => Value::integer(from_str::<i64>(input)?),
|
|
||||||
Type::List { .. } => todo!(),
|
|
||||||
Type::ListOf(_) => todo!(),
|
|
||||||
Type::Map(_) => todo!(),
|
|
||||||
Type::Range => todo!(),
|
|
||||||
Type::String => Value::string(from_str::<String>(input)?),
|
|
||||||
Type::Structure { .. } => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_value(input, target_type).map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Type, TypeConstructor, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct EnumDeclaration {
|
|
||||||
name: WithPosition<Identifier>,
|
|
||||||
type_parameters: Option<Vec<WithPosition<Identifier>>>,
|
|
||||||
variants: Vec<EnumVariant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EnumDeclaration {
|
|
||||||
pub fn new(
|
|
||||||
name: WithPosition<Identifier>,
|
|
||||||
type_parameters: Option<Vec<WithPosition<Identifier>>>,
|
|
||||||
variants: Vec<EnumVariant>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
type_parameters,
|
|
||||||
variants,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for EnumDeclaration {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_: bool,
|
|
||||||
_scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
let EnumDeclaration {
|
|
||||||
name,
|
|
||||||
type_parameters,
|
|
||||||
variants,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let type_parameters = type_parameters.as_ref().map(|parameters| {
|
|
||||||
parameters
|
|
||||||
.iter()
|
|
||||||
.map(|identifier| Type::Generic {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
concrete_type: None,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
let mut type_variants = Vec::with_capacity(variants.len());
|
|
||||||
|
|
||||||
for EnumVariant { name, content } in variants {
|
|
||||||
let types = if let Some(content) = content {
|
|
||||||
let mut types = Vec::with_capacity(content.len());
|
|
||||||
|
|
||||||
for constructor in content {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
types.push(r#type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(types)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
type_variants.push((name.node.clone(), types));
|
|
||||||
}
|
|
||||||
|
|
||||||
let r#type = Type::Enum {
|
|
||||||
name: name.node.clone(),
|
|
||||||
type_parameters,
|
|
||||||
variants: type_variants,
|
|
||||||
};
|
|
||||||
let final_node_position = if let Some(constructors) = &self.variants.last().unwrap().content
|
|
||||||
{
|
|
||||||
constructors.last().unwrap().position()
|
|
||||||
} else {
|
|
||||||
self.variants.last().unwrap().name.position
|
|
||||||
};
|
|
||||||
let scope = SourcePosition(self.name.position.0, final_node_position.1);
|
|
||||||
|
|
||||||
context.set_type(name.node.clone(), r#type, scope)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_: &Context,
|
|
||||||
_: bool,
|
|
||||||
_scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for EnumDeclaration {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let EnumDeclaration {
|
|
||||||
name,
|
|
||||||
type_parameters,
|
|
||||||
variants,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "enum {}", name.node)?;
|
|
||||||
|
|
||||||
if let Some(parameters) = type_parameters {
|
|
||||||
write!(f, "<")?;
|
|
||||||
for WithPosition { node, .. } in parameters {
|
|
||||||
write!(f, "{node}, ")?;
|
|
||||||
}
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for EnumVariant { name, content } in variants {
|
|
||||||
write!(f, "{}", name.node)?;
|
|
||||||
|
|
||||||
if let Some(content) = content {
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
for constructor in content {
|
|
||||||
write!(f, "{constructor}, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct EnumVariant {
|
|
||||||
pub name: WithPosition<Identifier>,
|
|
||||||
pub content: Option<Vec<TypeConstructor>>,
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
AbstractNode, As, Evaluation, FunctionCall, ListIndex, Logic, MapIndex, Math, SourcePosition,
|
|
||||||
Type, ValueNode, WithPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Expression {
|
|
||||||
As(WithPosition<Box<As>>),
|
|
||||||
FunctionCall(WithPosition<FunctionCall>),
|
|
||||||
Identifier(WithPosition<Identifier>),
|
|
||||||
MapIndex(WithPosition<Box<MapIndex>>),
|
|
||||||
ListIndex(WithPosition<Box<ListIndex>>),
|
|
||||||
Logic(WithPosition<Box<Logic>>),
|
|
||||||
Math(WithPosition<Box<Math>>),
|
|
||||||
Value(WithPosition<ValueNode>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Expression {
|
|
||||||
pub fn position(&self) -> SourcePosition {
|
|
||||||
match self {
|
|
||||||
Expression::As(inner) => inner.position,
|
|
||||||
Expression::FunctionCall(inner) => inner.position,
|
|
||||||
Expression::Identifier(inner) => inner.position,
|
|
||||||
Expression::MapIndex(inner) => inner.position,
|
|
||||||
Expression::ListIndex(inner) => inner.position,
|
|
||||||
Expression::Logic(inner) => inner.position,
|
|
||||||
Expression::Math(inner) => inner.position,
|
|
||||||
Expression::Value(inner) => inner.position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Expression {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
match self {
|
|
||||||
Expression::As(r#as) => r#as.node.define_and_validate(context, manage_memory, scope),
|
|
||||||
Expression::FunctionCall(function_call) => {
|
|
||||||
function_call
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
let found = context.add_expected_use(&identifier.node)?;
|
|
||||||
|
|
||||||
if found {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::VariableNotFound {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
position: identifier.position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::MapIndex(map_index) => {
|
|
||||||
map_index
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::ListIndex(list_index) => {
|
|
||||||
list_index
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::Logic(logic) => {
|
|
||||||
logic
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::Math(math) => math.node.define_and_validate(context, manage_memory, scope),
|
|
||||||
Expression::Value(value_node) => {
|
|
||||||
value_node
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
match self {
|
|
||||||
Expression::As(r#as) => r#as.node.evaluate(context, manage_memory, scope),
|
|
||||||
Expression::FunctionCall(function_call) => {
|
|
||||||
function_call.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
let value_option = if manage_memory {
|
|
||||||
context.use_value(&identifier.node)?
|
|
||||||
} else {
|
|
||||||
context.get_value(&identifier.node)?
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(value) = value_option {
|
|
||||||
Ok(Some(Evaluation::Return(value)))
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::VariableNotFound {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
position: identifier.position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::MapIndex(map_index) => {
|
|
||||||
map_index.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::ListIndex(list_index) => {
|
|
||||||
list_index.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Expression::Logic(logic) => logic.node.evaluate(context, manage_memory, scope),
|
|
||||||
Expression::Math(math) => math.node.evaluate(context, manage_memory, scope),
|
|
||||||
Expression::Value(value_node) => {
|
|
||||||
value_node.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
match self {
|
|
||||||
Expression::As(r#as) => r#as.node.expected_type(_context),
|
|
||||||
Expression::FunctionCall(function_call) => function_call.node.expected_type(_context),
|
|
||||||
Expression::Identifier(identifier) => {
|
|
||||||
let get_type = _context.get_type(&identifier.node)?;
|
|
||||||
|
|
||||||
if get_type.is_none() {
|
|
||||||
Err(ValidationError::VariableNotFound {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
position: identifier.position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(get_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::MapIndex(map_index) => map_index.node.expected_type(_context),
|
|
||||||
Expression::ListIndex(list_index) => list_index.node.expected_type(_context),
|
|
||||||
Expression::Logic(logic) => logic.node.expected_type(_context),
|
|
||||||
Expression::Math(math) => math.node.expected_type(_context),
|
|
||||||
Expression::Value(value_node) => value_node.node.expected_type(_context),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Expression {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Expression::As(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::FunctionCall(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::Identifier(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::MapIndex(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::ListIndex(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::Logic(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::Math(inner) => write!(f, "{}", inner.node),
|
|
||||||
Expression::Value(inner) => write!(f, "{}", inner.node),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Block, Evaluation, Expression, Statement, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct For {
|
|
||||||
identifier: Identifier,
|
|
||||||
expression: Expression,
|
|
||||||
block: Block,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
context: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl For {
|
|
||||||
pub fn new(identifier: Identifier, expression: Expression, block: Block) -> Self {
|
|
||||||
Self {
|
|
||||||
identifier,
|
|
||||||
expression,
|
|
||||||
block,
|
|
||||||
context: Context::new(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for For {
|
|
||||||
fn define_types(&self, context: &Context) -> Result<(), ValidationError> {
|
|
||||||
self.context.set_parent(context.clone())?;
|
|
||||||
self.expression.define_types(context)?;
|
|
||||||
|
|
||||||
let collection_type =
|
|
||||||
self.expression
|
|
||||||
.expected_type(context)?
|
|
||||||
.ok_or(ValidationError::ExpectedExpression(
|
|
||||||
self.expression.position(),
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let item_type = if let Type::Range = collection_type {
|
|
||||||
Type::Integer
|
|
||||||
} else {
|
|
||||||
todo!("Create an error for this occurence");
|
|
||||||
};
|
|
||||||
|
|
||||||
self.context.set_type(self.identifier.clone(), item_type)?;
|
|
||||||
|
|
||||||
for statement in self.block.statements() {
|
|
||||||
statement.define_types(&self.context)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(&self, context: &Context, manage_memory: bool) -> Result<(), ValidationError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(
|
|
||||||
&self,
|
|
||||||
context: &crate::context::Context,
|
|
||||||
) -> Result<Option<super::Type>, ValidationError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for For {}
|
|
||||||
|
|
||||||
impl PartialEq for For {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.identifier == other.identifier
|
|
||||||
&& self.expression == other.expression
|
|
||||||
&& self.block == other.block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for For {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for For {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,381 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type, TypeConstructor};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct FunctionCall {
|
|
||||||
function_expression: Box<Expression>,
|
|
||||||
type_arguments: Option<Vec<TypeConstructor>>,
|
|
||||||
value_arguments: Option<Vec<Expression>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FunctionCall {
|
|
||||||
pub fn new(
|
|
||||||
function_expression: Expression,
|
|
||||||
type_arguments: Option<Vec<TypeConstructor>>,
|
|
||||||
value_arguments: Option<Vec<Expression>>,
|
|
||||||
) -> Self {
|
|
||||||
FunctionCall {
|
|
||||||
function_expression: Box::new(function_expression),
|
|
||||||
type_arguments,
|
|
||||||
value_arguments,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for FunctionCall {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.function_expression
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
if let Some(value_arguments) = &self.value_arguments {
|
|
||||||
for expression in value_arguments {
|
|
||||||
expression.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let function_node_type =
|
|
||||||
if let Some(r#type) = self.function_expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.function_expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type: _,
|
|
||||||
} = function_node_type
|
|
||||||
{
|
|
||||||
match (type_parameters, &self.type_arguments) {
|
|
||||||
(Some(parameters), Some(arguments)) => {
|
|
||||||
if parameters.len() != arguments.len() {
|
|
||||||
return Err(ValidationError::WrongTypeArguments {
|
|
||||||
arguments: arguments.clone(),
|
|
||||||
parameters: parameters.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (identifier, constructor) in parameters.into_iter().zip(arguments.iter()) {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
context.set_type(
|
|
||||||
identifier,
|
|
||||||
r#type,
|
|
||||||
self.function_expression.position(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(parameters), None) => {
|
|
||||||
return Err(ValidationError::WrongTypeArguments {
|
|
||||||
arguments: Vec::with_capacity(0),
|
|
||||||
parameters: parameters.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(None, Some(arguments)) => {
|
|
||||||
return Err(ValidationError::WrongTypeArguments {
|
|
||||||
arguments: arguments.clone(),
|
|
||||||
parameters: Vec::with_capacity(0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match (value_parameters, &self.value_arguments) {
|
|
||||||
(Some(parameters), Some(arguments)) => {
|
|
||||||
for ((identifier, _), expression) in parameters.iter().zip(arguments.iter()) {
|
|
||||||
let r#type = if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
context.set_type(
|
|
||||||
identifier.clone(),
|
|
||||||
r#type,
|
|
||||||
self.function_expression.position(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if parameters.len() != arguments.len() {
|
|
||||||
return Err(ValidationError::WrongValueArguments {
|
|
||||||
parameters,
|
|
||||||
arguments: arguments.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(parameters), None) => {
|
|
||||||
return Err(ValidationError::WrongValueArguments {
|
|
||||||
parameters,
|
|
||||||
arguments: Vec::with_capacity(0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(None, Some(arguments)) => {
|
|
||||||
return Err(ValidationError::WrongValueArguments {
|
|
||||||
parameters: Vec::with_capacity(0),
|
|
||||||
arguments: arguments.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(ValidationError::ExpectedFunction {
|
|
||||||
actual: function_node_type,
|
|
||||||
position: self.function_expression.position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let function_position = self.function_expression.position();
|
|
||||||
let evaluation = self
|
|
||||||
.function_expression
|
|
||||||
.evaluate(context, manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(function_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ValueInner::Function(function) = value.inner().as_ref() {
|
|
||||||
let type_arguments = if let Some(type_arguments) = self.type_arguments {
|
|
||||||
let mut types = Vec::with_capacity(type_arguments.len());
|
|
||||||
|
|
||||||
for constructor in type_arguments {
|
|
||||||
types.push(constructor.construct(context)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(types)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let value_arguments = if let Some(value_arguments) = self.value_arguments {
|
|
||||||
let mut values = Vec::with_capacity(value_arguments.len());
|
|
||||||
|
|
||||||
for expression in value_arguments {
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = (expression.evaluate(context, manage_memory, scope)?).ok_or(
|
|
||||||
RuntimeError::ValidationFailure(ValidationError::ExpectedValueStatement(
|
|
||||||
position,
|
|
||||||
)),
|
|
||||||
)?;
|
|
||||||
let value = if let Evaluation::Return(value) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(values)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
return function
|
|
||||||
.clone()
|
|
||||||
.call(Some(context), type_arguments, value_arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ValueInner::BuiltInFunction(function) = value.inner().as_ref() {
|
|
||||||
let (type_parameters, value_parameters, _) = if let Type::Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
} = function.r#type()
|
|
||||||
{
|
|
||||||
(type_parameters, value_parameters, return_type)
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedFunction {
|
|
||||||
actual: function.r#type(),
|
|
||||||
position: function_position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (Some(type_parameters), Some(type_arguments)) =
|
|
||||||
(type_parameters, self.type_arguments)
|
|
||||||
{
|
|
||||||
for (identifier, constructor) in
|
|
||||||
type_parameters.into_iter().zip(type_arguments.into_iter())
|
|
||||||
{
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
context.set_type(identifier.clone(), r#type, function_position)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(parameters), Some(arguments)) = (value_parameters, self.value_arguments) {
|
|
||||||
for ((identifier, _), expression) in
|
|
||||||
parameters.into_iter().zip(arguments.into_iter())
|
|
||||||
{
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
context.set_value(identifier.clone(), value, function_position)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function
|
|
||||||
.call(context, manage_memory)
|
|
||||||
.map(|option| option.map(Evaluation::Return));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedFunction {
|
|
||||||
actual: value.r#type(context)?,
|
|
||||||
position: function_position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
let expression_type = self.function_expression.expected_type(context)?.ok_or(
|
|
||||||
ValidationError::ExpectedValueStatement(self.function_expression.position()),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (type_parameters, return_type) = if let Type::Function {
|
|
||||||
type_parameters,
|
|
||||||
return_type,
|
|
||||||
..
|
|
||||||
} = expression_type
|
|
||||||
{
|
|
||||||
(type_parameters, return_type)
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedFunction {
|
|
||||||
actual: expression_type,
|
|
||||||
position: self.function_expression.position(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(Type::Generic {
|
|
||||||
identifier: return_identifier,
|
|
||||||
concrete_type: None,
|
|
||||||
}) = return_type.clone().map(|r#box| *r#box)
|
|
||||||
{
|
|
||||||
if let (Some(parameters), Some(arguments)) = (type_parameters, &self.type_arguments) {
|
|
||||||
for (identifier, constructor) in parameters.into_iter().zip(arguments.iter()) {
|
|
||||||
if identifier == return_identifier {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
return Ok(Some(Type::Generic {
|
|
||||||
identifier,
|
|
||||||
concrete_type: Some(Box::new(r#type)),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(return_type.map(|r#box| *r#box))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FunctionCall {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let FunctionCall {
|
|
||||||
function_expression,
|
|
||||||
type_arguments,
|
|
||||||
value_arguments,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "{function_expression}")?;
|
|
||||||
|
|
||||||
if let Some(type_arguments) = type_arguments {
|
|
||||||
write!(f, "::<")?;
|
|
||||||
|
|
||||||
for constructor in type_arguments {
|
|
||||||
write!(f, "{constructor}, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
if let Some(value_arguments) = value_arguments {
|
|
||||||
for expression in value_arguments {
|
|
||||||
write!(f, "{expression}, ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for FunctionCall {}
|
|
||||||
|
|
||||||
impl PartialEq for FunctionCall {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.function_expression == other.function_expression
|
|
||||||
&& self.type_arguments == other.type_arguments
|
|
||||||
&& self.value_arguments == other.value_arguments
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for FunctionCall {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for FunctionCall {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
let expression_cmp = self.function_expression.cmp(&other.function_expression);
|
|
||||||
|
|
||||||
if expression_cmp.is_eq() {
|
|
||||||
let type_arg_cmp = self.type_arguments.cmp(&other.type_arguments);
|
|
||||||
|
|
||||||
if type_arg_cmp.is_eq() {
|
|
||||||
self.value_arguments.cmp(&other.value_arguments)
|
|
||||||
} else {
|
|
||||||
type_arg_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expression_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,231 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Block, Evaluation, Expression, SourcePosition, Type, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct IfElse {
|
|
||||||
if_expression: Expression,
|
|
||||||
if_block: WithPosition<Block>,
|
|
||||||
else_ifs: Option<Vec<(Expression, WithPosition<Block>)>>,
|
|
||||||
else_block: Option<WithPosition<Block>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IfElse {
|
|
||||||
pub fn new(
|
|
||||||
if_expression: Expression,
|
|
||||||
if_block: WithPosition<Block>,
|
|
||||||
else_ifs: Option<Vec<(Expression, WithPosition<Block>)>>,
|
|
||||||
else_block: Option<WithPosition<Block>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
if_expression,
|
|
||||||
if_block,
|
|
||||||
else_ifs,
|
|
||||||
else_block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for IfElse {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.if_expression
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
self.if_block
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
let if_expression_type = if let Some(r#type) = self.if_expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.if_expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let if_block_type = self.if_block.node.expected_type(context)?;
|
|
||||||
|
|
||||||
if let Some(else_ifs) = &self.else_ifs {
|
|
||||||
for (expression, block) in else_ifs {
|
|
||||||
let expression_type = expression.expected_type(context)?;
|
|
||||||
|
|
||||||
if let Some(Type::Boolean) = expression_type {
|
|
||||||
block
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
let else_if_block_type = block.node.expected_type(context)?;
|
|
||||||
|
|
||||||
if let (Some(expected), Some(actual)) = (&if_block_type, else_if_block_type) {
|
|
||||||
expected
|
|
||||||
.check(&actual)
|
|
||||||
.map_err(|conflict| ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: self.if_block.node.last_statement().position(),
|
|
||||||
|
|
||||||
expected_position: Some(self.if_expression.position()),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedBoolean {
|
|
||||||
actual: if_expression_type,
|
|
||||||
position: self.if_expression.position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(block) = &self.else_block {
|
|
||||||
block
|
|
||||||
.node
|
|
||||||
.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
let else_if_block_type = block.node.expected_type(context)?;
|
|
||||||
|
|
||||||
if let (Some(expected), Some(actual)) = (if_block_type, else_if_block_type) {
|
|
||||||
expected
|
|
||||||
.check(&actual)
|
|
||||||
.map_err(|conflict| ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: self.if_block.node.last_statement().position(),
|
|
||||||
expected_position: Some(self.if_expression.position()),
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let if_position = self.if_expression.position();
|
|
||||||
let evaluation = self
|
|
||||||
.if_expression
|
|
||||||
.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(if_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ValueInner::Boolean(if_boolean) = value.inner().as_ref() {
|
|
||||||
if *if_boolean {
|
|
||||||
return self.if_block.node.evaluate(context, _manage_memory, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(else_ifs) = self.else_ifs {
|
|
||||||
for (expression, block) in else_ifs {
|
|
||||||
let expression_position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(expression_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ValueInner::Boolean(else_if_boolean) = value.inner().as_ref() {
|
|
||||||
if *else_if_boolean {
|
|
||||||
return block.node.evaluate(context, _manage_memory, scope);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedBoolean {
|
|
||||||
actual: value.r#type(context)?,
|
|
||||||
position: expression_position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(else_statement) = self.else_block {
|
|
||||||
else_statement.node.evaluate(context, _manage_memory, scope)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedBoolean {
|
|
||||||
actual: value.r#type(context)?,
|
|
||||||
position: if_position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.if_block.node.expected_type(_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for IfElse {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let IfElse {
|
|
||||||
if_expression,
|
|
||||||
if_block,
|
|
||||||
else_ifs,
|
|
||||||
else_block,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "if {if_expression} {}", if_block.node)?;
|
|
||||||
|
|
||||||
if let Some(else_ifs) = else_ifs {
|
|
||||||
for (expression, block) in else_ifs {
|
|
||||||
write!(f, "else if {expression} {}", block.node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(else_block) = else_block {
|
|
||||||
write!(f, "{}", else_block.node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::{Statement, ValueNode, WithPos},
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_if() {
|
|
||||||
assert_eq!(
|
|
||||||
IfElse::new(
|
|
||||||
Expression::Value(ValueNode::Boolean(true).with_position((0, 0))),
|
|
||||||
Block::new(vec![Statement::Expression(Expression::Value(
|
|
||||||
ValueNode::String("foo".to_string()).with_position((0, 0))
|
|
||||||
))])
|
|
||||||
.with_position((0, 0)),
|
|
||||||
Some(Vec::with_capacity(0)),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0))
|
|
||||||
.unwrap(),
|
|
||||||
Some(Evaluation::Return(Value::string("foo".to_string())))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type, ValueNode, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct ListIndex {
|
|
||||||
collection: Expression,
|
|
||||||
index: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListIndex {
|
|
||||||
pub fn new(left: Expression, right: Expression) -> Self {
|
|
||||||
Self {
|
|
||||||
collection: left,
|
|
||||||
index: right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for ListIndex {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.collection
|
|
||||||
.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
self.index
|
|
||||||
.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let collection_type = if let Some(r#type) = self.collection.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.collection.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let index_type = if let Some(r#type) = self.index.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.index.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
match collection_type {
|
|
||||||
Type::List {
|
|
||||||
length: _,
|
|
||||||
item_type: _,
|
|
||||||
} => {
|
|
||||||
if index_type == Type::Integer {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::CannotIndexWith {
|
|
||||||
collection_type,
|
|
||||||
collection_position: self.collection.position(),
|
|
||||||
index_type,
|
|
||||||
index_position: self.index.position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Type::ListOf(_) => todo!(),
|
|
||||||
_ => Err(ValidationError::CannotIndex {
|
|
||||||
r#type: collection_type,
|
|
||||||
position: self.collection.position(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_clear_variables: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let left_position = self.collection.position();
|
|
||||||
let left_evaluation = self.collection.evaluate(context, _clear_variables, scope)?;
|
|
||||||
let left_value = if let Some(Evaluation::Return(value)) = left_evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(left_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let right_position = self.index.position();
|
|
||||||
let right_evaluation = self.index.evaluate(context, _clear_variables, scope)?;
|
|
||||||
let right_value = if let Some(Evaluation::Return(value)) = right_evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(right_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) {
|
|
||||||
let found_item = list.get(index as usize);
|
|
||||||
|
|
||||||
if let Some(item) = found_item {
|
|
||||||
Ok(Some(Evaluation::Return(item.clone())))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::CannotIndexWith {
|
|
||||||
collection_type: left_value.r#type(context)?,
|
|
||||||
collection_position: left_position,
|
|
||||||
index_type: right_value.r#type(context)?,
|
|
||||||
index_position: right_position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
let left_type = if let Some(r#type) = self.collection.expected_type(_context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.collection.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (
|
|
||||||
Expression::Value(WithPosition {
|
|
||||||
node: ValueNode::List(expression_list),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Expression::Value(WithPosition {
|
|
||||||
node: ValueNode::Integer(index),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) = (&self.collection, &self.index)
|
|
||||||
{
|
|
||||||
let expression = if let Some(expression) = expression_list.get(*index as usize) {
|
|
||||||
expression
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
expression.expected_type(_context)
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::CannotIndex {
|
|
||||||
r#type: left_type,
|
|
||||||
position: self.collection.position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ListIndex {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let ListIndex { collection, index } = self;
|
|
||||||
|
|
||||||
write!(f, "{collection}[{index}]")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,378 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Logic {
|
|
||||||
Equal(Expression, Expression),
|
|
||||||
NotEqual(Expression, Expression),
|
|
||||||
Greater(Expression, Expression),
|
|
||||||
Less(Expression, Expression),
|
|
||||||
GreaterOrEqual(Expression, Expression),
|
|
||||||
LessOrEqual(Expression, Expression),
|
|
||||||
And(Expression, Expression),
|
|
||||||
Or(Expression, Expression),
|
|
||||||
Not(Expression),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Logic {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
match self {
|
|
||||||
Logic::Equal(left, right)
|
|
||||||
| Logic::NotEqual(left, right)
|
|
||||||
| Logic::Greater(left, right)
|
|
||||||
| Logic::Less(left, right)
|
|
||||||
| Logic::GreaterOrEqual(left, right)
|
|
||||||
| Logic::LessOrEqual(left, right) => {
|
|
||||||
left.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
right.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let left_type = if let Some(r#type) = left.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(left.position()));
|
|
||||||
};
|
|
||||||
let right_type = if let Some(r#type) = right.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(right.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
left_type
|
|
||||||
.check(&right_type)
|
|
||||||
.map_err(|conflict| ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: left.position(),
|
|
||||||
expected_position: Some(right.position()),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Logic::And(left, right) | Logic::Or(left, right) => {
|
|
||||||
left.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
right.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let left_type = if let Some(r#type) = left.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(left.position()));
|
|
||||||
};
|
|
||||||
let right_type = if let Some(r#type) = right.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(right.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Boolean = left_type {
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedBoolean {
|
|
||||||
actual: left_type,
|
|
||||||
position: left.position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Type::Boolean = right_type {
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedBoolean {
|
|
||||||
actual: right_type,
|
|
||||||
position: right.position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Logic::Not(expression) => {
|
|
||||||
expression.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let expression_type = if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Boolean = expression_type {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::ExpectedBoolean {
|
|
||||||
actual: expression_type,
|
|
||||||
position: expression.position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let run_and_expect_value = |expression: Expression| -> Result<Value, RuntimeError> {
|
|
||||||
let expression_position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(expression_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
};
|
|
||||||
|
|
||||||
let run_and_expect_boolean = |expression: Expression| -> Result<bool, RuntimeError> {
|
|
||||||
let expression_position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(expression_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ValueInner::Boolean(boolean) = value.inner().as_ref() {
|
|
||||||
Ok(*boolean)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedBoolean {
|
|
||||||
actual: value.r#type(context)?,
|
|
||||||
position: expression_position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let boolean = match self {
|
|
||||||
Logic::Equal(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value == right_value
|
|
||||||
}
|
|
||||||
Logic::NotEqual(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value != right_value
|
|
||||||
}
|
|
||||||
Logic::Greater(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value > right_value
|
|
||||||
}
|
|
||||||
Logic::Less(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value < right_value
|
|
||||||
}
|
|
||||||
Logic::GreaterOrEqual(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value >= right_value
|
|
||||||
}
|
|
||||||
Logic::LessOrEqual(left, right) => {
|
|
||||||
let (left_value, right_value) =
|
|
||||||
(run_and_expect_value(left)?, run_and_expect_value(right)?);
|
|
||||||
|
|
||||||
left_value <= right_value
|
|
||||||
}
|
|
||||||
Logic::And(left, right) => {
|
|
||||||
let (left_boolean, right_boolean) = (
|
|
||||||
run_and_expect_boolean(left)?,
|
|
||||||
run_and_expect_boolean(right)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
left_boolean && right_boolean
|
|
||||||
}
|
|
||||||
Logic::Or(left, right) => {
|
|
||||||
let (left_boolean, right_boolean) = (
|
|
||||||
run_and_expect_boolean(left)?,
|
|
||||||
run_and_expect_boolean(right)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
left_boolean || right_boolean
|
|
||||||
}
|
|
||||||
Logic::Not(expression) => {
|
|
||||||
let boolean = run_and_expect_boolean(expression)?;
|
|
||||||
|
|
||||||
!boolean
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(boolean))))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
Ok(Some(Type::Boolean))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Logic {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Logic::Equal(left, right) => write!(f, "{left} == {right}"),
|
|
||||||
Logic::NotEqual(left, right) => write!(f, "{left} != {right}"),
|
|
||||||
Logic::Greater(left, right) => write!(f, "{left} > {right}"),
|
|
||||||
Logic::Less(left, right) => write!(f, "{left} < {right}"),
|
|
||||||
Logic::GreaterOrEqual(left, right) => write!(f, "{left} >= {right}"),
|
|
||||||
Logic::LessOrEqual(left, right) => write!(f, "{left} <= {right}"),
|
|
||||||
Logic::And(left, right) => write!(f, "{left} && {right}"),
|
|
||||||
Logic::Or(left, right) => write!(f, "{left} || {right}"),
|
|
||||||
Logic::Not(expression) => write!(f, "!{expression}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::abstract_tree::{ValueNode, WithPos};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn equal() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::Equal(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0)))
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn not_equal() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::NotEqual(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(43).with_position((0, 0)))
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn greater() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::Greater(
|
|
||||||
Expression::Value(ValueNode::Integer(43).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0)))
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn less() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::Less(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(43).with_position((0, 0)))
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn greater_or_equal() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::GreaterOrEqual(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(41).with_position((0, 0)))
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Logic::GreaterOrEqual(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn less_or_equal() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::LessOrEqual(
|
|
||||||
Expression::Value(ValueNode::Integer(41).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Logic::LessOrEqual(
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Integer(42).with_position((0, 0))),
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn and() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::And(
|
|
||||||
Expression::Value(ValueNode::Boolean(true).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Boolean(true).with_position((0, 0))),
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn or() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::Or(
|
|
||||||
Expression::Value(ValueNode::Boolean(true).with_position((0, 0))),
|
|
||||||
Expression::Value(ValueNode::Boolean(false).with_position((0, 0))),
|
|
||||||
)
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn not() {
|
|
||||||
assert_eq!(
|
|
||||||
Logic::Not(Expression::Value(
|
|
||||||
ValueNode::Boolean(false).with_position((0, 0))
|
|
||||||
))
|
|
||||||
.evaluate(&Context::new(), true, SourcePosition(0, 0)),
|
|
||||||
Ok(Some(Evaluation::Return(Value::boolean(true))))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Statement, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Loop {
|
|
||||||
statements: Vec<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Loop {
|
|
||||||
pub fn new(statements: Vec<Statement>) -> Self {
|
|
||||||
Self { statements }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_statement(&self) -> &Statement {
|
|
||||||
self.statements.last().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Loop {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
for statement in &self.statements {
|
|
||||||
statement.define_and_validate(_context, false, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
loop {
|
|
||||||
for statement in &self.statements {
|
|
||||||
let run = statement.clone().evaluate(_context, false, scope)?;
|
|
||||||
|
|
||||||
if let Some(Evaluation::Break) = run {
|
|
||||||
return Ok(run);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.last_statement().expected_type(_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Loop {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "loop {{ ")?;
|
|
||||||
|
|
||||||
for statement in &self.statements {
|
|
||||||
write!(f, "{statement}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,187 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type, ValueNode, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct MapIndex {
|
|
||||||
collection: Expression,
|
|
||||||
index: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MapIndex {
|
|
||||||
pub fn new(left: Expression, right: Expression) -> Self {
|
|
||||||
Self {
|
|
||||||
collection: left,
|
|
||||||
index: right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for MapIndex {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.collection
|
|
||||||
.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let collection_type = if let Some(r#type) = self.collection.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.collection.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (Type::Map(fields), Expression::Identifier(identifier)) =
|
|
||||||
(collection_type, &self.index)
|
|
||||||
{
|
|
||||||
if !fields.contains_key(&identifier.node) {
|
|
||||||
return Err(ValidationError::FieldNotFound {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
position: identifier.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Expression::Identifier(_) = &self.index {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
self.index
|
|
||||||
.define_and_validate(context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let collection_position = self.collection.position();
|
|
||||||
let evaluation = self.collection.evaluate(context, _manage_memory, scope)?;
|
|
||||||
let collection = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(collection_position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (ValueInner::Map(map), Expression::Identifier(index)) =
|
|
||||||
(collection.inner().as_ref(), self.index)
|
|
||||||
{
|
|
||||||
let eval_option = map.get(&index.node).cloned().map(Evaluation::Return);
|
|
||||||
|
|
||||||
Ok(eval_option)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::CannotIndex {
|
|
||||||
r#type: collection.r#type(context)?,
|
|
||||||
position: collection_position,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
if let (Expression::Identifier(collection), Expression::Identifier(index)) =
|
|
||||||
(&self.collection, &self.index)
|
|
||||||
{
|
|
||||||
let r#type = if let Some(r#type) = context.get_type(&collection.node)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::VariableNotFound {
|
|
||||||
identifier: collection.node.clone(),
|
|
||||||
position: collection.position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Map(map) = r#type {
|
|
||||||
return if let Some(r#type) = map.get(&index.node) {
|
|
||||||
Ok(Some(r#type.clone()))
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::FieldNotFound {
|
|
||||||
identifier: index.node.clone(),
|
|
||||||
position: index.position,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (
|
|
||||||
Expression::Value(WithPosition {
|
|
||||||
node: ValueNode::Map(properties),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Expression::Identifier(index),
|
|
||||||
) = (&self.collection, &self.index)
|
|
||||||
{
|
|
||||||
for (property, constructor_option, expression) in properties {
|
|
||||||
if property == &index.node {
|
|
||||||
return if let Some(constructor) = constructor_option {
|
|
||||||
let r#type = constructor.clone().construct(context)?;
|
|
||||||
|
|
||||||
Ok(Some(r#type))
|
|
||||||
} else {
|
|
||||||
expression.expected_type(context)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (
|
|
||||||
Expression::Value(WithPosition {
|
|
||||||
node: ValueNode::Structure { fields, .. },
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Expression::Identifier(index),
|
|
||||||
) = (&self.collection, &self.index)
|
|
||||||
{
|
|
||||||
return if let Some(type_result) = fields.iter().find_map(|(property, expression)| {
|
|
||||||
if property.node == index.node {
|
|
||||||
Some(expression.expected_type(context))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
type_result
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let collection_type = if let Some(r#type) = self.collection.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
self.collection.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Err(ValidationError::CannotIndex {
|
|
||||||
r#type: collection_type,
|
|
||||||
position: self.collection.position(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for MapIndex {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let MapIndex { collection, index } = self;
|
|
||||||
|
|
||||||
write!(f, "{collection}.{index}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,361 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Math {
|
|
||||||
Add(Expression, Expression),
|
|
||||||
Subtract(Expression, Expression),
|
|
||||||
Multiply(Expression, Expression),
|
|
||||||
Divide(Expression, Expression),
|
|
||||||
Modulo(Expression, Expression),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Math {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
_scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
match self {
|
|
||||||
Math::Add(left, right) => {
|
|
||||||
let left_type = if let Some(r#type) = left.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(left.position()));
|
|
||||||
};
|
|
||||||
let right_type = if let Some(r#type) = right.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(right.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
match (&left_type, &right_type) {
|
|
||||||
(Type::Integer, Type::Integer) => Ok(()),
|
|
||||||
(Type::Float, Type::Float) => Ok(()),
|
|
||||||
(Type::String, Type::String) => Ok(()),
|
|
||||||
(Type::Integer, _) => {
|
|
||||||
Err(ValidationError::ExpectedIntegerOrFloat(right.position()))
|
|
||||||
}
|
|
||||||
(Type::Float, _) => {
|
|
||||||
Err(ValidationError::ExpectedIntegerOrFloat(right.position()))
|
|
||||||
}
|
|
||||||
(Type::String, _) => Err(ValidationError::ExpectedString {
|
|
||||||
actual: right_type,
|
|
||||||
position: right.position(),
|
|
||||||
}),
|
|
||||||
(_, _) => Err(ValidationError::ExpectedIntegerFloatOrString {
|
|
||||||
actual: right_type,
|
|
||||||
position: right.position(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Math::Subtract(left, right)
|
|
||||||
| Math::Multiply(left, right)
|
|
||||||
| Math::Divide(left, right)
|
|
||||||
| Math::Modulo(left, right) => {
|
|
||||||
let left_type = if let Some(r#type) = left.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(left.position()));
|
|
||||||
};
|
|
||||||
let right_type = if let Some(r#type) = right.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(right.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Integer | Type::Float = left_type {
|
|
||||||
if let Type::Integer | Type::Float = right_type {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::ExpectedIntegerOrFloat(right.position()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::ExpectedIntegerOrFloat(left.position()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let run_and_expect_value =
|
|
||||||
|position: SourcePosition, expression: Expression| -> Result<Value, RuntimeError> {
|
|
||||||
let evaluation = expression.evaluate(_context, _manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = match self {
|
|
||||||
Math::Add(left, right) => {
|
|
||||||
let left_position = left.position();
|
|
||||||
let left_value = run_and_expect_value(left_position, left)?;
|
|
||||||
let right_position = right.position();
|
|
||||||
let right_value = run_and_expect_value(right_position, right)?;
|
|
||||||
|
|
||||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Value::integer(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let sum = left + right;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let sum = left + *right as f64;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let sum = *left as f64 + right;
|
|
||||||
|
|
||||||
Value::float(sum)
|
|
||||||
}
|
|
||||||
(ValueInner::String(left), ValueInner::String(right)) => {
|
|
||||||
let mut concatenated = String::with_capacity(left.len() + right.len());
|
|
||||||
|
|
||||||
concatenated.extend(left.chars().chain(right.chars()));
|
|
||||||
|
|
||||||
Value::string(concatenated)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(right_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(left_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Math::Subtract(left, right) => {
|
|
||||||
let left_position = left.position();
|
|
||||||
let left_value = run_and_expect_value(left_position, left)?;
|
|
||||||
let right_position = right.position();
|
|
||||||
let right_value = run_and_expect_value(right_position, right)?;
|
|
||||||
|
|
||||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let difference = left.saturating_sub(*right);
|
|
||||||
|
|
||||||
Value::integer(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let difference = left - right;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let difference = left - *right as f64;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let difference = *left as f64 - right;
|
|
||||||
|
|
||||||
Value::float(difference)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(right_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(left_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Math::Multiply(left, right) => {
|
|
||||||
let left_position = left.position();
|
|
||||||
let left_value = run_and_expect_value(left_position, left)?;
|
|
||||||
let right_position = right.position();
|
|
||||||
let right_value = run_and_expect_value(right_position, right)?;
|
|
||||||
|
|
||||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let product = left.saturating_mul(*right);
|
|
||||||
|
|
||||||
Value::integer(product)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let product = left * right;
|
|
||||||
|
|
||||||
Value::float(product)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let product = left * *right as f64;
|
|
||||||
|
|
||||||
Value::float(product)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let product = *left as f64 * right;
|
|
||||||
|
|
||||||
Value::float(product)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(right_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(left_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Math::Divide(left, right) => {
|
|
||||||
let left_position = left.position();
|
|
||||||
let left_value = run_and_expect_value(left_position, left)?;
|
|
||||||
let right_position = right.position();
|
|
||||||
let right_value = run_and_expect_value(right_position, right)?;
|
|
||||||
|
|
||||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let quotient = left.saturating_div(*right);
|
|
||||||
|
|
||||||
Value::integer(quotient)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let quotient = left / right;
|
|
||||||
|
|
||||||
Value::float(quotient)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let quotient = left / *right as f64;
|
|
||||||
|
|
||||||
Value::float(quotient)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let quotient = *left as f64 / right;
|
|
||||||
|
|
||||||
Value::float(quotient)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(right_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(left_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Math::Modulo(left, right) => {
|
|
||||||
let left_position = left.position();
|
|
||||||
let left_value = run_and_expect_value(left_position, left)?;
|
|
||||||
let right_position = right.position();
|
|
||||||
let right_value = run_and_expect_value(right_position, right)?;
|
|
||||||
|
|
||||||
match (left_value.inner().as_ref(), right_value.inner().as_ref()) {
|
|
||||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
|
||||||
let remainder = left % right;
|
|
||||||
|
|
||||||
Value::integer(remainder)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Float(right)) => {
|
|
||||||
let remainder = left % right;
|
|
||||||
|
|
||||||
Value::float(remainder)
|
|
||||||
}
|
|
||||||
(ValueInner::Float(left), ValueInner::Integer(right)) => {
|
|
||||||
let remainder = left % *right as f64;
|
|
||||||
|
|
||||||
Value::float(remainder)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(left), ValueInner::Float(right)) => {
|
|
||||||
let remainder = *left as f64 % right;
|
|
||||||
|
|
||||||
Value::float(remainder)
|
|
||||||
}
|
|
||||||
(ValueInner::Integer(_) | ValueInner::Float(_), _) => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(right_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(left_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(Evaluation::Return(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
match self {
|
|
||||||
Math::Add(left, right)
|
|
||||||
| Math::Subtract(left, right)
|
|
||||||
| Math::Multiply(left, right)
|
|
||||||
| Math::Divide(left, right)
|
|
||||||
| Math::Modulo(left, right) => {
|
|
||||||
let left_type = if let Some(r#type) = left.expected_type(_context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(left.position()));
|
|
||||||
};
|
|
||||||
let right_type = if let Some(r#type) = right.expected_type(_context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(right.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Float = left_type {
|
|
||||||
return Ok(Some(Type::Float));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Type::Float = right_type {
|
|
||||||
return Ok(Some(Type::Float));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(left_type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Math {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Math::Add(left, right) => write!(f, "{left} + {right}"),
|
|
||||||
Math::Subtract(left, right) => write!(f, "{left} - {right}"),
|
|
||||||
Math::Multiply(left, right) => write!(f, "{left} * {right}"),
|
|
||||||
Math::Divide(left, right) => write!(f, "{left} / {right}"),
|
|
||||||
Math::Modulo(left, right) => write!(f, "{left} % {right}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
pub mod r#as;
|
|
||||||
pub mod assignment;
|
|
||||||
pub mod async_block;
|
|
||||||
pub mod block;
|
|
||||||
pub mod built_in_function;
|
|
||||||
pub mod enum_declaration;
|
|
||||||
pub mod expression;
|
|
||||||
pub mod function_call;
|
|
||||||
pub mod if_else;
|
|
||||||
pub mod list_index;
|
|
||||||
pub mod logic;
|
|
||||||
pub mod r#loop;
|
|
||||||
pub mod map_index;
|
|
||||||
pub mod math;
|
|
||||||
pub mod statement;
|
|
||||||
pub mod structure_definition;
|
|
||||||
pub mod type_alias;
|
|
||||||
pub mod type_constructor;
|
|
||||||
pub mod r#use;
|
|
||||||
pub mod value_node;
|
|
||||||
pub mod r#while;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
ops::Index,
|
|
||||||
};
|
|
||||||
|
|
||||||
use chumsky::span::{SimpleSpan, Span};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub use self::{
|
|
||||||
assignment::{Assignment, AssignmentOperator},
|
|
||||||
async_block::AsyncBlock,
|
|
||||||
block::Block,
|
|
||||||
built_in_function::BuiltInFunction,
|
|
||||||
enum_declaration::{EnumDeclaration, EnumVariant},
|
|
||||||
expression::Expression,
|
|
||||||
function_call::FunctionCall,
|
|
||||||
if_else::IfElse,
|
|
||||||
list_index::ListIndex,
|
|
||||||
logic::Logic,
|
|
||||||
map_index::MapIndex,
|
|
||||||
math::Math,
|
|
||||||
r#as::As,
|
|
||||||
r#loop::Loop,
|
|
||||||
r#use::Use,
|
|
||||||
r#while::While,
|
|
||||||
statement::Statement,
|
|
||||||
structure_definition::StructureDefinition,
|
|
||||||
type_alias::TypeAlias,
|
|
||||||
type_constructor::{FunctionTypeConstructor, ListTypeConstructor, TypeConstructor},
|
|
||||||
value_node::ValueNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{DustError, RuntimeError, ValidationError},
|
|
||||||
r#type::Type,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct WithPosition<T> {
|
|
||||||
pub node: T,
|
|
||||||
pub position: SourcePosition,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait WithPos: Sized {
|
|
||||||
fn with_position<T: Into<SourcePosition>>(self, span: T) -> WithPosition<Self> {
|
|
||||||
WithPosition {
|
|
||||||
node: self,
|
|
||||||
position: span.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> WithPos for T {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct SourcePosition(pub usize, pub usize);
|
|
||||||
|
|
||||||
impl Display for SourcePosition {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {})", self.0, self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SimpleSpan> for SourcePosition {
|
|
||||||
fn from(span: SimpleSpan) -> Self {
|
|
||||||
SourcePosition(span.start(), span.end())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(usize, usize)> for SourcePosition {
|
|
||||||
fn from((start, end): (usize, usize)) -> Self {
|
|
||||||
SourcePosition(start, end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub enum Evaluation {
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
Return(Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct AbstractTree(Vec<Statement>);
|
|
||||||
|
|
||||||
impl AbstractTree {
|
|
||||||
pub fn new(mut statements: Vec<Statement>) -> Self {
|
|
||||||
statements.sort_by(|left, right| match (&left, &right) {
|
|
||||||
(Statement::StructureDefinition(_), _) => Ordering::Less,
|
|
||||||
(_, Statement::StructureDefinition(_)) => Ordering::Greater,
|
|
||||||
(_, _) => Ordering::Equal,
|
|
||||||
});
|
|
||||||
|
|
||||||
AbstractTree(statements)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
) -> Result<Option<Value>, Vec<DustError>> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
let global_scope = SourcePosition(0, usize::MAX);
|
|
||||||
|
|
||||||
for statement in &self.0 {
|
|
||||||
let validation_result =
|
|
||||||
statement.define_and_validate(context, manage_memory, global_scope);
|
|
||||||
|
|
||||||
if let Err(error) = validation_result {
|
|
||||||
errors.push(DustError::Validation {
|
|
||||||
error,
|
|
||||||
position: statement.position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut previous_value = None;
|
|
||||||
|
|
||||||
for statement in self.0 {
|
|
||||||
let position = statement.position();
|
|
||||||
let run = statement.evaluate(context, manage_memory, global_scope);
|
|
||||||
|
|
||||||
match run {
|
|
||||||
Ok(evaluation) => match evaluation {
|
|
||||||
Some(Evaluation::Return(value)) => previous_value = Some(value),
|
|
||||||
None => previous_value = None,
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Err(runtime_error) => {
|
|
||||||
return Err(vec![DustError::Runtime {
|
|
||||||
error: runtime_error,
|
|
||||||
position,
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(previous_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<usize> for AbstractTree {
|
|
||||||
type Output = Statement;
|
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
|
||||||
&self.0[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for AbstractTree {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
for statement in &self.0 {
|
|
||||||
statement.define_and_validate(context, manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let mut previous = None;
|
|
||||||
|
|
||||||
for statement in self.0 {
|
|
||||||
previous = statement.evaluate(context, manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(previous)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.0.last().unwrap().expected_type(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AbstractNode {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError>;
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError>;
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError>;
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
AbstractNode, Assignment, AsyncBlock, Block, EnumDeclaration, Evaluation, Expression, IfElse,
|
|
||||||
Loop, SourcePosition, StructureDefinition, Type, TypeAlias, Use, While, WithPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Statement {
|
|
||||||
Assignment(WithPosition<Assignment>),
|
|
||||||
AsyncBlock(WithPosition<AsyncBlock>),
|
|
||||||
Block(WithPosition<Block>),
|
|
||||||
Break(WithPosition<()>),
|
|
||||||
IfElse(WithPosition<IfElse>),
|
|
||||||
Loop(WithPosition<Loop>),
|
|
||||||
Null(WithPosition<()>),
|
|
||||||
StructureDefinition(WithPosition<StructureDefinition>),
|
|
||||||
TypeAlias(WithPosition<TypeAlias>),
|
|
||||||
EnumDeclaration(WithPosition<EnumDeclaration>),
|
|
||||||
Expression(Expression),
|
|
||||||
While(WithPosition<While>),
|
|
||||||
Use(WithPosition<Use>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Statement {
|
|
||||||
pub fn position(&self) -> SourcePosition {
|
|
||||||
match self {
|
|
||||||
Statement::Assignment(inner) => inner.position,
|
|
||||||
Statement::AsyncBlock(inner) => inner.position,
|
|
||||||
Statement::Block(inner) => inner.position,
|
|
||||||
Statement::Break(inner) => inner.position,
|
|
||||||
Statement::Expression(expression) => expression.position(),
|
|
||||||
Statement::IfElse(inner) => inner.position,
|
|
||||||
Statement::Loop(inner) => inner.position,
|
|
||||||
Statement::Null(inner) => inner.position,
|
|
||||||
Statement::StructureDefinition(inner) => inner.position,
|
|
||||||
Statement::TypeAlias(inner) => inner.position,
|
|
||||||
Statement::EnumDeclaration(inner) => inner.position,
|
|
||||||
Statement::While(inner) => inner.position,
|
|
||||||
Statement::Use(inner) => inner.position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_evaluated_statement(&self) -> &Self {
|
|
||||||
match self {
|
|
||||||
Statement::Block(inner) => inner.node.last_statement(),
|
|
||||||
Statement::Loop(inner) => inner.node.last_statement(),
|
|
||||||
statement => statement,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Statement {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
log::trace!("Validating statement at {}", self.position());
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Statement::Assignment(assignment) => {
|
|
||||||
assignment
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::AsyncBlock(async_block) => {
|
|
||||||
async_block
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::Block(block) => {
|
|
||||||
block
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::EnumDeclaration(enum_declaration) => enum_declaration
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope),
|
|
||||||
Statement::Expression(expression) => {
|
|
||||||
expression.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::IfElse(if_else) => {
|
|
||||||
if_else
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::Loop(r#loop) => {
|
|
||||||
r#loop
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::StructureDefinition(struct_definition) => struct_definition
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope),
|
|
||||||
Statement::TypeAlias(type_alias) => {
|
|
||||||
type_alias
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::While(r#while) => {
|
|
||||||
r#while
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::Use(r#use) => {
|
|
||||||
r#use
|
|
||||||
.node
|
|
||||||
.define_and_validate(_context, _manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::Break(_) | Statement::Null(_) => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
log::trace!("Evaluating statement at {}", self.position());
|
|
||||||
|
|
||||||
let result = match self {
|
|
||||||
Statement::Assignment(assignment) => {
|
|
||||||
assignment.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::AsyncBlock(async_block) => {
|
|
||||||
async_block.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::Block(block) => block.node.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::Break(_) => Ok(Some(Evaluation::Break)),
|
|
||||||
Statement::Expression(expression) => expression.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::IfElse(if_else) => if_else.node.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::Loop(r#loop) => r#loop.node.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::Null(_) => Ok(None),
|
|
||||||
Statement::StructureDefinition(structure_definition) => structure_definition
|
|
||||||
.node
|
|
||||||
.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::TypeAlias(type_alias) => {
|
|
||||||
type_alias.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::EnumDeclaration(type_alias) => {
|
|
||||||
type_alias.node.evaluate(context, manage_memory, scope)
|
|
||||||
}
|
|
||||||
Statement::While(r#while) => r#while.node.evaluate(context, manage_memory, scope),
|
|
||||||
Statement::Use(r#use) => r#use.node.evaluate(context, manage_memory, scope),
|
|
||||||
};
|
|
||||||
|
|
||||||
if manage_memory {
|
|
||||||
context.clean()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
match self {
|
|
||||||
Statement::Expression(expression) => expression.expected_type(_context),
|
|
||||||
Statement::IfElse(if_else) => if_else.node.expected_type(_context),
|
|
||||||
Statement::Block(block) => block.node.expected_type(_context),
|
|
||||||
Statement::AsyncBlock(async_block) => async_block.node.expected_type(_context),
|
|
||||||
Statement::Assignment(assignment) => assignment.node.expected_type(_context),
|
|
||||||
Statement::Loop(r#loop) => r#loop.node.expected_type(_context),
|
|
||||||
Statement::StructureDefinition(struct_definition) => {
|
|
||||||
struct_definition.node.expected_type(_context)
|
|
||||||
}
|
|
||||||
Statement::TypeAlias(type_alias) => type_alias.node.expected_type(_context),
|
|
||||||
Statement::EnumDeclaration(enum_declaration) => {
|
|
||||||
enum_declaration.node.expected_type(_context)
|
|
||||||
}
|
|
||||||
Statement::While(r#while) => r#while.node.expected_type(_context),
|
|
||||||
Statement::Use(r#use) => r#use.node.expected_type(_context),
|
|
||||||
Statement::Break(_) | Statement::Null(_) => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Statement {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Statement::Assignment(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::AsyncBlock(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Block(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Break(_) => write!(f, "break"),
|
|
||||||
Statement::IfElse(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Loop(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Null(_) => write!(f, ";"),
|
|
||||||
Statement::StructureDefinition(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::TypeAlias(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::EnumDeclaration(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Expression(expression) => write!(f, "{expression}"),
|
|
||||||
Statement::While(inner) => write!(f, "{}", inner.node),
|
|
||||||
Statement::Use(inner) => write!(f, "{}", inner.node),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Type, TypeConstructor};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct StructureDefinition {
|
|
||||||
name: Identifier,
|
|
||||||
fields: Vec<(Identifier, TypeConstructor)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StructureDefinition {
|
|
||||||
pub fn new(name: Identifier, fields: Vec<(Identifier, TypeConstructor)>) -> Self {
|
|
||||||
Self { name, fields }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for StructureDefinition {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
let mut fields = Vec::with_capacity(self.fields.len());
|
|
||||||
|
|
||||||
for (identifier, constructor) in &self.fields {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
fields.push((identifier.clone(), r#type));
|
|
||||||
}
|
|
||||||
|
|
||||||
let struct_type = Type::Structure {
|
|
||||||
name: self.name.clone(),
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
context.set_type(self.name.clone(), struct_type, scope)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let mut fields = Vec::with_capacity(self.fields.len());
|
|
||||||
|
|
||||||
for (identifier, constructor) in self.fields {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
fields.push((identifier, r#type));
|
|
||||||
}
|
|
||||||
|
|
||||||
let struct_type = Type::Structure {
|
|
||||||
name: self.name.clone(),
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
context.set_type(self.name, struct_type, scope)?;
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for StructureDefinition {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let StructureDefinition { name, fields } = self;
|
|
||||||
|
|
||||||
write!(f, "struct {name} {{ ")?;
|
|
||||||
|
|
||||||
for (identifer, constructor) in fields {
|
|
||||||
write!(f, "{identifer}: {constructor}, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, SourcePosition, Type, TypeConstructor, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct TypeAlias {
|
|
||||||
identifier: WithPosition<Identifier>,
|
|
||||||
constructor: TypeConstructor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeAlias {
|
|
||||||
pub fn new(identifier: WithPosition<Identifier>, constructor: TypeConstructor) -> Self {
|
|
||||||
Self {
|
|
||||||
identifier,
|
|
||||||
constructor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for TypeAlias {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
let r#type = self.constructor.construct(context)?;
|
|
||||||
|
|
||||||
context.set_type(self.identifier.node.clone(), r#type, scope)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_: &Context,
|
|
||||||
_: bool,
|
|
||||||
_: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TypeAlias {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let TypeAlias {
|
|
||||||
identifier,
|
|
||||||
constructor,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "type {} = {constructor}", identifier.node)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,280 +0,0 @@
|
|||||||
use std::{
|
|
||||||
collections::BTreeMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
};
|
|
||||||
|
|
||||||
use chumsky::container::Container;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{context::Context, error::ValidationError, identifier::Identifier};
|
|
||||||
|
|
||||||
use super::{SourcePosition, Type, WithPosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum TypeConstructor {
|
|
||||||
Function(WithPosition<FunctionTypeConstructor>),
|
|
||||||
Invokation(TypeInvokationConstructor),
|
|
||||||
List(WithPosition<ListTypeConstructor>),
|
|
||||||
ListOf(WithPosition<Box<TypeConstructor>>),
|
|
||||||
Map(WithPosition<Vec<(WithPosition<Identifier>, TypeConstructor)>>),
|
|
||||||
Raw(WithPosition<RawTypeConstructor>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeConstructor {
|
|
||||||
pub fn position(&self) -> SourcePosition {
|
|
||||||
match self {
|
|
||||||
TypeConstructor::Function(WithPosition { position, .. }) => *position,
|
|
||||||
TypeConstructor::Invokation(TypeInvokationConstructor {
|
|
||||||
identifier,
|
|
||||||
type_arguments,
|
|
||||||
}) => {
|
|
||||||
if let Some(arguments) = type_arguments {
|
|
||||||
SourcePosition(
|
|
||||||
identifier.position.0,
|
|
||||||
arguments.last().unwrap().position().1,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
SourcePosition(identifier.position.0, identifier.position.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeConstructor::List(WithPosition { position, .. }) => *position,
|
|
||||||
TypeConstructor::ListOf(WithPosition { position, .. }) => *position,
|
|
||||||
TypeConstructor::Map(WithPosition { position, .. }) => *position,
|
|
||||||
TypeConstructor::Raw(WithPosition { position, .. }) => *position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn construct(&self, context: &Context) -> Result<Type, ValidationError> {
|
|
||||||
let r#type = match self {
|
|
||||||
TypeConstructor::Invokation(TypeInvokationConstructor { identifier, .. }) => {
|
|
||||||
let invoked_type = if let Some(r#type) = context.get_type(&identifier.node)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Ok(Type::Generic {
|
|
||||||
identifier: identifier.node.clone(),
|
|
||||||
concrete_type: None,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Type::Enum {
|
|
||||||
name,
|
|
||||||
type_parameters,
|
|
||||||
variants,
|
|
||||||
} = invoked_type
|
|
||||||
{
|
|
||||||
let mut mapped_variants = Vec::with_capacity(variants.len());
|
|
||||||
|
|
||||||
for (variant_name, content) in variants {
|
|
||||||
mapped_variants.push((variant_name.clone(), content.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Enum {
|
|
||||||
name,
|
|
||||||
type_parameters: type_parameters.clone(),
|
|
||||||
variants: mapped_variants,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invoked_type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeConstructor::Function(function_type_constructor) => {
|
|
||||||
let FunctionTypeConstructor {
|
|
||||||
type_parameters: declared_type_parameters,
|
|
||||||
value_parameters: declared_value_parameters,
|
|
||||||
return_type,
|
|
||||||
} = &function_type_constructor.node;
|
|
||||||
|
|
||||||
let type_parameters = declared_type_parameters.as_ref().map(|identifiers| {
|
|
||||||
identifiers
|
|
||||||
.iter()
|
|
||||||
.map(|identifier| identifier.node.clone())
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
let value_parameters =
|
|
||||||
if let Some(declared_value_parameters) = declared_value_parameters {
|
|
||||||
let mut parameters = Vec::with_capacity(declared_value_parameters.len());
|
|
||||||
|
|
||||||
for (identifier, constructor) in declared_value_parameters {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
parameters.push((identifier.node.clone(), r#type));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(parameters)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let return_type = if let Some(constructor) = return_type {
|
|
||||||
Some(Box::new(constructor.construct(context)?))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeConstructor::List(constructor) => {
|
|
||||||
let ListTypeConstructor { length, item_type } = &constructor.node;
|
|
||||||
let constructed_type = item_type.construct(context)?;
|
|
||||||
|
|
||||||
Type::List {
|
|
||||||
length: *length,
|
|
||||||
item_type: Box::new(constructed_type),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeConstructor::ListOf(item_type) => {
|
|
||||||
let item_type = item_type.node.construct(context)?;
|
|
||||||
|
|
||||||
Type::ListOf(Box::new(item_type))
|
|
||||||
}
|
|
||||||
TypeConstructor::Map(field_type_constructors) => {
|
|
||||||
let mut field_types = BTreeMap::with_capacity(field_type_constructors.node.len());
|
|
||||||
|
|
||||||
for (identifier, constructor) in &field_type_constructors.node {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
field_types.insert(identifier.node.clone(), r#type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Map(field_types)
|
|
||||||
}
|
|
||||||
TypeConstructor::Raw(raw_type) => match raw_type.node {
|
|
||||||
RawTypeConstructor::Any => Type::Any,
|
|
||||||
RawTypeConstructor::Boolean => Type::Boolean,
|
|
||||||
RawTypeConstructor::Float => Type::Float,
|
|
||||||
RawTypeConstructor::Integer => Type::Integer,
|
|
||||||
RawTypeConstructor::Range => Type::Range,
|
|
||||||
RawTypeConstructor::String => Type::String,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(r#type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TypeConstructor {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
TypeConstructor::Function(WithPosition { node, .. }) => write!(f, "{node}")?,
|
|
||||||
TypeConstructor::Invokation(type_invokation) => write!(f, "{type_invokation}")?,
|
|
||||||
TypeConstructor::List(WithPosition { node, .. }) => write!(f, "{node}")?,
|
|
||||||
TypeConstructor::ListOf(WithPosition { node, .. }) => write!(f, "{node}")?,
|
|
||||||
TypeConstructor::Map(WithPosition { node, .. }) => {
|
|
||||||
write!(f, "{{ ")?;
|
|
||||||
|
|
||||||
for (identifier, constructor) in node {
|
|
||||||
write!(f, "{}: {constructor}, ", identifier.node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")?;
|
|
||||||
}
|
|
||||||
TypeConstructor::Raw(WithPosition { node, .. }) => write!(f, "{node}")?,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum RawTypeConstructor {
|
|
||||||
Any,
|
|
||||||
Boolean,
|
|
||||||
Float,
|
|
||||||
Integer,
|
|
||||||
Range,
|
|
||||||
String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for RawTypeConstructor {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
RawTypeConstructor::Any => write!(f, "any"),
|
|
||||||
RawTypeConstructor::Boolean => write!(f, "bool"),
|
|
||||||
RawTypeConstructor::Float => write!(f, "float"),
|
|
||||||
RawTypeConstructor::Integer => write!(f, "int"),
|
|
||||||
RawTypeConstructor::Range => write!(f, "range"),
|
|
||||||
RawTypeConstructor::String => write!(f, "str"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct FunctionTypeConstructor {
|
|
||||||
pub type_parameters: Option<Vec<WithPosition<Identifier>>>,
|
|
||||||
pub value_parameters: Option<Vec<(WithPosition<Identifier>, TypeConstructor)>>,
|
|
||||||
pub return_type: Option<Box<TypeConstructor>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FunctionTypeConstructor {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "fn ")?;
|
|
||||||
|
|
||||||
if let Some(parameters) = &self.type_parameters {
|
|
||||||
write!(f, "<")?;
|
|
||||||
|
|
||||||
for identifier in parameters {
|
|
||||||
write!(f, "{}, ", identifier.node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parameters) = &self.value_parameters {
|
|
||||||
for (identifier, constructor) in parameters {
|
|
||||||
write!(f, "{}: {constructor}", identifier.node)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct ListTypeConstructor {
|
|
||||||
pub length: usize,
|
|
||||||
pub item_type: Box<TypeConstructor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ListTypeConstructor {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let ListTypeConstructor { length, item_type } = self;
|
|
||||||
|
|
||||||
write!(f, "[{item_type}; {length}]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct TypeInvokationConstructor {
|
|
||||||
pub identifier: WithPosition<Identifier>,
|
|
||||||
pub type_arguments: Option<Vec<TypeConstructor>>,
|
|
||||||
}
|
|
||||||
impl Display for TypeInvokationConstructor {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
let TypeInvokationConstructor {
|
|
||||||
identifier,
|
|
||||||
type_arguments,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "{}", identifier.node)?;
|
|
||||||
|
|
||||||
if let Some(arguments) = type_arguments {
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
for constructor in arguments {
|
|
||||||
write!(f, "{constructor}, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
path::Path,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
standard_library::{std_fs_compiled, std_io_compiled, std_json_compiled, std_thread_compiled},
|
|
||||||
Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, AbstractTree, Evaluation, SourcePosition};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Use {
|
|
||||||
path: String,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
abstract_tree: Arc<RwLock<Option<AbstractTree>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Use {
|
|
||||||
pub fn new(path: String) -> Self {
|
|
||||||
Self {
|
|
||||||
path,
|
|
||||||
abstract_tree: Arc::new(RwLock::new(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for Use {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
let abstract_tree = match self.path.as_str() {
|
|
||||||
"std.fs" => std_fs_compiled().clone(),
|
|
||||||
"std.json" => std_json_compiled().clone(),
|
|
||||||
"std.io" => std_io_compiled().clone(),
|
|
||||||
"std.thread" => std_thread_compiled().clone(),
|
|
||||||
_ => {
|
|
||||||
if Path::new(&self.path).exists() {
|
|
||||||
todo!()
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::CannotUsePath(self.path.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
*self.abstract_tree.write()? = Some(abstract_tree);
|
|
||||||
|
|
||||||
if let Some(abstract_tree) = self.abstract_tree.read()?.as_ref() {
|
|
||||||
abstract_tree.define_and_validate(context, manage_memory, scope)
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::Uninitialized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
if let Some(abstract_tree) = self.abstract_tree.read()?.as_ref() {
|
|
||||||
abstract_tree
|
|
||||||
.clone()
|
|
||||||
.evaluate(context, manage_memory, scope)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::Uninitialized,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
if let Some(abstract_tree) = self.abstract_tree.read()?.as_ref() {
|
|
||||||
abstract_tree.expected_type(context)
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::Uninitialized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Use {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "use {}", self.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Use {}
|
|
||||||
|
|
||||||
impl PartialEq for Use {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.path == other.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Use {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Use {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.path.cmp(&other.path)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,818 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
collections::BTreeMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
ops::Range,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
AbstractNode, Block, BuiltInFunction, Evaluation, Expression, SourcePosition, Type,
|
|
||||||
TypeConstructor, WithPos, WithPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ValueNode {
|
|
||||||
Boolean(bool),
|
|
||||||
BuiltInFunction(BuiltInFunction),
|
|
||||||
EnumInstance {
|
|
||||||
type_name: WithPosition<Identifier>,
|
|
||||||
type_arguments: Option<Vec<TypeConstructor>>,
|
|
||||||
variant: WithPosition<Identifier>,
|
|
||||||
content: Option<Box<Expression>>,
|
|
||||||
},
|
|
||||||
Float(f64),
|
|
||||||
Integer(i64),
|
|
||||||
List(Vec<Expression>),
|
|
||||||
Map(Vec<(Identifier, Option<TypeConstructor>, Expression)>),
|
|
||||||
Range(Range<i64>),
|
|
||||||
String(String),
|
|
||||||
Structure {
|
|
||||||
name: WithPosition<Identifier>,
|
|
||||||
fields: Vec<(WithPosition<Identifier>, Expression)>,
|
|
||||||
},
|
|
||||||
Function(FunctionNode),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueNode {
|
|
||||||
pub fn function(
|
|
||||||
type_parameters: Option<Vec<Identifier>>,
|
|
||||||
value_parameters: Option<Vec<(Identifier, TypeConstructor)>>,
|
|
||||||
return_type: Option<TypeConstructor>,
|
|
||||||
body: WithPosition<Block>,
|
|
||||||
) -> Self {
|
|
||||||
ValueNode::Function(FunctionNode {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
context: Context::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for ValueNode {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
if let ValueNode::List(list) = self {
|
|
||||||
let mut items = list.iter();
|
|
||||||
let first_item = if let Some(item) = items.next() {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let first_item_type = if let Some(r#type) = first_item.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
first_item.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
item.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let item_type = if let Some(r#type) = item.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(item.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
first_item_type.check(&item_type).map_err(|conflict| {
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: item.position(),
|
|
||||||
expected_position: Some(first_item.position()),
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ValueNode::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content,
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
if let Some(expression) = content {
|
|
||||||
expression.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Type::Enum {
|
|
||||||
variants,
|
|
||||||
type_parameters,
|
|
||||||
..
|
|
||||||
}) = context.get_type(&type_name.node)?
|
|
||||||
{
|
|
||||||
if let (Some(parameters), Some(arguments)) = (type_parameters, type_arguments) {
|
|
||||||
if arguments.len() != parameters.len() {
|
|
||||||
return Err(ValidationError::WrongTypeArgumentsCount {
|
|
||||||
expected: parameters.len(),
|
|
||||||
actual: arguments.len(),
|
|
||||||
position: arguments.last().unwrap().position(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let found = variants
|
|
||||||
.into_iter()
|
|
||||||
.any(|(identifier, _)| identifier == variant.node);
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return Err(ValidationError::EnumVariantNotFound {
|
|
||||||
identifier: variant.node.clone(),
|
|
||||||
position: variant.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::EnumDefinitionNotFound {
|
|
||||||
identifier: type_name.node.clone(),
|
|
||||||
position: Some(type_name.position),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ValueNode::Map(map_assignments) = self {
|
|
||||||
for (_identifier, constructor_option, expression) in map_assignments {
|
|
||||||
expression.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
if let Some(constructor) = constructor_option {
|
|
||||||
let actual_type = if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let expected_type = constructor.clone().construct(context)?;
|
|
||||||
|
|
||||||
expected_type.check(&actual_type).map_err(|conflict| {
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: expression.position(),
|
|
||||||
expected_position: Some(constructor.position()),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ValueNode::Function(FunctionNode {
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
context: function_context,
|
|
||||||
}) = self
|
|
||||||
{
|
|
||||||
function_context.inherit_variables_from(context)?;
|
|
||||||
|
|
||||||
if let Some(type_parameters) = type_parameters {
|
|
||||||
for identifier in type_parameters {
|
|
||||||
function_context.set_type(
|
|
||||||
identifier.clone(),
|
|
||||||
Type::Generic {
|
|
||||||
identifier: identifier.clone(),
|
|
||||||
concrete_type: None,
|
|
||||||
},
|
|
||||||
(0, usize::MAX).into(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(value_parameters) = value_parameters {
|
|
||||||
for (identifier, type_constructor) in value_parameters {
|
|
||||||
let r#type = type_constructor.clone().construct(context)?;
|
|
||||||
|
|
||||||
function_context.set_type(
|
|
||||||
identifier.clone(),
|
|
||||||
r#type,
|
|
||||||
(0, usize::MAX).into(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body.node
|
|
||||||
.define_and_validate(function_context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let ((expected_return, expected_position), actual_return) =
|
|
||||||
match (return_type, body.node.expected_type(function_context)?) {
|
|
||||||
(Some(constructor), Some(r#type)) => (
|
|
||||||
(constructor.construct(context)?, constructor.position()),
|
|
||||||
r#type,
|
|
||||||
),
|
|
||||||
(None, Some(_)) => {
|
|
||||||
return Err(ValidationError::ExpectedNonValueStatement(
|
|
||||||
body.node.last_statement().position(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
(Some(constructor), None) => {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
constructor.position(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
(None, None) => return Ok(()),
|
|
||||||
};
|
|
||||||
|
|
||||||
expected_return.check(&actual_return).map_err(|conflict| {
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: body.position,
|
|
||||||
expected_position: Some(expected_position),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ValueNode::Structure {
|
|
||||||
name,
|
|
||||||
fields: expressions,
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
if !context.contains(&name.node, scope)? {
|
|
||||||
return Err(ValidationError::VariableNotFound {
|
|
||||||
identifier: name.node.clone(),
|
|
||||||
position: name.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Type::Structure {
|
|
||||||
name: _,
|
|
||||||
fields: types,
|
|
||||||
}) = context.get_type(&name.node)?
|
|
||||||
{
|
|
||||||
for ((_, expression), (_, expected_type)) in expressions.iter().zip(types.iter()) {
|
|
||||||
expression.define_and_validate(context, _manage_memory, scope)?;
|
|
||||||
|
|
||||||
let actual_type = if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
expected_type.check(&actual_type).map_err(|conflict| {
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict,
|
|
||||||
actual_position: expression.position(),
|
|
||||||
expected_position: None,
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
context: &Context,
|
|
||||||
manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let value = match self {
|
|
||||||
ValueNode::Boolean(boolean) => Value::boolean(boolean),
|
|
||||||
ValueNode::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content: expressions,
|
|
||||||
} => {
|
|
||||||
let content = if let Some(expression) = expressions {
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, manage_memory, scope)?;
|
|
||||||
|
|
||||||
if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
Some(value)
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let type_arguments = if let Some(arguments) = type_arguments {
|
|
||||||
let mut types = Vec::with_capacity(arguments.len());
|
|
||||||
|
|
||||||
for constructor in arguments {
|
|
||||||
let r#type = constructor.construct(context)?;
|
|
||||||
|
|
||||||
types.push(r#type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(types)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::enum_instance(type_name.node, type_arguments, variant.node, content)
|
|
||||||
}
|
|
||||||
ValueNode::Float(float) => Value::float(float),
|
|
||||||
ValueNode::Integer(integer) => Value::integer(integer),
|
|
||||||
ValueNode::List(expression_list) => {
|
|
||||||
let mut value_list = Vec::with_capacity(expression_list.len());
|
|
||||||
|
|
||||||
for expression in expression_list {
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
value_list.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::list(value_list)
|
|
||||||
}
|
|
||||||
ValueNode::Map(property_list) => {
|
|
||||||
let mut property_map = BTreeMap::new();
|
|
||||||
|
|
||||||
for (identifier, _type, expression) in property_list {
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
property_map.insert(identifier, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::map(property_map)
|
|
||||||
}
|
|
||||||
ValueNode::Range(range) => Value::range(range),
|
|
||||||
ValueNode::String(string) => Value::string(string),
|
|
||||||
ValueNode::Function(FunctionNode {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let outer_context = context;
|
|
||||||
let value_parameters = if let Some(value_parameters) = value_parameters {
|
|
||||||
let mut parameters = Vec::with_capacity(value_parameters.len());
|
|
||||||
|
|
||||||
for (identifier, constructor) in value_parameters {
|
|
||||||
let r#type = constructor.construct(outer_context)?;
|
|
||||||
|
|
||||||
parameters.push((identifier, r#type));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(parameters)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let return_type = if let Some(constructor) = return_type {
|
|
||||||
Some(constructor.construct(outer_context)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::function(type_parameters, value_parameters, return_type, body.node)
|
|
||||||
}
|
|
||||||
ValueNode::Structure {
|
|
||||||
name,
|
|
||||||
fields: expressions,
|
|
||||||
} => {
|
|
||||||
let mut fields = Vec::with_capacity(expressions.len());
|
|
||||||
|
|
||||||
for (identifier, expression) in expressions {
|
|
||||||
let position = expression.position();
|
|
||||||
let evaluation = expression.evaluate(context, manage_memory, scope)?;
|
|
||||||
let value = if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(position),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
fields.push((identifier.node, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::structure(name.node, fields)
|
|
||||||
}
|
|
||||||
ValueNode::BuiltInFunction(function) => Value::built_in_function(function),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(Evaluation::Return(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
let r#type = match self {
|
|
||||||
ValueNode::Boolean(_) => Type::Boolean,
|
|
||||||
ValueNode::EnumInstance { type_name, .. } => {
|
|
||||||
if let Some(r#type) = context.get_type(&type_name.node)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::EnumDefinitionNotFound {
|
|
||||||
identifier: type_name.node.clone(),
|
|
||||||
position: Some(type_name.position),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueNode::Float(_) => Type::Float,
|
|
||||||
ValueNode::Integer(_) => Type::Integer,
|
|
||||||
ValueNode::List(expressions) => {
|
|
||||||
let first_item = expressions.first().unwrap();
|
|
||||||
let item_type = if let Some(r#type) = first_item.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
first_item.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::List {
|
|
||||||
length: expressions.len(),
|
|
||||||
item_type: Box::new(item_type),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueNode::Map(fields) => {
|
|
||||||
let mut field_types = BTreeMap::new();
|
|
||||||
|
|
||||||
for (identifier, constructor_option, expression) in fields {
|
|
||||||
let r#type = if let Some(constructor) = constructor_option {
|
|
||||||
constructor.construct(context)?
|
|
||||||
} else if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::CannotAssignToNone(expression.position()));
|
|
||||||
};
|
|
||||||
|
|
||||||
field_types.insert(identifier.clone(), r#type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Map(field_types)
|
|
||||||
}
|
|
||||||
ValueNode::Range(_) => Type::Range,
|
|
||||||
ValueNode::String(_) => Type::String,
|
|
||||||
ValueNode::Function(FunctionNode {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let value_parameters = if let Some(value_parameters) = value_parameters {
|
|
||||||
let mut parameters = Vec::with_capacity(value_parameters.len());
|
|
||||||
|
|
||||||
for (identifier, type_constructor) in value_parameters {
|
|
||||||
let r#type = type_constructor.clone().construct(context)?;
|
|
||||||
|
|
||||||
parameters.push((identifier.clone(), r#type));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(parameters)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let type_parameters = type_parameters
|
|
||||||
.clone()
|
|
||||||
.map(|parameters| parameters.into_iter().collect());
|
|
||||||
let return_type = if let Some(constructor) = return_type {
|
|
||||||
Some(Box::new(constructor.construct(context)?))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueNode::Structure {
|
|
||||||
name,
|
|
||||||
fields: expressions,
|
|
||||||
} => {
|
|
||||||
let mut types = Vec::with_capacity(expressions.len());
|
|
||||||
|
|
||||||
for (identifier, expression) in expressions {
|
|
||||||
let r#type = if let Some(r#type) = expression.expected_type(context)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::ExpectedValueStatement(
|
|
||||||
expression.position(),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
types.push((
|
|
||||||
identifier.clone(),
|
|
||||||
r#type.with_position(expression.position()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Structure {
|
|
||||||
name: name.node.clone(),
|
|
||||||
fields: types
|
|
||||||
.into_iter()
|
|
||||||
.map(|(identifier, r#type)| (identifier.node, r#type.node))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueNode::BuiltInFunction(built_in_function) => built_in_function.r#type(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(r#type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for ValueNode {}
|
|
||||||
|
|
||||||
impl PartialOrd for ValueNode {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for ValueNode {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
use self::ValueNode::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => left.cmp(right),
|
|
||||||
(Boolean(_), _) => Ordering::Greater,
|
|
||||||
(Float(left), Float(right)) => left.total_cmp(right),
|
|
||||||
(Float(_), _) => Ordering::Greater,
|
|
||||||
(Integer(left), Integer(right)) => left.cmp(right),
|
|
||||||
(Integer(_), _) => Ordering::Greater,
|
|
||||||
(List(left), List(right)) => left.cmp(right),
|
|
||||||
(List(_), _) => Ordering::Greater,
|
|
||||||
(Map(left), Map(right)) => left.cmp(right),
|
|
||||||
(Map(_), _) => Ordering::Greater,
|
|
||||||
(Range(left), Range(right)) => {
|
|
||||||
let start_cmp = left.start.cmp(&right.start);
|
|
||||||
|
|
||||||
if start_cmp.is_eq() {
|
|
||||||
left.end.cmp(&right.end)
|
|
||||||
} else {
|
|
||||||
start_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Range(_), _) => Ordering::Greater,
|
|
||||||
(String(left), String(right)) => left.cmp(right),
|
|
||||||
(String(_), _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
EnumInstance {
|
|
||||||
type_name: left_name,
|
|
||||||
type_arguments: left_type_args,
|
|
||||||
variant: left_variant,
|
|
||||||
content: left_content,
|
|
||||||
},
|
|
||||||
EnumInstance {
|
|
||||||
type_name: right_name,
|
|
||||||
type_arguments: right_type_args,
|
|
||||||
variant: right_variant,
|
|
||||||
content: right_content,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let name_cmp = left_name.cmp(right_name);
|
|
||||||
|
|
||||||
if name_cmp.is_eq() {
|
|
||||||
let type_arg_cmp = left_type_args.cmp(right_type_args);
|
|
||||||
|
|
||||||
if type_arg_cmp.is_eq() {
|
|
||||||
let variant_cmp = left_variant.cmp(right_variant);
|
|
||||||
|
|
||||||
if variant_cmp.is_eq() {
|
|
||||||
left_content.cmp(right_content)
|
|
||||||
} else {
|
|
||||||
variant_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type_arg_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(EnumInstance { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
Function(FunctionNode {
|
|
||||||
type_parameters: left_type_arguments,
|
|
||||||
value_parameters: left_parameters,
|
|
||||||
return_type: left_return,
|
|
||||||
body: left_body,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Function(FunctionNode {
|
|
||||||
type_parameters: right_type_arguments,
|
|
||||||
value_parameters: right_parameters,
|
|
||||||
return_type: right_return,
|
|
||||||
body: right_body,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) => {
|
|
||||||
let parameter_cmp = left_parameters.cmp(right_parameters);
|
|
||||||
|
|
||||||
if parameter_cmp.is_eq() {
|
|
||||||
let return_cmp = left_return.cmp(right_return);
|
|
||||||
|
|
||||||
if return_cmp.is_eq() {
|
|
||||||
let type_argument_cmp = left_type_arguments.cmp(right_type_arguments);
|
|
||||||
|
|
||||||
if type_argument_cmp.is_eq() {
|
|
||||||
left_body.cmp(right_body)
|
|
||||||
} else {
|
|
||||||
type_argument_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parameter_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Function { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
Structure {
|
|
||||||
name: left_name,
|
|
||||||
fields: left_fields,
|
|
||||||
},
|
|
||||||
Structure {
|
|
||||||
name: right_name,
|
|
||||||
fields: right_fields,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let name_cmp = left_name.cmp(right_name);
|
|
||||||
|
|
||||||
if name_cmp.is_eq() {
|
|
||||||
left_fields.cmp(right_fields)
|
|
||||||
} else {
|
|
||||||
name_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Structure { .. }, _) => Ordering::Greater,
|
|
||||||
(BuiltInFunction(left), BuiltInFunction(right)) => left.cmp(right),
|
|
||||||
(BuiltInFunction(_), _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ValueNode {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ValueNode::Boolean(boolean) => write!(f, "{boolean}"),
|
|
||||||
ValueNode::BuiltInFunction(built_in_function) => write!(f, "{built_in_function}"),
|
|
||||||
ValueNode::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content,
|
|
||||||
} => {
|
|
||||||
write!(f, "{}", type_name.node)?;
|
|
||||||
|
|
||||||
if let Some(types) = type_arguments {
|
|
||||||
write!(f, "::<")?;
|
|
||||||
|
|
||||||
for (index, r#type) in types.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{type}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "::{}", variant.node)?;
|
|
||||||
|
|
||||||
if let Some(expression) = content {
|
|
||||||
write!(f, "({expression})")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ValueNode::Float(float) => write!(f, "{float}"),
|
|
||||||
ValueNode::Integer(integer) => write!(f, "{integer}"),
|
|
||||||
ValueNode::List(expressions) => {
|
|
||||||
for expression in expressions {
|
|
||||||
write!(f, "{expression}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ValueNode::Map(fields) => {
|
|
||||||
write!(f, "{{ ")?;
|
|
||||||
|
|
||||||
for (identifier, type_option, expression) in fields {
|
|
||||||
write!(f, "{identifier}")?;
|
|
||||||
|
|
||||||
if let Some(r#type) = type_option {
|
|
||||||
write!(f, ": {type}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " = {expression}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
ValueNode::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
|
||||||
ValueNode::String(string) => write!(f, "{string}"),
|
|
||||||
ValueNode::Structure { name, fields } => {
|
|
||||||
write!(f, "{}", name.node)?;
|
|
||||||
|
|
||||||
for (identifier, expression) in fields {
|
|
||||||
write!(f, "{} = {expression},", identifier.node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ValueNode::Function(FunctionNode {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
write!(f, "fn ")?;
|
|
||||||
|
|
||||||
if let Some(type_parameters) = type_parameters {
|
|
||||||
write!(f, "<")?;
|
|
||||||
|
|
||||||
for identifier in type_parameters {
|
|
||||||
write!(f, "{identifier}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(value_parameters) = value_parameters {
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
for (identifier, constructor) in value_parameters {
|
|
||||||
write!(f, "{identifier}: {constructor}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(r#type) = return_type {
|
|
||||||
write!(f, " -> {type}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " {}", body.node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct FunctionNode {
|
|
||||||
type_parameters: Option<Vec<Identifier>>,
|
|
||||||
value_parameters: Option<Vec<(Identifier, TypeConstructor)>>,
|
|
||||||
return_type: Option<TypeConstructor>,
|
|
||||||
body: WithPosition<Block>,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
context: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for FunctionNode {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
FunctionNode {
|
|
||||||
type_parameters: self.type_parameters.clone(),
|
|
||||||
value_parameters: self.value_parameters.clone(),
|
|
||||||
return_type: self.return_type.clone(),
|
|
||||||
body: self.body.clone(),
|
|
||||||
context: Context::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for FunctionNode {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.type_parameters == other.type_parameters
|
|
||||||
&& self.value_parameters == other.value_parameters
|
|
||||||
&& self.return_type == other.return_type
|
|
||||||
&& self.body == other.body
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
value::ValueInner,
|
|
||||||
Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AbstractNode, Evaluation, Expression, SourcePosition, Statement, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct While {
|
|
||||||
expression: Expression,
|
|
||||||
statements: Vec<Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl While {
|
|
||||||
pub fn new(expression: Expression, statements: Vec<Statement>) -> Self {
|
|
||||||
Self {
|
|
||||||
expression,
|
|
||||||
statements,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractNode for While {
|
|
||||||
fn define_and_validate(
|
|
||||||
&self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), ValidationError> {
|
|
||||||
self.expression
|
|
||||||
.define_and_validate(_context, false, scope)?;
|
|
||||||
|
|
||||||
for statement in &self.statements {
|
|
||||||
statement.define_and_validate(_context, false, scope)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(
|
|
||||||
self,
|
|
||||||
_context: &Context,
|
|
||||||
_manage_memory: bool,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
let get_boolean = || -> Result<Value, RuntimeError> {
|
|
||||||
let expression_position = self.expression.position();
|
|
||||||
let evaluation = self.expression.clone().evaluate(_context, false, scope)?;
|
|
||||||
|
|
||||||
if let Some(Evaluation::Return(value)) = evaluation {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::ExpectedValueStatement(expression_position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
while let ValueInner::Boolean(true) = get_boolean()?.inner().as_ref() {
|
|
||||||
for statement in &self.statements {
|
|
||||||
let evaluation = statement.clone().evaluate(_context, false, scope)?;
|
|
||||||
|
|
||||||
if let Some(Evaluation::Break) = evaluation {
|
|
||||||
return Ok(evaluation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Option<Type>, ValidationError> {
|
|
||||||
self.statements.last().unwrap().expected_type(_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for While {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "while {} {{", self.expression)?;
|
|
||||||
|
|
||||||
for statement in &self.statements {
|
|
||||||
write!(f, "{statement}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,327 +0,0 @@
|
|||||||
use crate::{identifier::Identifier, Value};
|
|
||||||
|
|
||||||
pub type Span = (usize, usize);
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum LexError {
|
|
||||||
IntegerParseError(std::num::ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::num::ParseIntError> for LexError {
|
|
||||||
fn from(v: std::num::ParseIntError) -> Self {
|
|
||||||
Self::IntegerParseError(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Token {
|
|
||||||
Eof,
|
|
||||||
Equal,
|
|
||||||
Identifier(Identifier),
|
|
||||||
Integer(i64),
|
|
||||||
Plus,
|
|
||||||
Star,
|
|
||||||
LeftParenthesis,
|
|
||||||
RightParenthesis,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lex(input: &str) -> Result<Vec<(Token, Span)>, LexError> {
|
|
||||||
let mut lexer = Lexer::new(input);
|
|
||||||
let mut tokens = Vec::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let (token, span) = lexer.next_token()?;
|
|
||||||
let is_eof = matches!(token, Token::Eof);
|
|
||||||
|
|
||||||
tokens.push((token, span));
|
|
||||||
|
|
||||||
if is_eof {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Lexer<'a> {
|
|
||||||
input: &'a str,
|
|
||||||
position: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Lexer<'a> {
|
|
||||||
pub fn new(input: &'a str) -> Self {
|
|
||||||
Lexer { input, position: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_char(&mut self) -> Option<char> {
|
|
||||||
self.input[self.position..].chars().next().map(|c| {
|
|
||||||
self.position += c.len_utf8();
|
|
||||||
c
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_token(&mut self) -> Result<(Token, Span), LexError> {
|
|
||||||
self.skip_whitespace();
|
|
||||||
|
|
||||||
let (token, span) = if let Some(c) = self.peek_char() {
|
|
||||||
match c {
|
|
||||||
'0'..='9' => self.lex_number()?,
|
|
||||||
'a'..='z' | 'A'..='Z' => self.lex_identifier()?,
|
|
||||||
'+' => {
|
|
||||||
self.position += 1;
|
|
||||||
(Token::Plus, (self.position - 1, self.position))
|
|
||||||
}
|
|
||||||
'*' => {
|
|
||||||
self.position += 1;
|
|
||||||
(Token::Star, (self.position - 1, self.position))
|
|
||||||
}
|
|
||||||
'(' => {
|
|
||||||
self.position += 1;
|
|
||||||
(Token::LeftParenthesis, (self.position - 1, self.position))
|
|
||||||
}
|
|
||||||
')' => {
|
|
||||||
self.position += 1;
|
|
||||||
(Token::RightParenthesis, (self.position - 1, self.position))
|
|
||||||
}
|
|
||||||
'=' => {
|
|
||||||
self.position += 1;
|
|
||||||
(Token::Equal, (self.position - 1, self.position))
|
|
||||||
}
|
|
||||||
_ => (Token::Eof, (self.position, self.position)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(Token::Eof, (self.position, self.position))
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((token, span))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_whitespace(&mut self) {
|
|
||||||
while let Some(c) = self.peek_char() {
|
|
||||||
if c.is_whitespace() {
|
|
||||||
self.next_char();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek_char(&self) -> Option<char> {
|
|
||||||
self.input[self.position..].chars().next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_number(&mut self) -> Result<(Token, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
while let Some(c) = self.peek_char() {
|
|
||||||
if c.is_ascii_digit() {
|
|
||||||
self.next_char();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let integer = self.input[start_pos..self.position].parse::<i64>()?;
|
|
||||||
|
|
||||||
Ok((Token::Integer(integer), (start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_identifier(&mut self) -> Result<(Token, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
while let Some(c) = self.peek_char() {
|
|
||||||
if c.is_ascii_alphanumeric() {
|
|
||||||
self.next_char();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let identifier = &self.input[start_pos..self.position];
|
|
||||||
let token = Token::Identifier(Identifier::new(identifier));
|
|
||||||
|
|
||||||
Ok((token, (start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum Instruction {
|
|
||||||
Add(Box<(Instruction, Instruction)>),
|
|
||||||
Assign(Box<(Instruction, Instruction)>),
|
|
||||||
Constant(Value),
|
|
||||||
Identifier(Identifier),
|
|
||||||
Multiply(Box<(Instruction, Instruction)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum ParseError {
|
|
||||||
LexError(LexError),
|
|
||||||
ExpectedClosingParenthesis,
|
|
||||||
UnexpectedToken(Token),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LexError> for ParseError {
|
|
||||||
fn from(v: LexError) -> Self {
|
|
||||||
Self::LexError(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Parser<'a> {
|
|
||||||
lexer: Lexer<'a>,
|
|
||||||
current_token: Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
|
||||||
pub fn new(lexer: Lexer<'a>) -> Self {
|
|
||||||
let mut lexer = lexer;
|
|
||||||
let current_token = lexer
|
|
||||||
.next_token()
|
|
||||||
.map(|(token, _)| token)
|
|
||||||
.unwrap_or(Token::Eof);
|
|
||||||
|
|
||||||
Parser {
|
|
||||||
lexer,
|
|
||||||
current_token,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Instruction, ParseError> {
|
|
||||||
self.parse_instruction(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_token(&mut self) -> Result<(), ParseError> {
|
|
||||||
self.current_token = self.lexer.next_token()?.0;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_instruction(&mut self, precedence: u8) -> Result<Instruction, ParseError> {
|
|
||||||
let mut left = self.parse_primary()?;
|
|
||||||
|
|
||||||
while precedence < self.current_precedence() {
|
|
||||||
match &self.current_token {
|
|
||||||
Token::Plus => {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let right = self.parse_instruction(self.current_precedence())?;
|
|
||||||
left = Instruction::Add(Box::new((left, right)));
|
|
||||||
}
|
|
||||||
Token::Star => {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let right = self.parse_instruction(self.current_precedence())?;
|
|
||||||
left = Instruction::Multiply(Box::new((left, right)));
|
|
||||||
}
|
|
||||||
Token::Equal => {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let right = self.parse_instruction(self.current_precedence())?;
|
|
||||||
left = Instruction::Assign(Box::new((left, right)));
|
|
||||||
}
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(left)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_primary(&mut self) -> Result<Instruction, ParseError> {
|
|
||||||
match self.current_token.clone() {
|
|
||||||
Token::Integer(int) => {
|
|
||||||
self.next_token()?;
|
|
||||||
Ok(Instruction::Constant(Value::integer(int)))
|
|
||||||
}
|
|
||||||
Token::Identifier(identifier) => {
|
|
||||||
self.next_token()?;
|
|
||||||
Ok(Instruction::Identifier(identifier))
|
|
||||||
}
|
|
||||||
Token::LeftParenthesis => {
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
let instruction = self.parse_instruction(0)?;
|
|
||||||
|
|
||||||
if let Token::RightParenthesis = self.current_token {
|
|
||||||
self.next_token()?;
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedClosingParenthesis);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(instruction)
|
|
||||||
}
|
|
||||||
_ => Err(ParseError::UnexpectedToken(self.current_token.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_precedence(&self) -> u8 {
|
|
||||||
match self.current_token {
|
|
||||||
Token::Equal => 3,
|
|
||||||
Token::Plus => 1,
|
|
||||||
Token::Star => 2,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{identifier::Identifier, Value};
|
|
||||||
|
|
||||||
use super::{lex, Instruction, Lexer, Parser, Token};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lex_api() {
|
|
||||||
let input = "1 + 2 * 3";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(input),
|
|
||||||
Ok(vec![
|
|
||||||
(Token::Integer(1), (0, 1)),
|
|
||||||
(Token::Plus, (2, 3)),
|
|
||||||
(Token::Integer(2), (4, 5)),
|
|
||||||
(Token::Star, (6, 7)),
|
|
||||||
(Token::Integer(3), (8, 9)),
|
|
||||||
(Token::Eof, (9, 9)),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser() {
|
|
||||||
let input = "1 + 2 * 3";
|
|
||||||
let lexer = Lexer::new(input);
|
|
||||||
let mut parser = Parser::new(lexer);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parser.parse(),
|
|
||||||
Ok(Instruction::Add(Box::new((
|
|
||||||
Instruction::Constant(Value::integer(1)),
|
|
||||||
Instruction::Multiply(Box::new((
|
|
||||||
Instruction::Constant(Value::integer(2)),
|
|
||||||
Instruction::Constant(Value::integer(3))
|
|
||||||
)))
|
|
||||||
))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn assignment() {
|
|
||||||
let input = "a = 1 + 2 * 3";
|
|
||||||
let lexer = Lexer::new(input);
|
|
||||||
let mut parser = Parser::new(lexer);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parser.parse(),
|
|
||||||
Ok(Instruction::Assign(Box::new((
|
|
||||||
Instruction::Identifier(Identifier::new("a")),
|
|
||||||
Instruction::Add(Box::new((
|
|
||||||
Instruction::Constant(Value::integer(1)),
|
|
||||||
Instruction::Multiply(Box::new((
|
|
||||||
Instruction::Constant(Value::integer(2)),
|
|
||||||
Instruction::Constant(Value::integer(3))
|
|
||||||
)))
|
|
||||||
)))
|
|
||||||
))))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,305 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::Debug,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::trace;
|
|
||||||
use rand::random;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::SourcePosition,
|
|
||||||
error::{PoisonError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
standard_library::core_context,
|
|
||||||
value::ValueInner,
|
|
||||||
Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
type VariableInfo = (VariableData, UsageData, SourcePosition);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Context {
|
|
||||||
id: u32,
|
|
||||||
variables: Arc<RwLock<HashMap<Identifier, VariableInfo>>>,
|
|
||||||
is_clean: Arc<RwLock<bool>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Context {
|
|
||||||
id: random(),
|
|
||||||
variables: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
is_clean: Arc::new(RwLock::new(true)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_std_core() -> Result<Self, PoisonError> {
|
|
||||||
let new = Context::with_variables_from(core_context())?;
|
|
||||||
|
|
||||||
Ok(new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_variables_from(other: &Context) -> Result<Self, PoisonError> {
|
|
||||||
let variables = other.variables.read()?.clone();
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
id: random(),
|
|
||||||
variables: Arc::new(RwLock::new(variables)),
|
|
||||||
is_clean: Arc::new(RwLock::new(true)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inherit_variables_from(&self, other: &Context) -> Result<(), PoisonError> {
|
|
||||||
let (get_self_variables, get_other_variables) =
|
|
||||||
(self.variables.try_write(), other.variables.try_read());
|
|
||||||
|
|
||||||
if let (Ok(mut self_variables), Ok(other_variables)) =
|
|
||||||
(get_self_variables, get_other_variables)
|
|
||||||
{
|
|
||||||
self_variables.extend(other_variables.iter().filter_map(
|
|
||||||
|(identifier, (variable, usage, position))| {
|
|
||||||
trace!("Inheriting {identifier}");
|
|
||||||
|
|
||||||
if let VariableData::Type(r#type) = variable {
|
|
||||||
match r#type {
|
|
||||||
Type::Enum { .. } | Type::Function { .. } | Type::Structure { .. } => {
|
|
||||||
return Some((
|
|
||||||
identifier.clone(),
|
|
||||||
(variable.clone(), usage.clone(), *position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let VariableData::Value(value) = variable {
|
|
||||||
match value.inner().as_ref() {
|
|
||||||
ValueInner::BuiltInFunction(_) | ValueInner::Function(_) => {
|
|
||||||
return Some((
|
|
||||||
identifier.clone(),
|
|
||||||
(variable.clone(), usage.clone(), *position),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<bool, ValidationError> {
|
|
||||||
log::trace!("Checking that {identifier} exists");
|
|
||||||
|
|
||||||
let variables = self.variables.read()?;
|
|
||||||
|
|
||||||
if let Some((_, _, variable_scope)) = variables.get(identifier) {
|
|
||||||
if scope.0 >= variable_scope.0 && scope.1 <= variable_scope.1 {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trace!("Denying access to {identifier}, out of scope")
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
|
|
||||||
log::trace!("Getting {identifier}'s type");
|
|
||||||
|
|
||||||
let variables = self.variables.read()?;
|
|
||||||
|
|
||||||
if let Some((data, _, _)) = variables.get(identifier) {
|
|
||||||
let r#type = match data {
|
|
||||||
VariableData::Type(r#type) => r#type.clone(),
|
|
||||||
VariableData::Value(value) => value.r#type(self)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(r#type.clone()))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn use_value(&self, identifier: &Identifier) -> Result<Option<Value>, PoisonError> {
|
|
||||||
log::trace!("Using {identifier}'s value");
|
|
||||||
|
|
||||||
let variables = self.variables.read()?;
|
|
||||||
|
|
||||||
if let Some((VariableData::Value(value), usage_data, _)) = variables.get(identifier) {
|
|
||||||
usage_data.inner().write()?.actual += 1;
|
|
||||||
*self.is_clean.write()? = false;
|
|
||||||
|
|
||||||
Ok(Some(value.clone()))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, PoisonError> {
|
|
||||||
log::trace!("Getting {identifier}'s value");
|
|
||||||
|
|
||||||
let variables = self.variables.read()?;
|
|
||||||
|
|
||||||
if let Some((VariableData::Value(value), _, _)) = variables.get(identifier) {
|
|
||||||
Ok(Some(value.clone()))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_type(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
r#type: Type,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), PoisonError> {
|
|
||||||
log::debug!("Setting {identifier} to type {}", r#type);
|
|
||||||
|
|
||||||
let mut variables = self.variables.write()?;
|
|
||||||
let (usage_data, scope) = variables
|
|
||||||
.remove(&identifier)
|
|
||||||
.map(|(_, old_usage_data, old_scope)| (old_usage_data, old_scope))
|
|
||||||
.unwrap_or_else(|| (UsageData::new(), scope));
|
|
||||||
|
|
||||||
variables.insert(identifier, (VariableData::Type(r#type), usage_data, scope));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_value(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
value: Value,
|
|
||||||
scope: SourcePosition,
|
|
||||||
) -> Result<(), PoisonError> {
|
|
||||||
log::debug!("Setting {identifier} to value {value}");
|
|
||||||
|
|
||||||
let mut variables = self.variables.write()?;
|
|
||||||
let (usage_data, scope) = variables
|
|
||||||
.remove(&identifier)
|
|
||||||
.map(|(_, old_usage_data, old_scope)| (old_usage_data, old_scope))
|
|
||||||
.unwrap_or_else(|| (UsageData::new(), scope));
|
|
||||||
|
|
||||||
variables.insert(identifier, (VariableData::Value(value), usage_data, scope));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_expected_use(&self, identifier: &Identifier) -> Result<bool, PoisonError> {
|
|
||||||
log::trace!("Adding expected use for variable {identifier}");
|
|
||||||
|
|
||||||
let variables = self.variables.read()?;
|
|
||||||
|
|
||||||
if let Some((_, usage_data, _)) = variables.get(identifier) {
|
|
||||||
usage_data.inner().write()?.expected += 1;
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clean(&self) -> Result<(), PoisonError> {
|
|
||||||
if *self.is_clean.read()? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.variables.write()?.retain(
|
|
||||||
|identifier, (value_data, usage_data, _)| match value_data {
|
|
||||||
VariableData::Type(_) => true,
|
|
||||||
VariableData::Value(_) => {
|
|
||||||
let usage = usage_data.inner().read().unwrap();
|
|
||||||
|
|
||||||
if usage.actual < usage.expected {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
log::trace!("Removing {identifier}");
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
*self.is_clean.write()? = true;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_clean(&mut self) -> Result<bool, PoisonError> {
|
|
||||||
Ok(*self.is_clean.read()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Context {
|
|
||||||
fn default() -> Self {
|
|
||||||
Context::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Context {}
|
|
||||||
|
|
||||||
impl PartialEq for Context {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Context {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Context {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.id.cmp(&other.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum VariableData {
|
|
||||||
Type(Type),
|
|
||||||
Value(Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct UsageData(Arc<RwLock<UsageDataInner>>);
|
|
||||||
|
|
||||||
impl UsageData {
|
|
||||||
pub fn inner(&self) -> &Arc<RwLock<UsageDataInner>> {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct UsageDataInner {
|
|
||||||
pub actual: u32,
|
|
||||||
pub expected: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsageData {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
UsageData(Arc::new(RwLock::new(UsageDataInner {
|
|
||||||
actual: 0,
|
|
||||||
expected: 0,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UsageData {
|
|
||||||
fn default() -> Self {
|
|
||||||
UsageData::new()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,221 +0,0 @@
|
|||||||
use std::{io, sync::PoisonError as StdPoisonError};
|
|
||||||
|
|
||||||
use chumsky::{prelude::Rich, span::Span};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::{Expression, SourcePosition, TypeConstructor},
|
|
||||||
identifier::Identifier,
|
|
||||||
lexer::Token,
|
|
||||||
Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum DustError {
|
|
||||||
Lex {
|
|
||||||
expected: String,
|
|
||||||
span: (usize, usize),
|
|
||||||
reason: String,
|
|
||||||
},
|
|
||||||
Parse {
|
|
||||||
expected: String,
|
|
||||||
span: (usize, usize),
|
|
||||||
found: Option<String>,
|
|
||||||
},
|
|
||||||
Runtime {
|
|
||||||
error: RuntimeError,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
Validation {
|
|
||||||
error: ValidationError,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rich<'_, char>> for DustError {
|
|
||||||
fn from(error: Rich<'_, char>) -> Self {
|
|
||||||
DustError::Lex {
|
|
||||||
expected: error.expected().map(|error| error.to_string()).collect(),
|
|
||||||
span: (error.span().start(), error.span().end()),
|
|
||||||
reason: error.reason().to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> From<Rich<'_, Token<'src>>> for DustError {
|
|
||||||
fn from(error: Rich<'_, Token<'src>>) -> Self {
|
|
||||||
DustError::Parse {
|
|
||||||
expected: error.expected().map(|error| error.to_string()).collect(),
|
|
||||||
span: (error.span().start(), error.span().end()),
|
|
||||||
found: error.found().map(|token| token.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RuntimeError {
|
|
||||||
Io(io::Error),
|
|
||||||
RwLockPoison(PoisonError),
|
|
||||||
ValidationFailure(ValidationError),
|
|
||||||
SerdeJson(serde_json::Error),
|
|
||||||
Use(Vec<DustError>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PoisonError> for RuntimeError {
|
|
||||||
fn from(error: PoisonError) -> Self {
|
|
||||||
RuntimeError::RwLockPoison(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<StdPoisonError<T>> for RuntimeError {
|
|
||||||
fn from(_: StdPoisonError<T>) -> Self {
|
|
||||||
RuntimeError::RwLockPoison(PoisonError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ValidationError> for RuntimeError {
|
|
||||||
fn from(error: ValidationError) -> Self {
|
|
||||||
RuntimeError::ValidationFailure(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for RuntimeError {
|
|
||||||
fn from(error: io::Error) -> Self {
|
|
||||||
RuntimeError::Io(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::Error> for RuntimeError {
|
|
||||||
fn from(error: serde_json::Error) -> Self {
|
|
||||||
RuntimeError::SerdeJson(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for RuntimeError {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(RuntimeError::Io(_), RuntimeError::Io(_)) => false,
|
|
||||||
(RuntimeError::RwLockPoison(_), RuntimeError::RwLockPoison(_)) => true,
|
|
||||||
(RuntimeError::ValidationFailure(left), RuntimeError::ValidationFailure(right)) => {
|
|
||||||
left == right
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ValidationError {
|
|
||||||
/// A node needs to be initialized before it can be validated.
|
|
||||||
Uninitialized,
|
|
||||||
BuiltInFunctionFailure(&'static str),
|
|
||||||
CannotAssignToNone(SourcePosition),
|
|
||||||
CannotIndex {
|
|
||||||
r#type: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
CannotIndexWith {
|
|
||||||
collection_type: Type,
|
|
||||||
collection_position: SourcePosition,
|
|
||||||
index_type: Type,
|
|
||||||
index_position: SourcePosition,
|
|
||||||
},
|
|
||||||
CannotUsePath(String),
|
|
||||||
ExpectedString {
|
|
||||||
actual: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
ExpectedList {
|
|
||||||
actual: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
ExpectedBoolean {
|
|
||||||
actual: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
ExpectedFunction {
|
|
||||||
actual: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
ExpectedIntegerOrFloat(SourcePosition),
|
|
||||||
ExpectedIntegerFloatOrString {
|
|
||||||
actual: Type,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
FullTypeNotKnown {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
ExpectedValueStatement(SourcePosition),
|
|
||||||
ExpectedNonValueStatement(SourcePosition),
|
|
||||||
RwLockPoison(PoisonError),
|
|
||||||
TypeCheck {
|
|
||||||
/// The mismatch that caused the error.
|
|
||||||
conflict: TypeConflict,
|
|
||||||
|
|
||||||
/// The position of the item that gave the "actual" type.
|
|
||||||
actual_position: SourcePosition,
|
|
||||||
|
|
||||||
/// The position of the item that gave the "expected" type.
|
|
||||||
expected_position: Option<SourcePosition>,
|
|
||||||
},
|
|
||||||
WrongTypeArguments {
|
|
||||||
parameters: Vec<Identifier>,
|
|
||||||
arguments: Vec<TypeConstructor>,
|
|
||||||
},
|
|
||||||
WrongValueArguments {
|
|
||||||
parameters: Vec<(Identifier, Type)>,
|
|
||||||
arguments: Vec<Expression>,
|
|
||||||
},
|
|
||||||
VariableNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
FieldNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
EnumDefinitionNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: Option<SourcePosition>,
|
|
||||||
},
|
|
||||||
EnumVariantNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
WrongTypeArgumentsCount {
|
|
||||||
expected: usize,
|
|
||||||
actual: usize,
|
|
||||||
position: SourcePosition,
|
|
||||||
},
|
|
||||||
StructDefinitionNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: Option<SourcePosition>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PoisonError> for ValidationError {
|
|
||||||
fn from(error: PoisonError) -> Self {
|
|
||||||
ValidationError::RwLockPoison(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<StdPoisonError<T>> for ValidationError {
|
|
||||||
fn from(_: StdPoisonError<T>) -> Self {
|
|
||||||
ValidationError::RwLockPoison(PoisonError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct PoisonError;
|
|
||||||
|
|
||||||
impl<T> From<StdPoisonError<T>> for PoisonError {
|
|
||||||
fn from(_: StdPoisonError<T>) -> Self {
|
|
||||||
PoisonError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct TypeConflict {
|
|
||||||
pub actual: Type,
|
|
||||||
pub expected: Type,
|
|
||||||
}
|
|
@ -1,520 +0,0 @@
|
|||||||
use std::{
|
|
||||||
collections::{hash_map, HashMap},
|
|
||||||
ops::Range,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::AbstractTree,
|
|
||||||
context::Context,
|
|
||||||
error::{DustError, RuntimeError, TypeConflict, ValidationError},
|
|
||||||
lexer::{lex, Token},
|
|
||||||
parser::parse,
|
|
||||||
standard_library::core_context,
|
|
||||||
Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn interpret(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
|
|
||||||
let interpreter = Interpreter::new();
|
|
||||||
|
|
||||||
interpreter.run(Arc::from(source_id), Arc::from(source))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interpreter, lexer and parser for the Dust programming language.
|
|
||||||
///
|
|
||||||
/// You must provide the interpreter with an ID for each piece of code you pass to it. These are
|
|
||||||
/// used to identify the source of errors and to provide more detailed error messages.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Interpreter {
|
|
||||||
contexts: Arc<RwLock<HashMap<Arc<str>, Context>>>,
|
|
||||||
sources: Arc<RwLock<HashMap<Arc<str>, Arc<str>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Interpreter {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Interpreter {
|
|
||||||
contexts: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
sources: Arc::new(RwLock::new(HashMap::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lexes the source code and returns a list of tokens.
|
|
||||||
pub fn lex<'src>(
|
|
||||||
&self,
|
|
||||||
source_id: Arc<str>,
|
|
||||||
source: &'src Arc<str>,
|
|
||||||
) -> Result<Vec<Token<'src>>, InterpreterError> {
|
|
||||||
self.sources
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert(source_id.clone(), source.clone());
|
|
||||||
|
|
||||||
lex(source.as_ref())
|
|
||||||
.map(|tokens| tokens.into_iter().map(|(token, _)| token).collect())
|
|
||||||
.map_err(|errors| InterpreterError {
|
|
||||||
source_id: source_id.clone(),
|
|
||||||
errors,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses the source code and returns an abstract syntax tree.
|
|
||||||
pub fn parse(
|
|
||||||
&self,
|
|
||||||
source_id: Arc<str>,
|
|
||||||
source: &Arc<str>,
|
|
||||||
) -> Result<AbstractTree, InterpreterError> {
|
|
||||||
self.sources
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert(source_id.clone(), source.clone());
|
|
||||||
|
|
||||||
parse(&lex(source).map_err(|errors| InterpreterError {
|
|
||||||
source_id: source_id.clone(),
|
|
||||||
errors,
|
|
||||||
})?)
|
|
||||||
.map_err(|errors| InterpreterError { source_id, errors })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs the source code and returns the result.
|
|
||||||
pub fn run(
|
|
||||||
&self,
|
|
||||||
source_id: Arc<str>,
|
|
||||||
source: Arc<str>,
|
|
||||||
) -> Result<Option<Value>, InterpreterError> {
|
|
||||||
let mut sources = self.sources.write().unwrap();
|
|
||||||
|
|
||||||
sources.insert(source_id.clone(), source.clone());
|
|
||||||
|
|
||||||
let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError {
|
|
||||||
source_id: source_id.clone(),
|
|
||||||
errors,
|
|
||||||
})?;
|
|
||||||
let abstract_tree = parse(&tokens).map_err(|errors| InterpreterError {
|
|
||||||
source_id: source_id.clone(),
|
|
||||||
errors,
|
|
||||||
})?;
|
|
||||||
let context = self.get_or_create_context(&source_id);
|
|
||||||
let value_option = abstract_tree
|
|
||||||
.run(&context, true)
|
|
||||||
.map_err(|errors| InterpreterError { source_id, errors })?;
|
|
||||||
|
|
||||||
Ok(value_option)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sources(&self) -> hash_map::IntoIter<Arc<str>, Arc<str>> {
|
|
||||||
self.sources.read().unwrap().clone().into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_or_create_context(&self, source_id: &Arc<str>) -> Context {
|
|
||||||
let mut contexts = self.contexts.write().unwrap();
|
|
||||||
|
|
||||||
if let Some(context) = contexts.get(source_id) {
|
|
||||||
context.clone()
|
|
||||||
} else {
|
|
||||||
let context = core_context().clone();
|
|
||||||
|
|
||||||
contexts.insert(source_id.clone(), context.clone());
|
|
||||||
|
|
||||||
context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Interpreter {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
/// An error that occurred during the interpretation of a piece of code.
|
|
||||||
///
|
|
||||||
/// Each error has a source ID that identifies the piece of code that caused the error, and a list
|
|
||||||
/// of errors that occurred during the interpretation of that code.
|
|
||||||
pub struct InterpreterError {
|
|
||||||
source_id: Arc<str>,
|
|
||||||
errors: Vec<DustError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterpreterError {
|
|
||||||
pub fn new(source_id: Arc<str>, errors: Vec<DustError>) -> Self {
|
|
||||||
InterpreterError { source_id, errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn errors(&self) -> &Vec<DustError> {
|
|
||||||
&self.errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterpreterError {
|
|
||||||
/// Converts the error into a list of user-friendly reports that can be printed to the console.
|
|
||||||
pub fn build_reports<'a>(self) -> Vec<Report<'a, (Arc<str>, Range<usize>)>> {
|
|
||||||
let token_color = Color::Yellow;
|
|
||||||
let type_color = Color::Green;
|
|
||||||
let identifier_color = Color::Blue;
|
|
||||||
|
|
||||||
let mut reports = Vec::new();
|
|
||||||
|
|
||||||
for error in self.errors {
|
|
||||||
let (mut builder, validation_error) = match error {
|
|
||||||
DustError::Lex {
|
|
||||||
expected,
|
|
||||||
span,
|
|
||||||
reason,
|
|
||||||
} => {
|
|
||||||
let description = if expected.is_empty() {
|
|
||||||
"Invalid character.".to_string()
|
|
||||||
} else {
|
|
||||||
format!("Expected {expected}.")
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Lexing Error", Color::Yellow),
|
|
||||||
self.source_id.clone(),
|
|
||||||
span.1,
|
|
||||||
)
|
|
||||||
.with_message(description)
|
|
||||||
.with_label(
|
|
||||||
Label::new((self.source_id.clone(), span.0..span.1))
|
|
||||||
.with_message(reason)
|
|
||||||
.with_color(Color::Red),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DustError::Parse {
|
|
||||||
expected,
|
|
||||||
span,
|
|
||||||
found,
|
|
||||||
} => {
|
|
||||||
let description = if expected.is_empty() {
|
|
||||||
"Invalid token.".to_string()
|
|
||||||
} else {
|
|
||||||
format!("Expected {expected}.")
|
|
||||||
};
|
|
||||||
let found = found
|
|
||||||
.unwrap_or_else(|| "End of input".to_string())
|
|
||||||
.fg(token_color);
|
|
||||||
|
|
||||||
(
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Parsing Error", Color::Yellow),
|
|
||||||
self.source_id.clone(),
|
|
||||||
span.1,
|
|
||||||
)
|
|
||||||
.with_message(description)
|
|
||||||
.with_label(
|
|
||||||
Label::new((self.source_id.clone(), span.0..span.1))
|
|
||||||
.with_message(format!("{found} is not valid in this position."))
|
|
||||||
.with_color(Color::Red),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DustError::Validation { error, position } => (
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Validation Error", Color::Magenta),
|
|
||||||
self.source_id.clone(),
|
|
||||||
position.1,
|
|
||||||
)
|
|
||||||
.with_message("The syntax is valid but this code would cause an error.")
|
|
||||||
.with_note(
|
|
||||||
"This error was detected by the interpreter before running the code.",
|
|
||||||
),
|
|
||||||
Some(error),
|
|
||||||
),
|
|
||||||
DustError::Runtime { error, position } => {
|
|
||||||
let note = match &error {
|
|
||||||
RuntimeError::Io(io_error) => &io_error.to_string(),
|
|
||||||
RuntimeError::RwLockPoison(_) => todo!(),
|
|
||||||
RuntimeError::ValidationFailure(_) => {
|
|
||||||
"This is the interpreter's fault. Please submit a bug with this error message."
|
|
||||||
}
|
|
||||||
RuntimeError::SerdeJson(serde_json_error) => &serde_json_error.to_string(),
|
|
||||||
RuntimeError::Use(_) => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
|
||||||
Report::build(
|
|
||||||
ReportKind::Custom("Runtime Error", Color::Red),
|
|
||||||
self.source_id.clone(),
|
|
||||||
position.1,
|
|
||||||
)
|
|
||||||
.with_message("An error occured that forced the program to exit. There may be unexpected side-effects because the program could not finish.")
|
|
||||||
.with_note(note)
|
|
||||||
.with_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message("Error occured here.")
|
|
||||||
),
|
|
||||||
if let RuntimeError::ValidationFailure(validation_error) = error {
|
|
||||||
Some(validation_error)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(validation_error) = validation_error {
|
|
||||||
match validation_error {
|
|
||||||
ValidationError::CannotAssignToNone(postion) => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), postion.0..postion.1))
|
|
||||||
.with_message(
|
|
||||||
"This statement does not yield a value, you cannot assign a variable to it."
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedBoolean { actual, position } => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message(format!(
|
|
||||||
"Expected {} but got {}.",
|
|
||||||
"boolean".fg(type_color),
|
|
||||||
actual.fg(type_color)
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedIntegerOrFloat(position) => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message(format!(
|
|
||||||
"Expected {} or {}.",
|
|
||||||
"integer".fg(type_color),
|
|
||||||
"float".fg(type_color)
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::FullTypeNotKnown {
|
|
||||||
identifier,
|
|
||||||
position,
|
|
||||||
} => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"The full type for {} must be known.",
|
|
||||||
identifier.fg(identifier_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::RwLockPoison(_) => todo!(),
|
|
||||||
ValidationError::TypeCheck {
|
|
||||||
conflict: TypeConflict { actual, expected },
|
|
||||||
actual_position,
|
|
||||||
expected_position,
|
|
||||||
} => {
|
|
||||||
if let Type::Generic {
|
|
||||||
concrete_type: None,
|
|
||||||
..
|
|
||||||
} = actual
|
|
||||||
{
|
|
||||||
builder = builder.with_help("Try specifying the type using turbofish.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let actual_type_message = if let Some(position) = expected_position {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message(format!(
|
|
||||||
"Type {} established here.",
|
|
||||||
expected.fg(type_color)
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
format!("Got type {} here.", actual.fg(type_color))
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"Got type {} but expected {}.",
|
|
||||||
actual.fg(type_color),
|
|
||||||
expected.fg(type_color)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((
|
|
||||||
self.source_id.clone(),
|
|
||||||
actual_position.0..actual_position.1,
|
|
||||||
))
|
|
||||||
.with_message(actual_type_message),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ValidationError::VariableNotFound {
|
|
||||||
identifier,
|
|
||||||
position,
|
|
||||||
} => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"Variable {} does not exist in this context.",
|
|
||||||
identifier.fg(identifier_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!("Cannot index into a {}.", r#type.fg(type_color)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::CannotIndexWith {
|
|
||||||
collection_type,
|
|
||||||
collection_position,
|
|
||||||
index_type,
|
|
||||||
index_position,
|
|
||||||
} => {
|
|
||||||
builder = builder.with_message(format!(
|
|
||||||
"Cannot index into {} with {}.",
|
|
||||||
collection_type.clone().fg(type_color),
|
|
||||||
index_type.clone().fg(type_color)
|
|
||||||
));
|
|
||||||
|
|
||||||
builder.add_labels([
|
|
||||||
Label::new((
|
|
||||||
self.source_id.clone(),
|
|
||||||
collection_position.0..collection_position.1,
|
|
||||||
))
|
|
||||||
.with_message(format!(
|
|
||||||
"This has type {}.",
|
|
||||||
collection_type.fg(type_color),
|
|
||||||
)),
|
|
||||||
Label::new((
|
|
||||||
self.source_id.clone(),
|
|
||||||
index_position.0..index_position.1,
|
|
||||||
))
|
|
||||||
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedValueStatement(position) => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message("Expected a statement that yields a value."),
|
|
||||||
),
|
|
||||||
ValidationError::ExpectedNonValueStatement(position) => {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message("Expected a statement that does not yield a value."),
|
|
||||||
);
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message("Try adding a semicolon here."),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedFunction { actual, position } => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"Expected a function value but got {}.",
|
|
||||||
actual.fg(type_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::FieldNotFound {
|
|
||||||
identifier,
|
|
||||||
position,
|
|
||||||
} => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"This map has no field named {}.",
|
|
||||||
identifier.fg(identifier_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::WrongTypeArguments {
|
|
||||||
parameters,
|
|
||||||
arguments,
|
|
||||||
} => {
|
|
||||||
builder = builder.with_message(format!(
|
|
||||||
"Expected {parameters:?} arguments but got {arguments:?}."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ValidationError::WrongValueArguments {
|
|
||||||
parameters,
|
|
||||||
arguments,
|
|
||||||
} => {
|
|
||||||
builder = builder.with_message(format!(
|
|
||||||
"Expected {parameters:?} arguments but got {arguments:?}."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedIntegerFloatOrString { actual, position } => {
|
|
||||||
builder = builder.with_message(format!(
|
|
||||||
"Expected an {}, {} or {}.",
|
|
||||||
Type::Integer.fg(type_color),
|
|
||||||
Type::Float.fg(type_color),
|
|
||||||
Type::String.fg(type_color)
|
|
||||||
));
|
|
||||||
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message(format!("This has type {}.", actual.fg(type_color),)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ValidationError::ExpectedString { .. } => todo!(),
|
|
||||||
ValidationError::EnumDefinitionNotFound {
|
|
||||||
identifier,
|
|
||||||
position,
|
|
||||||
} => {
|
|
||||||
let message = format!(
|
|
||||||
"The enum {} does not exist in this context.",
|
|
||||||
identifier.fg(identifier_color),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(position) = position {
|
|
||||||
builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1))
|
|
||||||
.with_message(message),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
builder = builder.with_message(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValidationError::EnumVariantNotFound { .. } => todo!(),
|
|
||||||
ValidationError::ExpectedList { .. } => todo!(),
|
|
||||||
ValidationError::BuiltInFunctionFailure(reason) => builder
|
|
||||||
.add_label(Label::new((self.source_id.clone(), 0..0)).with_message(reason)),
|
|
||||||
ValidationError::CannotUsePath(_) => todo!(),
|
|
||||||
ValidationError::Uninitialized => todo!(),
|
|
||||||
ValidationError::WrongTypeArgumentsCount {
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
position,
|
|
||||||
} => builder.add_label(
|
|
||||||
Label::new((self.source_id.clone(), position.0..position.1)).with_message(
|
|
||||||
format!(
|
|
||||||
"Expected {} type arguments but got {}.",
|
|
||||||
expected.fg(type_color),
|
|
||||||
actual.fg(type_color)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValidationError::StructDefinitionNotFound {
|
|
||||||
identifier,
|
|
||||||
position,
|
|
||||||
} => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let report = builder.finish();
|
|
||||||
|
|
||||||
reports.push(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
reports
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::{AbstractNode, SourcePosition},
|
|
||||||
standard_library::std_full_compiled,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_standard_library() {
|
|
||||||
let context = Context::new();
|
|
||||||
|
|
||||||
for abstract_tree in std_full_compiled() {
|
|
||||||
abstract_tree
|
|
||||||
.define_and_validate(&context, true, SourcePosition(0, usize::MAX))
|
|
||||||
.unwrap();
|
|
||||||
abstract_tree.run(&context, true).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
132
dust-lang/src/lex.rs
Normal file
132
dust-lang/src/lex.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use crate::{Identifier, Span, Token};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum LexError {
|
||||||
|
IntegerParseError(std::num::ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::num::ParseIntError> for LexError {
|
||||||
|
fn from(v: std::num::ParseIntError) -> Self {
|
||||||
|
Self::IntegerParseError(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lex(input: &str) -> Result<Vec<(Token, Span)>, LexError> {
|
||||||
|
let mut lexer = Lexer::new(input);
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (token, span) = lexer.next_token()?;
|
||||||
|
let is_eof = matches!(token, Token::Eof);
|
||||||
|
|
||||||
|
tokens.push((token, span));
|
||||||
|
|
||||||
|
if is_eof {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Lexer<'a> {
|
||||||
|
input: &'a str,
|
||||||
|
position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Lexer<'a> {
|
||||||
|
pub fn new(input: &'a str) -> Self {
|
||||||
|
Lexer { input, position: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_char(&mut self) -> Option<char> {
|
||||||
|
self.input[self.position..].chars().next().map(|c| {
|
||||||
|
self.position += c.len_utf8();
|
||||||
|
c
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_token(&mut self) -> Result<(Token, Span), LexError> {
|
||||||
|
self.skip_whitespace();
|
||||||
|
|
||||||
|
let (token, span) = if let Some(c) = self.peek_char() {
|
||||||
|
match c {
|
||||||
|
'0'..='9' => self.lex_number()?,
|
||||||
|
'a'..='z' | 'A'..='Z' => self.lex_identifier()?,
|
||||||
|
'+' => {
|
||||||
|
self.position += 1;
|
||||||
|
(Token::Plus, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'*' => {
|
||||||
|
self.position += 1;
|
||||||
|
(Token::Star, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
self.position += 1;
|
||||||
|
(Token::LeftParenthesis, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
self.position += 1;
|
||||||
|
(Token::RightParenthesis, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'=' => {
|
||||||
|
self.position += 1;
|
||||||
|
(Token::Equal, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
_ => (Token::Eof, (self.position, self.position)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(Token::Eof, (self.position, self.position))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((token, span))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_whitespace(&mut self) {
|
||||||
|
while let Some(c) = self.peek_char() {
|
||||||
|
if c.is_whitespace() {
|
||||||
|
self.next_char();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_char(&self) -> Option<char> {
|
||||||
|
self.input[self.position..].chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex_number(&mut self) -> Result<(Token, Span), LexError> {
|
||||||
|
let start_pos = self.position;
|
||||||
|
|
||||||
|
while let Some(c) = self.peek_char() {
|
||||||
|
if c.is_ascii_digit() {
|
||||||
|
self.next_char();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let integer = self.input[start_pos..self.position].parse::<i64>()?;
|
||||||
|
|
||||||
|
Ok((Token::Integer(integer), (start_pos, self.position)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex_identifier(&mut self) -> Result<(Token, Span), LexError> {
|
||||||
|
let start_pos = self.position;
|
||||||
|
|
||||||
|
while let Some(c) = self.peek_char() {
|
||||||
|
if c.is_ascii_alphanumeric() {
|
||||||
|
self.next_char();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let identifier = &self.input[start_pos..self.position];
|
||||||
|
let token = Token::Identifier(Identifier::new(identifier));
|
||||||
|
|
||||||
|
Ok((token, (start_pos, self.position)))
|
||||||
|
}
|
||||||
|
}
|
@ -1,573 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use chumsky::prelude::*;
|
|
||||||
|
|
||||||
use crate::error::DustError;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum Token<'src> {
|
|
||||||
Boolean(bool),
|
|
||||||
Comment(&'src str),
|
|
||||||
Integer(i64),
|
|
||||||
Float(f64),
|
|
||||||
String(&'src str),
|
|
||||||
Identifier(&'src str),
|
|
||||||
Symbol(Symbol),
|
|
||||||
Keyword(Keyword),
|
|
||||||
Use(&'src str),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> Display for Token<'src> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Token::Boolean(boolean) => write!(f, "{boolean}"),
|
|
||||||
Token::Comment(comment) => write!(f, "// {comment}"),
|
|
||||||
Token::Integer(integer) => write!(f, "{integer}"),
|
|
||||||
Token::Float(float) => write!(f, "{float}"),
|
|
||||||
Token::String(string) => write!(f, "{string}"),
|
|
||||||
Token::Identifier(string) => write!(f, "{string}"),
|
|
||||||
Token::Symbol(control) => write!(f, "{control}"),
|
|
||||||
Token::Keyword(keyword) => write!(f, "{keyword}"),
|
|
||||||
Token::Use(path) => write!(f, "use {path}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum Keyword {
|
|
||||||
Any,
|
|
||||||
As,
|
|
||||||
Async,
|
|
||||||
Bool,
|
|
||||||
Break,
|
|
||||||
Else,
|
|
||||||
Enum,
|
|
||||||
Float,
|
|
||||||
Fn,
|
|
||||||
Int,
|
|
||||||
If,
|
|
||||||
JsonParse,
|
|
||||||
Length,
|
|
||||||
Map,
|
|
||||||
None,
|
|
||||||
Range,
|
|
||||||
ReadFile,
|
|
||||||
ReadLine,
|
|
||||||
Sleep,
|
|
||||||
Struct,
|
|
||||||
Str,
|
|
||||||
Type,
|
|
||||||
Loop,
|
|
||||||
While,
|
|
||||||
WriteLine,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Keyword {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Keyword::Any => write!(f, "any"),
|
|
||||||
Keyword::As => write!(f, "as"),
|
|
||||||
Keyword::Async => write!(f, "async"),
|
|
||||||
Keyword::Bool => write!(f, "bool"),
|
|
||||||
Keyword::Break => write!(f, "break"),
|
|
||||||
Keyword::Else => write!(f, "else"),
|
|
||||||
Keyword::Enum => write!(f, "enum"),
|
|
||||||
Keyword::Float => write!(f, "float"),
|
|
||||||
Keyword::Fn => write!(f, "fn"),
|
|
||||||
Keyword::Int => write!(f, "int"),
|
|
||||||
Keyword::If => write!(f, "if"),
|
|
||||||
Keyword::Map => write!(f, "map"),
|
|
||||||
Keyword::None => write!(f, "none"),
|
|
||||||
Keyword::Range => write!(f, "range"),
|
|
||||||
Keyword::Struct => write!(f, "struct"),
|
|
||||||
Keyword::Str => write!(f, "str"),
|
|
||||||
Keyword::Loop => write!(f, "loop"),
|
|
||||||
Keyword::While => write!(f, "while"),
|
|
||||||
Keyword::Type => write!(f, "type"),
|
|
||||||
Keyword::JsonParse => write!(f, "JSON_PARSE"),
|
|
||||||
Keyword::Length => write!(f, "LENGTH"),
|
|
||||||
Keyword::ReadFile => write!(f, "READ_FILE"),
|
|
||||||
Keyword::ReadLine => write!(f, "READ_LINE"),
|
|
||||||
Keyword::Sleep => write!(f, "SLEEP"),
|
|
||||||
Keyword::WriteLine => write!(f, "WRITE_LINE"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub enum Symbol {
|
|
||||||
Plus,
|
|
||||||
PlusEqual,
|
|
||||||
DoubleAmpersand,
|
|
||||||
Colon,
|
|
||||||
Comma,
|
|
||||||
CurlyClose,
|
|
||||||
CurlyOpen,
|
|
||||||
Slash,
|
|
||||||
Dollar,
|
|
||||||
Dot,
|
|
||||||
DoubleColon,
|
|
||||||
DoubleDot,
|
|
||||||
DoubleEqual,
|
|
||||||
DoubleUnderscore,
|
|
||||||
Equal,
|
|
||||||
FatArrow,
|
|
||||||
Greater,
|
|
||||||
GreaterOrEqual,
|
|
||||||
Less,
|
|
||||||
LessOrEqual,
|
|
||||||
Percent,
|
|
||||||
Asterisk,
|
|
||||||
Exclamation,
|
|
||||||
NotEqual,
|
|
||||||
DoublePipe,
|
|
||||||
ParenClose,
|
|
||||||
ParenOpen,
|
|
||||||
Pipe,
|
|
||||||
Semicolon,
|
|
||||||
SkinnyArrow,
|
|
||||||
SquareClose,
|
|
||||||
SquareOpen,
|
|
||||||
MinusEqual,
|
|
||||||
Minus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Symbol {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Symbol::Asterisk => write!(f, "*"),
|
|
||||||
Symbol::Colon => write!(f, ":"),
|
|
||||||
Symbol::Comma => write!(f, ","),
|
|
||||||
Symbol::CurlyClose => write!(f, "}}"),
|
|
||||||
Symbol::CurlyOpen => write!(f, "{{"),
|
|
||||||
Symbol::Dollar => write!(f, "$"),
|
|
||||||
Symbol::Dot => write!(f, "."),
|
|
||||||
Symbol::DoubleAmpersand => write!(f, "&&"),
|
|
||||||
Symbol::DoubleColon => write!(f, "::"),
|
|
||||||
Symbol::DoubleDot => write!(f, ".."),
|
|
||||||
Symbol::DoubleEqual => write!(f, "=="),
|
|
||||||
Symbol::DoublePipe => write!(f, "||"),
|
|
||||||
Symbol::DoubleUnderscore => write!(f, "__"),
|
|
||||||
Symbol::Equal => write!(f, "="),
|
|
||||||
Symbol::Exclamation => write!(f, "!"),
|
|
||||||
Symbol::FatArrow => write!(f, "=>"),
|
|
||||||
Symbol::Greater => write!(f, ">"),
|
|
||||||
Symbol::GreaterOrEqual => write!(f, ">="),
|
|
||||||
Symbol::Less => write!(f, "<"),
|
|
||||||
Symbol::LessOrEqual => write!(f, "<="),
|
|
||||||
Symbol::Minus => write!(f, "-"),
|
|
||||||
Symbol::MinusEqual => write!(f, "-="),
|
|
||||||
Symbol::NotEqual => write!(f, "!="),
|
|
||||||
Symbol::ParenClose => write!(f, ")"),
|
|
||||||
Symbol::ParenOpen => write!(f, "("),
|
|
||||||
Symbol::Percent => write!(f, "%"),
|
|
||||||
Symbol::Pipe => write!(f, "|"),
|
|
||||||
Symbol::Plus => write!(f, "+"),
|
|
||||||
Symbol::PlusEqual => write!(f, "+="),
|
|
||||||
Symbol::Semicolon => write!(f, ";"),
|
|
||||||
Symbol::SkinnyArrow => write!(f, "->"),
|
|
||||||
Symbol::Slash => write!(f, "/"),
|
|
||||||
Symbol::SquareClose => write!(f, "]"),
|
|
||||||
Symbol::SquareOpen => write!(f, "["),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lex(source: &str) -> Result<Vec<(Token, SimpleSpan)>, Vec<DustError>> {
|
|
||||||
lexer()
|
|
||||||
.parse(source)
|
|
||||||
.into_result()
|
|
||||||
.map_err(|errors| errors.into_iter().map(|error| error.into()).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lexer<'src>() -> impl Parser<
|
|
||||||
'src,
|
|
||||||
&'src str,
|
|
||||||
Vec<(Token<'src>, SimpleSpan<usize>)>,
|
|
||||||
extra::Err<Rich<'src, char, SimpleSpan<usize>>>,
|
|
||||||
> {
|
|
||||||
let line_comment = just("//")
|
|
||||||
.ignore_then(
|
|
||||||
none_of('\n')
|
|
||||||
.repeated()
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| Token::Comment(text.trim())),
|
|
||||||
)
|
|
||||||
.then_ignore(just('\n').or_not());
|
|
||||||
|
|
||||||
let multi_line_comment = just("/*")
|
|
||||||
.ignore_then(
|
|
||||||
none_of('*')
|
|
||||||
.repeated()
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| Token::Comment(text.trim())),
|
|
||||||
)
|
|
||||||
.then_ignore(just("*/"));
|
|
||||||
|
|
||||||
let boolean = choice((
|
|
||||||
just("true").to(Token::Boolean(true)),
|
|
||||||
just("false").to(Token::Boolean(false)),
|
|
||||||
));
|
|
||||||
|
|
||||||
let float_numeric = just('-')
|
|
||||||
.or_not()
|
|
||||||
.then(text::int(10))
|
|
||||||
.then(just('.').then(text::digits(10)))
|
|
||||||
.then(just('e').then(text::digits(10)).or_not())
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| Token::Float(text.parse().unwrap()));
|
|
||||||
|
|
||||||
let float = choice((
|
|
||||||
float_numeric,
|
|
||||||
just("Infinity").to(Token::Float(f64::INFINITY)),
|
|
||||||
just("-Infinity").to(Token::Float(f64::NEG_INFINITY)),
|
|
||||||
just("NaN").to(Token::Float(f64::NAN)),
|
|
||||||
));
|
|
||||||
|
|
||||||
let integer = just('-')
|
|
||||||
.or_not()
|
|
||||||
.then(text::int(10))
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| {
|
|
||||||
let integer = text.parse().unwrap();
|
|
||||||
|
|
||||||
Token::Integer(integer)
|
|
||||||
});
|
|
||||||
|
|
||||||
let delimited_string = |delimiter| {
|
|
||||||
just(delimiter)
|
|
||||||
.then(none_of(delimiter).repeated())
|
|
||||||
.then(just(delimiter))
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| Token::String(&text[1..text.len() - 1]))
|
|
||||||
};
|
|
||||||
|
|
||||||
let string = choice((
|
|
||||||
delimited_string('\''),
|
|
||||||
delimited_string('"'),
|
|
||||||
delimited_string('`'),
|
|
||||||
));
|
|
||||||
|
|
||||||
let keyword = choice((
|
|
||||||
just("any").to(Token::Keyword(Keyword::Any)),
|
|
||||||
just("async").to(Token::Keyword(Keyword::Async)),
|
|
||||||
just("as").to(Token::Keyword(Keyword::As)),
|
|
||||||
just("bool").to(Token::Keyword(Keyword::Bool)),
|
|
||||||
just("break").to(Token::Keyword(Keyword::Break)),
|
|
||||||
just("enum").to(Token::Keyword(Keyword::Enum)),
|
|
||||||
just("else").to(Token::Keyword(Keyword::Else)),
|
|
||||||
just("float").to(Token::Keyword(Keyword::Float)),
|
|
||||||
just("fn").to(Token::Keyword(Keyword::Fn)),
|
|
||||||
just("int").to(Token::Keyword(Keyword::Int)),
|
|
||||||
just("if").to(Token::Keyword(Keyword::If)),
|
|
||||||
just("map").to(Token::Keyword(Keyword::Map)),
|
|
||||||
just("none").to(Token::Keyword(Keyword::None)),
|
|
||||||
just("range").to(Token::Keyword(Keyword::Range)),
|
|
||||||
just("struct").to(Token::Keyword(Keyword::Struct)),
|
|
||||||
just("str").to(Token::Keyword(Keyword::Str)),
|
|
||||||
just("type").to(Token::Keyword(Keyword::Type)),
|
|
||||||
just("loop").to(Token::Keyword(Keyword::Loop)),
|
|
||||||
just("while").to(Token::Keyword(Keyword::While)),
|
|
||||||
just("JSON_PARSE").to(Token::Keyword(Keyword::JsonParse)),
|
|
||||||
just("LENGTH").to(Token::Keyword(Keyword::Length)),
|
|
||||||
just("READ_FILE").to(Token::Keyword(Keyword::ReadFile)),
|
|
||||||
just("READ_LINE").to(Token::Keyword(Keyword::ReadLine)),
|
|
||||||
just("SLEEP").to(Token::Keyword(Keyword::Sleep)),
|
|
||||||
just("WRITE_LINE").to(Token::Keyword(Keyword::WriteLine)),
|
|
||||||
));
|
|
||||||
|
|
||||||
let symbol = choice([
|
|
||||||
just("!=").to(Token::Symbol(Symbol::NotEqual)),
|
|
||||||
just("!").to(Token::Symbol(Symbol::Exclamation)),
|
|
||||||
just("$").to(Token::Symbol(Symbol::Dollar)),
|
|
||||||
just("%").to(Token::Symbol(Symbol::Percent)),
|
|
||||||
just("&&").to(Token::Symbol(Symbol::DoubleAmpersand)),
|
|
||||||
just("(").to(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(")").to(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
just("*").to(Token::Symbol(Symbol::Asterisk)),
|
|
||||||
just("+=").to(Token::Symbol(Symbol::PlusEqual)),
|
|
||||||
just("+").to(Token::Symbol(Symbol::Plus)),
|
|
||||||
just(",").to(Token::Symbol(Symbol::Comma)),
|
|
||||||
just("->").to(Token::Symbol(Symbol::SkinnyArrow)),
|
|
||||||
just("-=").to(Token::Symbol(Symbol::MinusEqual)),
|
|
||||||
just("-").to(Token::Symbol(Symbol::Minus)),
|
|
||||||
just("..").to(Token::Symbol(Symbol::DoubleDot)),
|
|
||||||
just(".").to(Token::Symbol(Symbol::Dot)),
|
|
||||||
just("/").to(Token::Symbol(Symbol::Slash)),
|
|
||||||
just("::").to(Token::Symbol(Symbol::DoubleColon)),
|
|
||||||
just(":").to(Token::Symbol(Symbol::Colon)),
|
|
||||||
just(";").to(Token::Symbol(Symbol::Semicolon)),
|
|
||||||
just("<=").to(Token::Symbol(Symbol::LessOrEqual)),
|
|
||||||
just("<").to(Token::Symbol(Symbol::Less)),
|
|
||||||
just("=>").to(Token::Symbol(Symbol::FatArrow)),
|
|
||||||
just("==").to(Token::Symbol(Symbol::DoubleEqual)),
|
|
||||||
just("=").to(Token::Symbol(Symbol::Equal)),
|
|
||||||
just(">=").to(Token::Symbol(Symbol::GreaterOrEqual)),
|
|
||||||
just(">").to(Token::Symbol(Symbol::Greater)),
|
|
||||||
just("[").to(Token::Symbol(Symbol::SquareOpen)),
|
|
||||||
just("]").to(Token::Symbol(Symbol::SquareClose)),
|
|
||||||
just("__").to(Token::Symbol(Symbol::DoubleUnderscore)),
|
|
||||||
just("{").to(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just("}").to(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
just("||").to(Token::Symbol(Symbol::DoublePipe)),
|
|
||||||
just("|").to(Token::Symbol(Symbol::Pipe)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let identifier = text::ident().map(|text: &str| Token::Identifier(text));
|
|
||||||
|
|
||||||
let r#use = just("use").padded().ignore_then(
|
|
||||||
none_of(" \n\r;")
|
|
||||||
.repeated()
|
|
||||||
.to_slice()
|
|
||||||
.map(|text: &str| Token::Use(text.trim())),
|
|
||||||
);
|
|
||||||
|
|
||||||
choice((
|
|
||||||
line_comment,
|
|
||||||
multi_line_comment,
|
|
||||||
boolean,
|
|
||||||
float,
|
|
||||||
integer,
|
|
||||||
string,
|
|
||||||
keyword,
|
|
||||||
symbol,
|
|
||||||
r#use,
|
|
||||||
identifier,
|
|
||||||
))
|
|
||||||
.map_with(|token: Token, state| (token, state.span()))
|
|
||||||
.padded()
|
|
||||||
.repeated()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#use() {
|
|
||||||
assert_eq!(
|
|
||||||
lex("use std.io").unwrap(),
|
|
||||||
vec![(Token::Use("std.io"), (0..10).into())]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex("use https://example.com/std.ds").unwrap(),
|
|
||||||
vec![(Token::Use("https://example.com/std.ds"), (0..30).into())]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn line_comment() {
|
|
||||||
assert_eq!(
|
|
||||||
lex("// 42").unwrap(),
|
|
||||||
vec![(Token::Comment("42"), (0..5).into())]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex("1// 42//2").unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (0..1).into()),
|
|
||||||
(Token::Comment("42//2"), (1..9).into()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
lex("
|
|
||||||
1
|
|
||||||
// 42
|
|
||||||
2
|
|
||||||
")
|
|
||||||
.unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (17..18).into()),
|
|
||||||
(Token::Comment("42"), (35..41).into()),
|
|
||||||
(Token::Integer(2), (57..58).into()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multi_line_comment() {
|
|
||||||
assert_eq!(
|
|
||||||
lex("/* 42 */").unwrap(),
|
|
||||||
vec![(Token::Comment("42"), (0..8).into())]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex("1/* 42//2 */").unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (0..1).into()),
|
|
||||||
(Token::Comment("42//2"), (1..12).into()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
lex("
|
|
||||||
1
|
|
||||||
/*
|
|
||||||
42
|
|
||||||
*/
|
|
||||||
2
|
|
||||||
")
|
|
||||||
.unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (17..18).into()),
|
|
||||||
(Token::Comment("42"), (35..79).into()),
|
|
||||||
(Token::Integer(2), (96..97).into()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn range() {
|
|
||||||
assert_eq!(
|
|
||||||
lex("1..10").unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (0..1).into()),
|
|
||||||
(Token::Symbol(Symbol::DoubleDot), (1..3).into()),
|
|
||||||
(Token::Integer(10), (3..5).into())
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn math_operators() {
|
|
||||||
assert_eq!(
|
|
||||||
lex("1 + 1").unwrap(),
|
|
||||||
vec![
|
|
||||||
(Token::Integer(1), (0..1).into()),
|
|
||||||
(Token::Symbol(Symbol::Plus), (2..3).into()),
|
|
||||||
(Token::Integer(1), (4..5).into())
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn keywords() {
|
|
||||||
assert_eq!(lex("int").unwrap()[0].0, Token::Keyword(Keyword::Int))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn identifier() {
|
|
||||||
assert_eq!(lex("x").unwrap()[0].0, Token::Identifier("x"));
|
|
||||||
assert_eq!(lex("foobar").unwrap()[0].0, Token::Identifier("foobar"));
|
|
||||||
assert_eq!(lex("HELLO").unwrap()[0].0, Token::Identifier("HELLO"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#true() {
|
|
||||||
assert_eq!(lex("true").unwrap()[0].0, Token::Boolean(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#false() {
|
|
||||||
assert_eq!(lex("false").unwrap()[0].0, Token::Boolean(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn positive_float() {
|
|
||||||
assert_eq!(lex("0.0").unwrap()[0].0, Token::Float(0.0));
|
|
||||||
assert_eq!(lex("42.0").unwrap()[0].0, Token::Float(42.0));
|
|
||||||
|
|
||||||
let max_float = f64::MAX.to_string() + ".0";
|
|
||||||
|
|
||||||
assert_eq!(lex(&max_float).unwrap()[0].0, Token::Float(f64::MAX));
|
|
||||||
|
|
||||||
let min_positive_float = f64::MIN_POSITIVE.to_string();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(&min_positive_float).unwrap()[0].0,
|
|
||||||
Token::Float(f64::MIN_POSITIVE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn negative_float() {
|
|
||||||
assert_eq!(lex("-0.0").unwrap()[0].0, Token::Float(-0.0));
|
|
||||||
assert_eq!(lex("-42.0").unwrap()[0].0, Token::Float(-42.0));
|
|
||||||
|
|
||||||
let min_float = f64::MIN.to_string() + ".0";
|
|
||||||
|
|
||||||
assert_eq!(lex(&min_float).unwrap()[0].0, Token::Float(f64::MIN));
|
|
||||||
|
|
||||||
let max_negative_float = format!("-{}", f64::MIN_POSITIVE);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(&max_negative_float).unwrap()[0].0,
|
|
||||||
Token::Float(-f64::MIN_POSITIVE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn other_float() {
|
|
||||||
assert_eq!(lex("Infinity").unwrap()[0].0, Token::Float(f64::INFINITY));
|
|
||||||
assert_eq!(
|
|
||||||
lex("-Infinity").unwrap()[0].0,
|
|
||||||
Token::Float(f64::NEG_INFINITY)
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Token::Float(float) = &lex("NaN").unwrap()[0].0 {
|
|
||||||
assert!(float.is_nan());
|
|
||||||
} else {
|
|
||||||
panic!("Expected a float.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn positive_integer() {
|
|
||||||
for i in 0..10 {
|
|
||||||
let source = i.to_string();
|
|
||||||
let tokens = lex(&source).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(tokens[0].0, Token::Integer(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(lex("42").unwrap()[0].0, Token::Integer(42));
|
|
||||||
|
|
||||||
let maximum_integer = i64::MAX.to_string();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(&maximum_integer).unwrap()[0].0,
|
|
||||||
Token::Integer(i64::MAX)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn negative_integer() {
|
|
||||||
for i in -9..1 {
|
|
||||||
let source = i.to_string();
|
|
||||||
let tokens = lex(&source).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(tokens[0].0, Token::Integer(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(lex("-42").unwrap()[0].0, Token::Integer(-42));
|
|
||||||
|
|
||||||
let minimum_integer = i64::MIN.to_string();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
lex(&minimum_integer).unwrap()[0].0,
|
|
||||||
Token::Integer(i64::MIN)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn double_quoted_string() {
|
|
||||||
assert_eq!(lex("\"\"").unwrap()[0].0, Token::String(""));
|
|
||||||
assert_eq!(lex("\"42\"").unwrap()[0].0, Token::String("42"));
|
|
||||||
assert_eq!(lex("\"foobar\"").unwrap()[0].0, Token::String("foobar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_quoted_string() {
|
|
||||||
assert_eq!(lex("''").unwrap()[0].0, Token::String(""));
|
|
||||||
assert_eq!(lex("'42'").unwrap()[0].0, Token::String("42"));
|
|
||||||
assert_eq!(lex("'foobar'").unwrap()[0].0, Token::String("foobar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn grave_quoted_string() {
|
|
||||||
assert_eq!(lex("``").unwrap()[0].0, Token::String(""));
|
|
||||||
assert_eq!(lex("`42`").unwrap()[0].0, Token::String("42"));
|
|
||||||
assert_eq!(lex("`foobar`").unwrap()[0].0, Token::String("foobar"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,21 +7,16 @@ The [interpreter] module contains the `Interpreter` struct, which is used to lex
|
|||||||
interpret Dust code. The `interpret` function is a convenience function that creates a new
|
interpret Dust code. The `interpret` function is a convenience function that creates a new
|
||||||
`Interpreter` and runs the given source code.
|
`Interpreter` and runs the given source code.
|
||||||
*/
|
*/
|
||||||
pub mod abstract_tree;
|
|
||||||
pub mod bytecode;
|
|
||||||
pub mod context;
|
|
||||||
pub mod error;
|
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod interpreter;
|
pub mod lex;
|
||||||
pub mod lexer;
|
pub mod parse;
|
||||||
pub mod parser;
|
pub mod token;
|
||||||
pub mod standard_library;
|
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
pub use context::Context;
|
pub use identifier::Identifier;
|
||||||
pub use interpreter::{interpret, Interpreter, InterpreterError};
|
|
||||||
pub use lexer::{lex, lexer};
|
|
||||||
pub use parser::{parse, parser};
|
|
||||||
pub use r#type::Type;
|
pub use r#type::Type;
|
||||||
|
pub use token::Token;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
|
|
||||||
|
pub type Span = (usize, usize);
|
||||||
|
243
dust-lang/src/parse.rs
Normal file
243
dust-lang/src/parse.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use crate::{
|
||||||
|
identifier::Identifier,
|
||||||
|
lex::{LexError, Lexer},
|
||||||
|
Span, Token, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Instruction {
|
||||||
|
Add(Box<(Instruction, Instruction)>),
|
||||||
|
Assign(Box<(Instruction, Instruction)>),
|
||||||
|
Constant(Value),
|
||||||
|
Identifier(Identifier),
|
||||||
|
Multiply(Box<(Instruction, Instruction)>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum ParseError {
|
||||||
|
LexError(LexError),
|
||||||
|
ExpectedClosingParenthesis,
|
||||||
|
UnexpectedToken(Token),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LexError> for ParseError {
|
||||||
|
fn from(v: LexError) -> Self {
|
||||||
|
Self::LexError(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<'src> {
|
||||||
|
lexer: Lexer<'src>,
|
||||||
|
current: (Token, Span),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Parser<'src> {
|
||||||
|
pub fn new(lexer: Lexer<'src>) -> Self {
|
||||||
|
let mut lexer = lexer;
|
||||||
|
let current_token = lexer
|
||||||
|
.next_token()
|
||||||
|
.map(|(token, _)| token)
|
||||||
|
.unwrap_or(Token::Eof);
|
||||||
|
|
||||||
|
Parser {
|
||||||
|
lexer,
|
||||||
|
current: (current_token, (0, 0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Result<(Instruction, Span), ParseError> {
|
||||||
|
self.parse_instruction(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token(&mut self) -> Result<(), ParseError> {
|
||||||
|
self.current = self.lexer.next_token()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_instruction(&mut self, precedence: u8) -> Result<(Instruction, Span), ParseError> {
|
||||||
|
let (left_instruction, left_span) = self.parse_primary()?;
|
||||||
|
|
||||||
|
if precedence < self.current_precedence() {
|
||||||
|
match &self.current {
|
||||||
|
(Token::Plus, _) => {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
let (right_instruction, right_span) =
|
||||||
|
self.parse_instruction(self.current_precedence())?;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
Instruction::Add(Box::new((left_instruction, right_instruction))),
|
||||||
|
(left_span.0, right_span.1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Token::Star, _) => {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
let (right_instruction, right_span) =
|
||||||
|
self.parse_instruction(self.current_precedence())?;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
Instruction::Multiply(Box::new((left_instruction, right_instruction))),
|
||||||
|
(left_span.0, right_span.1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
(Token::Equal, _) => {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
let (right_instruction, right_span) =
|
||||||
|
self.parse_instruction(self.current_precedence())?;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
Instruction::Assign(Box::new((left_instruction, right_instruction))),
|
||||||
|
(left_span.0, right_span.1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((left_instruction, left_span))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_primary(&mut self) -> Result<(Instruction, Span), ParseError> {
|
||||||
|
match self.current.clone() {
|
||||||
|
(Token::Integer(int), span) => {
|
||||||
|
self.next_token()?;
|
||||||
|
Ok((Instruction::Constant(Value::integer(int)), span))
|
||||||
|
}
|
||||||
|
(Token::Identifier(identifier), span) => {
|
||||||
|
self.next_token()?;
|
||||||
|
Ok((Instruction::Identifier(identifier), span))
|
||||||
|
}
|
||||||
|
(Token::LeftParenthesis, left_span) => {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
let (instruction, _) = self.parse_instruction(0)?;
|
||||||
|
|
||||||
|
if let (Token::RightParenthesis, right_span) = self.current {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
|
Ok((instruction, (left_span.0, right_span.1)))
|
||||||
|
} else {
|
||||||
|
Err(ParseError::ExpectedClosingParenthesis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(ParseError::UnexpectedToken(self.current.0.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_precedence(&self) -> u8 {
|
||||||
|
match self.current {
|
||||||
|
(Token::Equal, _) => 3,
|
||||||
|
(Token::Plus, _) => 1,
|
||||||
|
(Token::Star, _) => 2,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{identifier::Identifier, lex::lex, Value};
|
||||||
|
|
||||||
|
use super::{Instruction, Lexer, Parser, Token};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add() {
|
||||||
|
let input = "1 + 2";
|
||||||
|
let lexer = Lexer::new(input);
|
||||||
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(),
|
||||||
|
Ok((
|
||||||
|
Instruction::Add(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(1)),
|
||||||
|
Instruction::Constant(Value::integer(2))
|
||||||
|
))),
|
||||||
|
(0, 5)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiply() {
|
||||||
|
let input = "1 * 2";
|
||||||
|
let lexer = Lexer::new(input);
|
||||||
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(),
|
||||||
|
Ok((
|
||||||
|
Instruction::Multiply(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(1)),
|
||||||
|
Instruction::Constant(Value::integer(2))
|
||||||
|
))),
|
||||||
|
(0, 5)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_and_multiply() {
|
||||||
|
let input = "1 + 2 * 3";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::Integer(1), (0, 1)),
|
||||||
|
(Token::Plus, (2, 3)),
|
||||||
|
(Token::Integer(2), (4, 5)),
|
||||||
|
(Token::Star, (6, 7)),
|
||||||
|
(Token::Integer(3), (8, 9)),
|
||||||
|
(Token::Eof, (9, 9)),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parser() {
|
||||||
|
let input = "1 + 2 * 3";
|
||||||
|
let lexer = Lexer::new(input);
|
||||||
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(),
|
||||||
|
Ok((
|
||||||
|
Instruction::Add(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(1)),
|
||||||
|
Instruction::Multiply(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(2)),
|
||||||
|
Instruction::Constant(Value::integer(3))
|
||||||
|
)))
|
||||||
|
))),
|
||||||
|
(0, 9)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assignment() {
|
||||||
|
let input = "a = 1 + 2 * 3";
|
||||||
|
let lexer = Lexer::new(input);
|
||||||
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(),
|
||||||
|
Ok((
|
||||||
|
Instruction::Assign(Box::new((
|
||||||
|
Instruction::Identifier(Identifier::new("a")),
|
||||||
|
Instruction::Add(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(1)),
|
||||||
|
Instruction::Multiply(Box::new((
|
||||||
|
Instruction::Constant(Value::integer(2)),
|
||||||
|
Instruction::Constant(Value::integer(3))
|
||||||
|
)))
|
||||||
|
)))
|
||||||
|
))),
|
||||||
|
(0, 13)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,781 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use chumsky::{input::SpannedInput, pratt::*, prelude::*};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::*,
|
|
||||||
error::DustError,
|
|
||||||
identifier::Identifier,
|
|
||||||
lexer::{Keyword, Symbol, Token},
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::{
|
|
||||||
built_in_function::BuiltInFunction,
|
|
||||||
enum_declaration::EnumVariant,
|
|
||||||
type_constructor::{RawTypeConstructor, TypeInvokationConstructor},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type ParserInput<'src> =
|
|
||||||
SpannedInput<Token<'src>, SimpleSpan, &'src [(Token<'src>, SimpleSpan)]>;
|
|
||||||
|
|
||||||
pub type ParserExtra<'src> = extra::Err<Rich<'src, Token<'src>, SimpleSpan>>;
|
|
||||||
|
|
||||||
pub fn parse<'src>(
|
|
||||||
tokens: &'src [(Token<'src>, SimpleSpan)],
|
|
||||||
) -> Result<AbstractTree, Vec<DustError>> {
|
|
||||||
parser(false)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.map_err(|errors| {
|
|
||||||
errors
|
|
||||||
.into_iter()
|
|
||||||
.map(DustError::from)
|
|
||||||
.collect::<Vec<DustError>>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parser<'src>(
|
|
||||||
allow_built_ins: bool,
|
|
||||||
) -> impl Parser<'src, ParserInput<'src>, AbstractTree, ParserExtra<'src>> {
|
|
||||||
let comment = select_ref! {
|
|
||||||
Token::Comment(_) => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let identifier = select! {
|
|
||||||
Token::Identifier(text) => Identifier::from(text),
|
|
||||||
};
|
|
||||||
|
|
||||||
let positioned_identifier =
|
|
||||||
identifier.map_with(|identifier, state| identifier.with_position(state.span()));
|
|
||||||
|
|
||||||
let basic_value = select! {
|
|
||||||
Token::Boolean(boolean) => ValueNode::Boolean(boolean),
|
|
||||||
Token::Float(float) => ValueNode::Float(float),
|
|
||||||
Token::Integer(integer) => ValueNode::Integer(integer),
|
|
||||||
Token::String(text) => ValueNode::String(text.to_string()),
|
|
||||||
}
|
|
||||||
.map_with(|value, state| Expression::Value(value.with_position(state.span())));
|
|
||||||
|
|
||||||
let raw_integer = select! {
|
|
||||||
Token::Integer(integer) => integer
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_constructor = recursive(|type_constructor| {
|
|
||||||
let primitive_type = choice((
|
|
||||||
just(Token::Keyword(Keyword::Any)).to(RawTypeConstructor::Any),
|
|
||||||
just(Token::Keyword(Keyword::Bool)).to(RawTypeConstructor::Boolean),
|
|
||||||
just(Token::Keyword(Keyword::Float)).to(RawTypeConstructor::Float),
|
|
||||||
just(Token::Keyword(Keyword::Int)).to(RawTypeConstructor::Integer),
|
|
||||||
just(Token::Keyword(Keyword::Range)).to(RawTypeConstructor::Range),
|
|
||||||
just(Token::Keyword(Keyword::Str)).to(RawTypeConstructor::String),
|
|
||||||
))
|
|
||||||
.map_with(|raw_constructor, state| {
|
|
||||||
TypeConstructor::Raw(raw_constructor.with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
type TypeParameters = Vec<WithPosition<Identifier>>;
|
|
||||||
type ValueParameters = Vec<(WithPosition<Identifier>, TypeConstructor)>;
|
|
||||||
type FunctionTypeParameters = (Option<TypeParameters>, ValueParameters);
|
|
||||||
|
|
||||||
let function_type = just(Token::Keyword(Keyword::Fn))
|
|
||||||
.ignore_then(
|
|
||||||
positioned_identifier
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::Less)),
|
|
||||||
just(Token::Symbol(Symbol::Greater)),
|
|
||||||
)
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
positioned_identifier
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Colon)))
|
|
||||||
.then(type_constructor.clone())
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
just(Token::Symbol(Symbol::SkinnyArrow))
|
|
||||||
.ignore_then(type_constructor.clone())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map_with(
|
|
||||||
|((type_parameters, value_parameters), return_type): (
|
|
||||||
FunctionTypeParameters,
|
|
||||||
Option<TypeConstructor>,
|
|
||||||
),
|
|
||||||
state| {
|
|
||||||
let value_parameters = if value_parameters.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value_parameters)
|
|
||||||
};
|
|
||||||
|
|
||||||
TypeConstructor::Function(
|
|
||||||
FunctionTypeConstructor {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type: return_type.map(Box::new),
|
|
||||||
}
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let list_type = type_constructor
|
|
||||||
.clone()
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Semicolon)))
|
|
||||||
.then(raw_integer)
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::SquareOpen)),
|
|
||||||
just(Token::Symbol(Symbol::SquareClose)),
|
|
||||||
)
|
|
||||||
.map_with(|(item_type, length), state| {
|
|
||||||
TypeConstructor::List(
|
|
||||||
ListTypeConstructor {
|
|
||||||
length: length as usize,
|
|
||||||
item_type: Box::new(item_type),
|
|
||||||
}
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let list_of_type = type_constructor
|
|
||||||
.clone()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::SquareOpen)),
|
|
||||||
just(Token::Symbol(Symbol::SquareClose)),
|
|
||||||
)
|
|
||||||
.map_with(|item_type, state| {
|
|
||||||
TypeConstructor::ListOf(Box::new(item_type).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let type_invokation = positioned_identifier
|
|
||||||
.then(
|
|
||||||
type_constructor
|
|
||||||
.clone()
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.at_least(1)
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
)
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map(|(identifier, type_arguments)| {
|
|
||||||
TypeConstructor::Invokation(TypeInvokationConstructor {
|
|
||||||
identifier,
|
|
||||||
type_arguments,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let map_type = positioned_identifier
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Colon)))
|
|
||||||
.then(type_constructor.clone())
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
)
|
|
||||||
.map_with(
|
|
||||||
|fields: Vec<(WithPosition<Identifier>, TypeConstructor)>, state| {
|
|
||||||
TypeConstructor::Map(fields.with_position(state.span()))
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
choice((
|
|
||||||
map_type,
|
|
||||||
type_invokation,
|
|
||||||
function_type,
|
|
||||||
list_type,
|
|
||||||
list_of_type,
|
|
||||||
primitive_type,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
let type_specification =
|
|
||||||
just(Token::Symbol(Symbol::Colon)).ignore_then(type_constructor.clone());
|
|
||||||
|
|
||||||
let statement = recursive(|statement| {
|
|
||||||
let block = statement
|
|
||||||
.clone()
|
|
||||||
.repeated()
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
)
|
|
||||||
.map_with(|statements, state| Block::new(statements).with_position(state.span()));
|
|
||||||
|
|
||||||
let expression = recursive(|expression| {
|
|
||||||
let identifier_expression = identifier.map_with(|identifier, state| {
|
|
||||||
Expression::Identifier(identifier.with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let range = raw_integer
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::DoubleDot)))
|
|
||||||
.then(raw_integer)
|
|
||||||
.map_with(|(start, end), state| {
|
|
||||||
Expression::Value(ValueNode::Range(start..end).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let list = expression
|
|
||||||
.clone()
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::SquareOpen)),
|
|
||||||
just(Token::Symbol(Symbol::SquareClose)),
|
|
||||||
)
|
|
||||||
.map_with(|list, state| {
|
|
||||||
Expression::Value(ValueNode::List(list).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let map_fields = identifier
|
|
||||||
.then(type_specification.clone().or_not())
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Equal)))
|
|
||||||
.then(expression.clone())
|
|
||||||
.map(|((identifier, r#type), expression)| (identifier, r#type, expression));
|
|
||||||
|
|
||||||
let map = map_fields
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)).or_not())
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
)
|
|
||||||
.map_with(|map_assigment_list, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::Map(map_assigment_list).with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
type TypeParameters = Vec<Identifier>;
|
|
||||||
type ValueParameters = Vec<(Identifier, TypeConstructor)>;
|
|
||||||
type FunctionParameters = (Option<TypeParameters>, ValueParameters);
|
|
||||||
|
|
||||||
let turbofish = just(Token::Symbol(Symbol::DoubleColon)).ignore_then(
|
|
||||||
type_constructor
|
|
||||||
.clone()
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::Less)),
|
|
||||||
just(Token::Symbol(Symbol::Greater)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let function = just(Token::Keyword(Keyword::Fn))
|
|
||||||
.ignore_then(
|
|
||||||
identifier
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.at_least(1)
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::Less)),
|
|
||||||
just(Token::Symbol(Symbol::Greater)),
|
|
||||||
)
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
identifier
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Colon)))
|
|
||||||
.then(type_constructor.clone())
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
just(Token::Symbol(Symbol::SkinnyArrow))
|
|
||||||
.ignore_then(type_constructor.clone())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.then(block.clone())
|
|
||||||
.map_with(
|
|
||||||
|(((type_parameters, value_parameters), return_type), body): (
|
|
||||||
(FunctionParameters, Option<TypeConstructor>),
|
|
||||||
WithPosition<Block>,
|
|
||||||
),
|
|
||||||
state| {
|
|
||||||
let value_parameters = if value_parameters.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value_parameters)
|
|
||||||
};
|
|
||||||
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::function(
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let enum_instance = positioned_identifier
|
|
||||||
.then(turbofish.clone().or_not())
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::DoubleColon)))
|
|
||||||
.then(positioned_identifier)
|
|
||||||
.then(expression.clone().or_not())
|
|
||||||
.map_with(|(((type_name, type_arguments), variant), content), state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content: content.map(Box::new),
|
|
||||||
}
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let underscored = |keyword| {
|
|
||||||
just(Token::Keyword(keyword)).delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::DoubleUnderscore)),
|
|
||||||
just(Token::Symbol(Symbol::DoubleUnderscore)),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let built_in_function = choice((
|
|
||||||
underscored(Keyword::Length).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::Length)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
underscored(Keyword::ReadLine).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::ReadLine)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
underscored(Keyword::ReadFile).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::ReadFile)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
underscored(Keyword::Sleep).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::Sleep)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
underscored(Keyword::WriteLine).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::WriteLine)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
underscored(Keyword::JsonParse).map_with(|_, state| {
|
|
||||||
Expression::Value(
|
|
||||||
ValueNode::BuiltInFunction(BuiltInFunction::JsonParse)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
.validate(move |expression, state, emitter| {
|
|
||||||
if !allow_built_ins {
|
|
||||||
emitter.emit(Rich::custom(
|
|
||||||
state.span(),
|
|
||||||
"Built-in functions can only be used by the standard library.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
expression
|
|
||||||
});
|
|
||||||
|
|
||||||
let atom = choice((
|
|
||||||
built_in_function,
|
|
||||||
enum_instance.clone(),
|
|
||||||
range,
|
|
||||||
function.clone(),
|
|
||||||
list.clone(),
|
|
||||||
map.clone(),
|
|
||||||
basic_value,
|
|
||||||
identifier_expression,
|
|
||||||
expression.clone().delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
let logic_math_indexes_as_and_function_calls = atom.pratt((
|
|
||||||
// Logic
|
|
||||||
prefix(
|
|
||||||
2,
|
|
||||||
just(Token::Symbol(Symbol::Exclamation)),
|
|
||||||
|_, expression, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::Not(expression)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::DoubleEqual)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::Equal(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::NotEqual)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(
|
|
||||||
Box::new(Logic::NotEqual(left, right)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::Greater)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::Greater(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::Less)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::Less(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::GreaterOrEqual)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(
|
|
||||||
Box::new(Logic::GreaterOrEqual(left, right)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::LessOrEqual)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(
|
|
||||||
Box::new(Logic::LessOrEqual(left, right)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::DoubleAmpersand)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::And(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::DoublePipe)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Logic(Box::new(Logic::Or(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Math
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::Plus)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Math(Box::new(Math::Add(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::Minus)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Math(Box::new(Math::Subtract(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(2),
|
|
||||||
just(Token::Symbol(Symbol::Asterisk)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Math(Box::new(Math::Multiply(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(2),
|
|
||||||
just(Token::Symbol(Symbol::Slash)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Math(Box::new(Math::Divide(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
infix(
|
|
||||||
left(1),
|
|
||||||
just(Token::Symbol(Symbol::Percent)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::Math(Box::new(Math::Modulo(left, right)).with_position(span))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Indexes
|
|
||||||
infix(
|
|
||||||
left(4),
|
|
||||||
just(Token::Symbol(Symbol::Dot)),
|
|
||||||
|left, _, right, span| {
|
|
||||||
Expression::MapIndex(
|
|
||||||
Box::new(MapIndex::new(left, right)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
postfix(
|
|
||||||
3,
|
|
||||||
expression.clone().delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::SquareOpen)),
|
|
||||||
just(Token::Symbol(Symbol::SquareClose)),
|
|
||||||
),
|
|
||||||
|left, right, span| {
|
|
||||||
Expression::ListIndex(
|
|
||||||
Box::new(ListIndex::new(left, right)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Function call
|
|
||||||
postfix(
|
|
||||||
3,
|
|
||||||
turbofish.clone().or_not().then(
|
|
||||||
expression
|
|
||||||
.clone()
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|function_expression,
|
|
||||||
(type_parameters, value_parameters): (
|
|
||||||
Option<Vec<TypeConstructor>>,
|
|
||||||
Vec<Expression>,
|
|
||||||
),
|
|
||||||
span| {
|
|
||||||
let value_parameters = if value_parameters.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value_parameters)
|
|
||||||
};
|
|
||||||
|
|
||||||
Expression::FunctionCall(
|
|
||||||
FunctionCall::new(
|
|
||||||
function_expression,
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
)
|
|
||||||
.with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// As
|
|
||||||
postfix(
|
|
||||||
2,
|
|
||||||
just(Token::Keyword(Keyword::As)).ignore_then(type_constructor.clone()),
|
|
||||||
|expression, constructor, span| {
|
|
||||||
Expression::As(
|
|
||||||
Box::new(As::new(expression, constructor)).with_position(span),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
choice((
|
|
||||||
logic_math_indexes_as_and_function_calls,
|
|
||||||
built_in_function,
|
|
||||||
enum_instance,
|
|
||||||
range,
|
|
||||||
function,
|
|
||||||
list,
|
|
||||||
map,
|
|
||||||
basic_value,
|
|
||||||
identifier_expression,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
let expression_statement = expression.clone().map(Statement::Expression);
|
|
||||||
|
|
||||||
let async_block = just(Token::Keyword(Keyword::Async))
|
|
||||||
.ignore_then(statement.clone().repeated().collect().delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
))
|
|
||||||
.map_with(|statements, state| {
|
|
||||||
Statement::AsyncBlock(AsyncBlock::new(statements).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let r#break = just(Token::Keyword(Keyword::Break))
|
|
||||||
.map_with(|_, state| Statement::Break(().with_position(state.span())));
|
|
||||||
|
|
||||||
let assignment = positioned_identifier
|
|
||||||
.then(type_specification.clone().or_not())
|
|
||||||
.then(choice((
|
|
||||||
just(Token::Symbol(Symbol::Equal)).to(AssignmentOperator::Assign),
|
|
||||||
just(Token::Symbol(Symbol::PlusEqual)).to(AssignmentOperator::AddAssign),
|
|
||||||
just(Token::Symbol(Symbol::MinusEqual)).to(AssignmentOperator::SubAssign),
|
|
||||||
)))
|
|
||||||
.then(statement.clone())
|
|
||||||
.map_with(|(((identifier, r#type), operator), statement), state| {
|
|
||||||
Statement::Assignment(
|
|
||||||
Assignment::new(identifier, r#type, operator, statement)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let block_statement = block.clone().map(Statement::Block);
|
|
||||||
|
|
||||||
let r#loop = statement
|
|
||||||
.clone()
|
|
||||||
.repeated()
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Keyword(Keyword::Loop)).then(just(Token::Symbol(Symbol::CurlyOpen))),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
)
|
|
||||||
.map_with(|statements, state| {
|
|
||||||
Statement::Loop(Loop::new(statements).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let r#while = just(Token::Keyword(Keyword::While))
|
|
||||||
.ignore_then(expression.clone())
|
|
||||||
.then(statement.clone().repeated().collect().delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
))
|
|
||||||
.map_with(|(expression, statements), state| {
|
|
||||||
Statement::While(While::new(expression, statements).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let if_else = just(Token::Keyword(Keyword::If))
|
|
||||||
.ignore_then(expression.clone())
|
|
||||||
.then(block.clone())
|
|
||||||
.then(
|
|
||||||
just(Token::Keyword(Keyword::Else))
|
|
||||||
.ignore_then(just(Token::Keyword(Keyword::If)))
|
|
||||||
.ignore_then(expression.clone())
|
|
||||||
.then(block.clone())
|
|
||||||
.repeated()
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
just(Token::Keyword(Keyword::Else))
|
|
||||||
.ignore_then(block.clone())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map_with(
|
|
||||||
|(((if_expression, if_block), else_ifs), else_block), state| {
|
|
||||||
Statement::IfElse(
|
|
||||||
IfElse::new(if_expression, if_block, else_ifs, else_block)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let type_alias = just(Token::Keyword(Keyword::Type))
|
|
||||||
.ignore_then(positioned_identifier)
|
|
||||||
.then_ignore(just(Token::Symbol(Symbol::Equal)))
|
|
||||||
.then(type_constructor.clone())
|
|
||||||
.map_with(|(identifier, constructor), state| {
|
|
||||||
Statement::TypeAlias(
|
|
||||||
TypeAlias::new(identifier, constructor).with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let enum_variant = positioned_identifier
|
|
||||||
.then(
|
|
||||||
type_constructor
|
|
||||||
.clone()
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::ParenOpen)),
|
|
||||||
just(Token::Symbol(Symbol::ParenClose)),
|
|
||||||
)
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map(|(identifier, constructors)| EnumVariant {
|
|
||||||
name: identifier,
|
|
||||||
content: constructors,
|
|
||||||
});
|
|
||||||
|
|
||||||
let enum_declaration = just(Token::Keyword(Keyword::Enum))
|
|
||||||
.ignore_then(positioned_identifier)
|
|
||||||
.then(
|
|
||||||
positioned_identifier
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.allow_trailing()
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::Less)),
|
|
||||||
just(Token::Symbol(Symbol::Greater)),
|
|
||||||
)
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
enum_variant
|
|
||||||
.separated_by(just(Token::Symbol(Symbol::Comma)))
|
|
||||||
.allow_trailing()
|
|
||||||
.at_least(1)
|
|
||||||
.collect()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Symbol(Symbol::CurlyOpen)),
|
|
||||||
just(Token::Symbol(Symbol::CurlyClose)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map_with(|((name, type_parameters), variants), state| {
|
|
||||||
Statement::EnumDeclaration(
|
|
||||||
EnumDeclaration::new(name, type_parameters, variants)
|
|
||||||
.with_position(state.span()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let r#use = select! {
|
|
||||||
Token::Use(text) => text,
|
|
||||||
}
|
|
||||||
.map_with(|text, state| {
|
|
||||||
Statement::Use(Use::new(text.to_string()).with_position(state.span()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let null = just(Token::Symbol(Symbol::Semicolon))
|
|
||||||
.ignored()
|
|
||||||
.map_with(|_, state| Statement::Null(().with_position(state.span())));
|
|
||||||
|
|
||||||
comment.repeated().or_not().ignore_then(choice((
|
|
||||||
assignment,
|
|
||||||
expression_statement,
|
|
||||||
async_block,
|
|
||||||
if_else,
|
|
||||||
r#break,
|
|
||||||
block_statement,
|
|
||||||
r#loop,
|
|
||||||
r#while,
|
|
||||||
type_alias,
|
|
||||||
enum_declaration,
|
|
||||||
r#use,
|
|
||||||
null,
|
|
||||||
)))
|
|
||||||
});
|
|
||||||
|
|
||||||
statement.repeated().collect().map(AbstractTree::new)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,133 +0,0 @@
|
|||||||
use std::sync::{Arc, OnceLock};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
abstract_tree::{AbstractNode, AbstractTree, SourcePosition},
|
|
||||||
context::Context,
|
|
||||||
lexer::lex,
|
|
||||||
parser,
|
|
||||||
};
|
|
||||||
use chumsky::prelude::*;
|
|
||||||
|
|
||||||
pub fn std_full_compiled() -> [AbstractTree; 5] {
|
|
||||||
[
|
|
||||||
std_core_compiled().clone(),
|
|
||||||
std_fs_compiled().clone(),
|
|
||||||
std_io_compiled().clone(),
|
|
||||||
std_json_compiled().clone(),
|
|
||||||
std_thread_compiled().clone(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const STD_CORE: &str = include_str!("../../std/core.ds");
|
|
||||||
pub const STD_FS: &str = include_str!("../../std/fs.ds");
|
|
||||||
pub const STD_IO: &str = include_str!("../../std/io.ds");
|
|
||||||
pub const STD_JSON: &str = include_str!("../../std/json.ds");
|
|
||||||
pub const STD_THREAD: &str = include_str!("../../std/thread.ds");
|
|
||||||
|
|
||||||
static CORE_CONTEXT: OnceLock<Context> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn core_context<'a>() -> &'a Context {
|
|
||||||
CORE_CONTEXT.get_or_init(|| {
|
|
||||||
let context = Context::new();
|
|
||||||
let std_core = std_core_compiled().clone();
|
|
||||||
let global_scope = SourcePosition(0, usize::MAX);
|
|
||||||
|
|
||||||
std_core
|
|
||||||
.define_and_validate(&context, true, global_scope)
|
|
||||||
.expect("Failed to validate std.core");
|
|
||||||
std_core
|
|
||||||
.evaluate(&context, true, global_scope)
|
|
||||||
.expect("Failed to evaluate std.core");
|
|
||||||
|
|
||||||
context
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static CORE_SOURCE: OnceLock<(Arc<str>, Arc<str>)> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn core_source<'a>() -> &'a (Arc<str>, Arc<str>) {
|
|
||||||
CORE_SOURCE.get_or_init(|| (Arc::from("std/core.ds"), Arc::from(STD_CORE)))
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_SOURCES: OnceLock<[(Arc<str>, Arc<str>); 4]> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_sources<'a>() -> &'a [(Arc<str>, Arc<str>); 4] {
|
|
||||||
STD_SOURCES.get_or_init(|| {
|
|
||||||
[
|
|
||||||
(Arc::from("std/fs.ds"), Arc::from(STD_FS)),
|
|
||||||
(Arc::from("std/io.ds"), Arc::from(STD_IO)),
|
|
||||||
(Arc::from("std/json.ds"), Arc::from(STD_JSON)),
|
|
||||||
(Arc::from("std/thread.ds"), Arc::from(STD_THREAD)),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_CORE_COMPILED: OnceLock<AbstractTree> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_core_compiled<'a>() -> &'a AbstractTree {
|
|
||||||
STD_CORE_COMPILED.get_or_init(|| {
|
|
||||||
let tokens = lex(STD_CORE).expect("Failed to lex");
|
|
||||||
let abstract_tree = parser(true)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.expect("Failed to parse");
|
|
||||||
|
|
||||||
abstract_tree
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_FS_COMPILED: OnceLock<AbstractTree> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_fs_compiled<'a>() -> &'a AbstractTree {
|
|
||||||
STD_FS_COMPILED.get_or_init(|| {
|
|
||||||
let tokens = lex(STD_FS).expect("Failed to lex");
|
|
||||||
let abstract_tree = parser(true)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.expect("Failed to parse");
|
|
||||||
|
|
||||||
abstract_tree
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_IO_COMPILED: OnceLock<AbstractTree> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_io_compiled<'a>() -> &'a AbstractTree {
|
|
||||||
STD_IO_COMPILED.get_or_init(|| {
|
|
||||||
let tokens = lex(STD_IO).expect("Failed to lex");
|
|
||||||
let abstract_tree = parser(true)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.expect("Failed to parse");
|
|
||||||
|
|
||||||
abstract_tree
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_JSON_COMPILED: OnceLock<AbstractTree> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_json_compiled<'a>() -> &'a AbstractTree {
|
|
||||||
STD_JSON_COMPILED.get_or_init(|| {
|
|
||||||
let tokens = lex(STD_JSON).expect("Failed to lex");
|
|
||||||
let abstract_tree = parser(true)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.expect("Failed to parse");
|
|
||||||
|
|
||||||
abstract_tree
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static STD_THREAD_COMPILED: OnceLock<AbstractTree> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn std_thread_compiled<'a>() -> &'a AbstractTree {
|
|
||||||
STD_THREAD_COMPILED.get_or_init(|| {
|
|
||||||
let tokens = lex(STD_THREAD).expect("Failed to lex");
|
|
||||||
let abstract_tree = parser(true)
|
|
||||||
.parse(tokens.spanned((tokens.len()..tokens.len()).into()))
|
|
||||||
.into_result()
|
|
||||||
.expect("Failed to parse");
|
|
||||||
|
|
||||||
abstract_tree
|
|
||||||
})
|
|
||||||
}
|
|
13
dust-lang/src/token.rs
Normal file
13
dust-lang/src/token.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::Identifier;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Token {
|
||||||
|
Eof,
|
||||||
|
Equal,
|
||||||
|
Identifier(Identifier),
|
||||||
|
Integer(i64),
|
||||||
|
Plus,
|
||||||
|
Star,
|
||||||
|
LeftParenthesis,
|
||||||
|
RightParenthesis,
|
||||||
|
}
|
@ -19,7 +19,13 @@ use std::{
|
|||||||
use clap::error::Result;
|
use clap::error::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{error::TypeConflict, identifier::Identifier};
|
use crate::identifier::Identifier;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct TypeConflict {
|
||||||
|
pub expected: Type,
|
||||||
|
pub actual: Type,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
/// Description of a kind of value.
|
/// Description of a kind of value.
|
||||||
|
@ -7,20 +7,13 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use chumsky::container::Container;
|
use chumsky::container::Container;
|
||||||
use log::{debug, trace};
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::Visitor,
|
de::Visitor,
|
||||||
ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple},
|
ser::{SerializeMap, SerializeSeq, SerializeTuple},
|
||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{identifier::Identifier, Type};
|
||||||
abstract_tree::{AbstractNode, Block, BuiltInFunction, Evaluation, SourcePosition},
|
|
||||||
context::Context,
|
|
||||||
error::{RuntimeError, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Value(Arc<ValueInner>);
|
pub struct Value(Arc<ValueInner>);
|
||||||
@ -34,24 +27,6 @@ impl Value {
|
|||||||
Value(Arc::new(ValueInner::Boolean(boolean)))
|
Value(Arc::new(ValueInner::Boolean(boolean)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn built_in_function(function: BuiltInFunction) -> Self {
|
|
||||||
Value(Arc::new(ValueInner::BuiltInFunction(function)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enum_instance(
|
|
||||||
type_name: Identifier,
|
|
||||||
type_arguments: Option<Vec<Type>>,
|
|
||||||
variant: Identifier,
|
|
||||||
content: Option<Value>,
|
|
||||||
) -> Self {
|
|
||||||
Value(Arc::new(ValueInner::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float(float: f64) -> Self {
|
pub fn float(float: f64) -> Self {
|
||||||
Value(Arc::new(ValueInner::Float(float)))
|
Value(Arc::new(ValueInner::Float(float)))
|
||||||
}
|
}
|
||||||
@ -76,26 +51,8 @@ impl Value {
|
|||||||
Value(Arc::new(ValueInner::String(to_string.to_string())))
|
Value(Arc::new(ValueInner::String(to_string.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(
|
pub fn r#type(&self) -> Type {
|
||||||
type_parameters: Option<Vec<Identifier>>,
|
self.0.r#type()
|
||||||
value_parameters: Option<Vec<(Identifier, Type)>>,
|
|
||||||
return_type: Option<Type>,
|
|
||||||
body: Block,
|
|
||||||
) -> Self {
|
|
||||||
Value(Arc::new(ValueInner::Function(Function::new(
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn structure(name: Identifier, fields: Vec<(Identifier, Value)>) -> Self {
|
|
||||||
Value(Arc::new(ValueInner::Structure { name, fields }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
|
|
||||||
self.0.r#type(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<bool> {
|
pub fn as_boolean(&self) -> Option<bool> {
|
||||||
@ -127,32 +84,6 @@ impl Display for Value {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self.inner().as_ref() {
|
match self.inner().as_ref() {
|
||||||
ValueInner::Boolean(boolean) => write!(f, "{boolean}"),
|
ValueInner::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
ValueInner::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content,
|
|
||||||
} => {
|
|
||||||
write!(f, "{type_name}::")?;
|
|
||||||
|
|
||||||
if let Some(types) = type_arguments {
|
|
||||||
write!(f, "::<")?;
|
|
||||||
|
|
||||||
for r#type in types {
|
|
||||||
write!(f, "{type}, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "::{variant}(")?;
|
|
||||||
|
|
||||||
if let Some(value) = content {
|
|
||||||
write!(f, "{value}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")
|
|
||||||
}
|
|
||||||
ValueInner::Float(float) => {
|
ValueInner::Float(float) => {
|
||||||
write!(f, "{float}")?;
|
write!(f, "{float}")?;
|
||||||
|
|
||||||
@ -191,55 +122,6 @@ impl Display for Value {
|
|||||||
}
|
}
|
||||||
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
||||||
ValueInner::String(string) => write!(f, "{string}"),
|
ValueInner::String(string) => write!(f, "{string}"),
|
||||||
ValueInner::Function(Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
write!(f, "fn ")?;
|
|
||||||
|
|
||||||
if let Some(type_parameters) = type_parameters {
|
|
||||||
write!(f, "<")?;
|
|
||||||
|
|
||||||
for (index, identifier) in type_parameters.iter().enumerate() {
|
|
||||||
if index == type_parameters.len() - 1 {
|
|
||||||
write!(f, "{}", identifier)?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{} ", identifier)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
if let Some(value_parameters) = value_parameters {
|
|
||||||
for (identifier, r#type) in value_parameters {
|
|
||||||
write!(f, "{identifier}: {}", r#type)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")?;
|
|
||||||
|
|
||||||
if let Some(return_type) = return_type {
|
|
||||||
write!(f, " -> {return_type}")?
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " {body}")
|
|
||||||
}
|
|
||||||
ValueInner::Structure { name, fields } => {
|
|
||||||
write!(f, "{name} {{")?;
|
|
||||||
|
|
||||||
for (key, value) in fields {
|
|
||||||
write!(f, "{key} = {value},")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
ValueInner::BuiltInFunction(built_in_function) => write!(f, "{built_in_function}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,38 +147,7 @@ impl Serialize for Value {
|
|||||||
{
|
{
|
||||||
match self.0.as_ref() {
|
match self.0.as_ref() {
|
||||||
ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean),
|
ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean),
|
||||||
ValueInner::EnumInstance {
|
|
||||||
type_name,
|
|
||||||
type_arguments,
|
|
||||||
variant,
|
|
||||||
content,
|
|
||||||
} => {
|
|
||||||
let mut struct_ser = serializer.serialize_struct("EnumInstance", 3)?;
|
|
||||||
|
|
||||||
struct_ser.serialize_field("type_name", type_name)?;
|
|
||||||
struct_ser.serialize_field("type_arguments", type_arguments)?;
|
|
||||||
struct_ser.serialize_field("variant", variant)?;
|
|
||||||
struct_ser.serialize_field("content", content)?;
|
|
||||||
|
|
||||||
struct_ser.end()
|
|
||||||
}
|
|
||||||
ValueInner::Float(float) => serializer.serialize_f64(*float),
|
ValueInner::Float(float) => serializer.serialize_f64(*float),
|
||||||
ValueInner::Function(Function {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let mut struct_ser = serializer.serialize_struct("Function", 4)?;
|
|
||||||
|
|
||||||
struct_ser.serialize_field("type_parameters", type_parameters)?;
|
|
||||||
struct_ser.serialize_field("value_parameters", value_parameters)?;
|
|
||||||
struct_ser.serialize_field("return_type", return_type)?;
|
|
||||||
struct_ser.serialize_field("body", body)?;
|
|
||||||
|
|
||||||
struct_ser.end()
|
|
||||||
}
|
|
||||||
ValueInner::Integer(integer) => serializer.serialize_i64(*integer),
|
ValueInner::Integer(integer) => serializer.serialize_i64(*integer),
|
||||||
ValueInner::List(list) => {
|
ValueInner::List(list) => {
|
||||||
let mut list_ser = serializer.serialize_seq(Some(list.len()))?;
|
let mut list_ser = serializer.serialize_seq(Some(list.len()))?;
|
||||||
@ -325,15 +176,6 @@ impl Serialize for Value {
|
|||||||
tuple_ser.end()
|
tuple_ser.end()
|
||||||
}
|
}
|
||||||
ValueInner::String(string) => serializer.serialize_str(string),
|
ValueInner::String(string) => serializer.serialize_str(string),
|
||||||
ValueInner::Structure { name, fields } => {
|
|
||||||
let mut struct_ser = serializer.serialize_struct("Structure", 2)?;
|
|
||||||
|
|
||||||
struct_ser.serialize_field("name", name)?;
|
|
||||||
struct_ser.serialize_field("fields", fields)?;
|
|
||||||
|
|
||||||
struct_ser.end()
|
|
||||||
}
|
|
||||||
ValueInner::BuiltInFunction(_) => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,44 +425,22 @@ impl<'de> Deserialize<'de> for Value {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum ValueInner {
|
pub enum ValueInner {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
BuiltInFunction(BuiltInFunction),
|
|
||||||
EnumInstance {
|
|
||||||
type_name: Identifier,
|
|
||||||
type_arguments: Option<Vec<Type>>,
|
|
||||||
variant: Identifier,
|
|
||||||
content: Option<Value>,
|
|
||||||
},
|
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Function(Function),
|
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
List(Vec<Value>),
|
List(Vec<Value>),
|
||||||
Map(BTreeMap<Identifier, Value>),
|
Map(BTreeMap<Identifier, Value>),
|
||||||
Range(Range<i64>),
|
Range(Range<i64>),
|
||||||
String(String),
|
String(String),
|
||||||
Structure {
|
|
||||||
name: Identifier,
|
|
||||||
fields: Vec<(Identifier, Value)>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueInner {
|
impl ValueInner {
|
||||||
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
|
pub fn r#type(&self) -> Type {
|
||||||
let r#type = match self {
|
match self {
|
||||||
ValueInner::Boolean(_) => Type::Boolean,
|
ValueInner::Boolean(_) => Type::Boolean,
|
||||||
ValueInner::EnumInstance { type_name, .. } => {
|
|
||||||
if let Some(r#type) = context.get_type(type_name)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::EnumDefinitionNotFound {
|
|
||||||
identifier: type_name.clone(),
|
|
||||||
position: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueInner::Float(_) => Type::Float,
|
ValueInner::Float(_) => Type::Float,
|
||||||
ValueInner::Integer(_) => Type::Integer,
|
ValueInner::Integer(_) => Type::Integer,
|
||||||
ValueInner::List(values) => {
|
ValueInner::List(values) => {
|
||||||
let item_type = values.first().unwrap().r#type(context)?;
|
let item_type = values.first().unwrap().r#type();
|
||||||
|
|
||||||
Type::List {
|
Type::List {
|
||||||
length: values.len(),
|
length: values.len(),
|
||||||
@ -631,7 +451,7 @@ impl ValueInner {
|
|||||||
let mut type_map = BTreeMap::with_capacity(value_map.len());
|
let mut type_map = BTreeMap::with_capacity(value_map.len());
|
||||||
|
|
||||||
for (identifier, value) in value_map {
|
for (identifier, value) in value_map {
|
||||||
let r#type = value.r#type(context)?;
|
let r#type = value.r#type();
|
||||||
|
|
||||||
type_map.insert(identifier.clone(), r#type);
|
type_map.insert(identifier.clone(), r#type);
|
||||||
}
|
}
|
||||||
@ -640,29 +460,7 @@ impl ValueInner {
|
|||||||
}
|
}
|
||||||
ValueInner::Range(_) => Type::Range,
|
ValueInner::Range(_) => Type::Range,
|
||||||
ValueInner::String(_) => Type::String,
|
ValueInner::String(_) => Type::String,
|
||||||
ValueInner::Function(function) => {
|
}
|
||||||
let return_type = function.return_type.clone().map(Box::new);
|
|
||||||
|
|
||||||
Type::Function {
|
|
||||||
type_parameters: function.type_parameters().clone(),
|
|
||||||
value_parameters: function.value_parameters().clone(),
|
|
||||||
return_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueInner::Structure { name, .. } => {
|
|
||||||
if let Some(r#type) = context.get_type(name)? {
|
|
||||||
r#type
|
|
||||||
} else {
|
|
||||||
return Err(ValidationError::StructDefinitionNotFound {
|
|
||||||
identifier: name.clone(),
|
|
||||||
position: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ValueInner::BuiltInFunction(function) => function.r#type(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(r#type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,199 +499,6 @@ impl Ord for ValueInner {
|
|||||||
(Range(_), _) => Ordering::Greater,
|
(Range(_), _) => Ordering::Greater,
|
||||||
(String(left), String(right)) => left.cmp(right),
|
(String(left), String(right)) => left.cmp(right),
|
||||||
(String(_), _) => Ordering::Greater,
|
(String(_), _) => Ordering::Greater,
|
||||||
(
|
|
||||||
EnumInstance {
|
|
||||||
type_name: left_name,
|
|
||||||
type_arguments: left_arguments,
|
|
||||||
variant: left_variant,
|
|
||||||
content: left_content,
|
|
||||||
},
|
|
||||||
EnumInstance {
|
|
||||||
type_name: right_name,
|
|
||||||
type_arguments: right_arguments,
|
|
||||||
variant: right_variant,
|
|
||||||
content: right_content,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let name_cmp = left_name.cmp(right_name);
|
|
||||||
|
|
||||||
if name_cmp.is_eq() {
|
|
||||||
let argument_cmp = left_arguments.cmp(right_arguments);
|
|
||||||
|
|
||||||
if argument_cmp.is_eq() {
|
|
||||||
let variant_cmp = left_variant.cmp(right_variant);
|
|
||||||
|
|
||||||
if variant_cmp.is_eq() {
|
|
||||||
left_content.cmp(right_content)
|
|
||||||
} else {
|
|
||||||
variant_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
argument_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(EnumInstance { .. }, _) => Ordering::Greater,
|
|
||||||
(Function(left), Function(right)) => left.cmp(right),
|
|
||||||
(Function(_), _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
Structure {
|
|
||||||
name: left_name,
|
|
||||||
fields: left_fields,
|
|
||||||
},
|
|
||||||
Structure {
|
|
||||||
name: right_name,
|
|
||||||
fields: right_fields,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let name_cmp = left_name.cmp(right_name);
|
|
||||||
|
|
||||||
if name_cmp.is_eq() {
|
|
||||||
left_fields.cmp(right_fields)
|
|
||||||
} else {
|
|
||||||
name_cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Structure { .. }, _) => Ordering::Greater,
|
|
||||||
(BuiltInFunction(left), BuiltInFunction(right)) => left.cmp(right),
|
|
||||||
(BuiltInFunction(_), _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Function {
|
|
||||||
type_parameters: Option<Vec<Identifier>>,
|
|
||||||
value_parameters: Option<Vec<(Identifier, Type)>>,
|
|
||||||
return_type: Option<Type>,
|
|
||||||
body: Block,
|
|
||||||
context: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
pub fn new(
|
|
||||||
type_parameters: Option<Vec<Identifier>>,
|
|
||||||
value_parameters: Option<Vec<(Identifier, Type)>>,
|
|
||||||
return_type: Option<Type>,
|
|
||||||
body: Block,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
type_parameters,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
body,
|
|
||||||
context: Context::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_parameters(&self) -> &Option<Vec<Identifier>> {
|
|
||||||
&self.type_parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value_parameters(&self) -> &Option<Vec<(Identifier, Type)>> {
|
|
||||||
&self.value_parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body(&self) -> &Block {
|
|
||||||
&self.body
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(
|
|
||||||
self,
|
|
||||||
outer_context: Option<&Context>,
|
|
||||||
type_arguments: Option<Vec<Type>>,
|
|
||||||
value_arguments: Option<Vec<Value>>,
|
|
||||||
) -> Result<Option<Evaluation>, RuntimeError> {
|
|
||||||
trace!("Setting function call variables");
|
|
||||||
|
|
||||||
if let Some(outer_context) = outer_context {
|
|
||||||
if &self.context == outer_context {
|
|
||||||
log::debug!("Recursion detected");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.context.inherit_variables_from(outer_context)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(type_parameters), Some(type_arguments)) =
|
|
||||||
(self.type_parameters, type_arguments)
|
|
||||||
{
|
|
||||||
for (identifier, r#type) in type_parameters.into_iter().zip(type_arguments.into_iter())
|
|
||||||
{
|
|
||||||
self.context
|
|
||||||
.set_type(identifier.clone(), r#type, SourcePosition(0, usize::MAX))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let (Some(value_parameters), Some(value_arguments)) =
|
|
||||||
(self.value_parameters, value_arguments)
|
|
||||||
{
|
|
||||||
for ((identifier, _), value) in value_parameters
|
|
||||||
.into_iter()
|
|
||||||
.zip(value_arguments.into_iter())
|
|
||||||
{
|
|
||||||
self.context
|
|
||||||
.set_value(identifier.clone(), value, SourcePosition(0, usize::MAX))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Calling function");
|
|
||||||
|
|
||||||
self.body
|
|
||||||
.evaluate(&self.context, false, SourcePosition(0, usize::MAX))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Function {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Function {
|
|
||||||
type_parameters: self.type_parameters.clone(),
|
|
||||||
value_parameters: self.value_parameters.clone(),
|
|
||||||
return_type: self.return_type.clone(),
|
|
||||||
body: self.body.clone(),
|
|
||||||
context: Context::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Function {}
|
|
||||||
|
|
||||||
impl PartialEq for Function {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.type_parameters == other.type_parameters
|
|
||||||
&& self.value_parameters == other.value_parameters
|
|
||||||
&& self.return_type == other.return_type
|
|
||||||
&& self.body == other.body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Function {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Function {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
let type_param_cmp = self.type_parameters().cmp(&other.type_parameters);
|
|
||||||
|
|
||||||
if type_param_cmp.is_eq() {
|
|
||||||
let value_param_cmp = self.value_parameters.cmp(&other.value_parameters);
|
|
||||||
|
|
||||||
if value_param_cmp.is_eq() {
|
|
||||||
let return_type_cmp = self.return_type.cmp(&other.return_type);
|
|
||||||
|
|
||||||
if return_type_cmp.is_eq() {
|
|
||||||
self.body.cmp(&other.body)
|
|
||||||
} else {
|
|
||||||
return_type_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value_param_cmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type_param_cmp
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
use dust_lang::{identifier::Identifier, *};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_enum() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
enum FooBar {
|
|
||||||
Foo,
|
|
||||||
Bar,
|
|
||||||
}
|
|
||||||
|
|
||||||
FooBar::Foo
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::enum_instance(
|
|
||||||
Identifier::new("FooBar"),
|
|
||||||
None,
|
|
||||||
Identifier::new("Foo"),
|
|
||||||
None
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn big_enum() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
enum FooBarBaz <T, U, V> {
|
|
||||||
Foo(T),
|
|
||||||
Bar(U),
|
|
||||||
Baz(V),
|
|
||||||
}
|
|
||||||
|
|
||||||
FooBarBaz::<int, str, float>::Baz(42.0)
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::enum_instance(
|
|
||||||
Identifier::new("FooBarBaz"),
|
|
||||||
Some(vec![Type::Integer, Type::String, Type::Float]),
|
|
||||||
Identifier::new("Baz"),
|
|
||||||
Some(Value::float(42.0)),
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
use dust_lang::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn logic() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "1 == 1").unwrap(),
|
|
||||||
Some(Value::boolean(true))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "('42' == '42') && (42 != 0)").unwrap(),
|
|
||||||
Some(Value::boolean(true))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn math() {
|
|
||||||
assert_eq!(interpret("test", "1 + 1").unwrap(), Some(Value::integer(2)));
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "2 * (21 + 19 + 1 * 2) / 2").unwrap(),
|
|
||||||
Some(Value::integer(42))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_index() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foo = [1, 2, 3]; foo[2]").unwrap(),
|
|
||||||
Some(Value::integer(3))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn map_index() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "{ x = 3 }.x").unwrap(),
|
|
||||||
Some(Value::integer(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foo = { x = 3 }; foo.x").unwrap(),
|
|
||||||
Some(Value::integer(3))
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
use abstract_tree::{Expression, ValueNode, WithPos};
|
|
||||||
use dust_lang::*;
|
|
||||||
use error::{DustError, ValidationError};
|
|
||||||
use identifier::Identifier;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_scope() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
x = 2
|
|
||||||
|
|
||||||
foo = fn () -> int {
|
|
||||||
x = 42
|
|
||||||
x
|
|
||||||
}
|
|
||||||
|
|
||||||
x = 1
|
|
||||||
|
|
||||||
foo()
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(42)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_call_with_type_argument() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = fn <T> (x: T) -> T { x }
|
|
||||||
foobar::<int>(42)
|
|
||||||
",
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(42)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_call() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = fn (message: str) -> str { message }
|
|
||||||
foobar('Hiya')
|
|
||||||
",
|
|
||||||
),
|
|
||||||
Ok(Some(Value::string("Hiya".to_string())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn callback() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = fn (cb: fn () -> str) -> str {
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
foobar(fn () -> str { 'Hiya' })
|
|
||||||
",
|
|
||||||
),
|
|
||||||
Ok(Some(Value::string("Hiya".to_string())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn built_in_function_call() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "use std.io io.write_line('Hiya')"),
|
|
||||||
Ok(None)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_context_captures_values() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
bar = fn () -> int { 2 }
|
|
||||||
foo = fn () -> int { bar() }
|
|
||||||
foo()
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(2)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn recursion() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
fib = fn (i: int) -> int {
|
|
||||||
if i <= 1 {
|
|
||||||
i
|
|
||||||
} else {
|
|
||||||
fib(i - 1) + fib(i - 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fib(7)
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(13)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn value_argument_error() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = fn (a: int, b: int) -> int { a + b }
|
|
||||||
foobar(1)
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Err(InterpreterError::new(
|
|
||||||
"test".into(),
|
|
||||||
vec![DustError::Validation {
|
|
||||||
error: ValidationError::WrongValueArguments {
|
|
||||||
parameters: vec![
|
|
||||||
(Identifier::new("a"), Type::Integer),
|
|
||||||
(Identifier::new("b"), Type::Integer),
|
|
||||||
],
|
|
||||||
arguments: vec![Expression::Value(
|
|
||||||
ValueNode::Integer(1).with_position((78, 79)),
|
|
||||||
)],
|
|
||||||
},
|
|
||||||
position: (71, 80).into()
|
|
||||||
}]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn type_argument_error() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = fn <T> (a: T) -> T { a }
|
|
||||||
foobar(1)
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Err(InterpreterError::new(
|
|
||||||
"test".into(),
|
|
||||||
vec![DustError::Validation {
|
|
||||||
error: ValidationError::WrongTypeArguments {
|
|
||||||
parameters: vec![Identifier::new("T")],
|
|
||||||
arguments: vec![]
|
|
||||||
},
|
|
||||||
position: (59, 68).into()
|
|
||||||
}]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
mod validation;
|
|
||||||
|
|
||||||
use dust_lang::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn async_block() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
x = 41
|
|
||||||
async {
|
|
||||||
x += 1
|
|
||||||
5
|
|
||||||
}
|
|
||||||
x
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(42)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn loops_and_breaks() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
i = 0
|
|
||||||
loop {
|
|
||||||
if i == 3 {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::integer(3)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foobar = {
|
|
||||||
while true {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
'foobar'
|
|
||||||
}
|
|
||||||
|
|
||||||
foobar
|
|
||||||
"
|
|
||||||
),
|
|
||||||
Ok(Some(Value::string("foobar".to_string())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn r#if() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "if true { 'foobar' }"),
|
|
||||||
Ok(Some(Value::string("foobar".to_string())))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "if false { 'foo' } else { 'bar' }"),
|
|
||||||
Ok(Some(Value::string("bar".to_string())))
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
// Reuse these tests when structures are reimplemented
|
|
||||||
// use dust_lang::{
|
|
||||||
// abstract_tree::{Type, WithPos},
|
|
||||||
// error::{DustError, TypeConflict, ValidationError},
|
|
||||||
// identifier::Identifier,
|
|
||||||
// interpret, Value,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn simple_structure() {
|
|
||||||
// assert_eq!(
|
|
||||||
// interpret(
|
|
||||||
// "test",
|
|
||||||
// "
|
|
||||||
// struct Foo {
|
|
||||||
// bar : int,
|
|
||||||
// baz : str,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Foo {
|
|
||||||
// bar = 42,
|
|
||||||
// baz = 'hiya',
|
|
||||||
// }
|
|
||||||
// "
|
|
||||||
// ),
|
|
||||||
// Ok(Some(Value::structure(
|
|
||||||
// Identifier::new("Foo").with_position((127, 130)),
|
|
||||||
// vec![
|
|
||||||
// (Identifier::new("bar"), Value::integer(42)),
|
|
||||||
// (Identifier::new("baz"), Value::string("hiya".to_string())),
|
|
||||||
// ]
|
|
||||||
// )))
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn field_type_error() {
|
|
||||||
// assert_eq!(
|
|
||||||
// interpret(
|
|
||||||
// "test",
|
|
||||||
// "
|
|
||||||
// struct Foo {
|
|
||||||
// bar : int,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Foo {
|
|
||||||
// bar = 'hiya',
|
|
||||||
// }
|
|
||||||
// "
|
|
||||||
// )
|
|
||||||
// .unwrap_err()
|
|
||||||
// .errors(),
|
|
||||||
// &vec![DustError::Validation {
|
|
||||||
// error: ValidationError::TypeCheck {
|
|
||||||
// conflict: TypeConflict {
|
|
||||||
// actual: Type::String,
|
|
||||||
// expected: Type::Integer
|
|
||||||
// },
|
|
||||||
// actual_position: (128, 134).into(),
|
|
||||||
// expected_position: Some((56, 59).into()),
|
|
||||||
// },
|
|
||||||
// position: (96, 153).into()
|
|
||||||
// }]
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn nested_structure() {
|
|
||||||
// assert_eq!(
|
|
||||||
// interpret(
|
|
||||||
// "test",
|
|
||||||
// "
|
|
||||||
// struct Bar {
|
|
||||||
// baz : int
|
|
||||||
// }
|
|
||||||
// struct Foo {
|
|
||||||
// bar : Bar
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Foo {
|
|
||||||
// bar = Bar {
|
|
||||||
// baz = 42
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "
|
|
||||||
// ),
|
|
||||||
// Ok(Some(Value::structure(
|
|
||||||
// Identifier::new("Foo").with_position((172, 175)),
|
|
||||||
// vec![(
|
|
||||||
// Identifier::new("bar"),
|
|
||||||
// Value::structure(
|
|
||||||
// Identifier::new("Bar").with_position((204, 207)),
|
|
||||||
// vec![(Identifier::new("baz"), Value::integer(42))]
|
|
||||||
// )
|
|
||||||
// ),]
|
|
||||||
// )))
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn undefined_struct() {
|
|
||||||
// assert_eq!(
|
|
||||||
// interpret(
|
|
||||||
// "test",
|
|
||||||
// "
|
|
||||||
// Foo {
|
|
||||||
// bar = 42
|
|
||||||
// }
|
|
||||||
// "
|
|
||||||
// )
|
|
||||||
// .unwrap_err()
|
|
||||||
// .errors(),
|
|
||||||
// &vec![DustError::Validation {
|
|
||||||
// error: ValidationError::VariableNotFound {
|
|
||||||
// identifier: Identifier::new("Foo"),
|
|
||||||
// position: (17, 20).into()
|
|
||||||
// },
|
|
||||||
// position: (17, 69).into()
|
|
||||||
// }]
|
|
||||||
// )
|
|
||||||
// }
|
|
@ -1,80 +0,0 @@
|
|||||||
use dust_lang::{
|
|
||||||
error::{DustError, TypeConflict, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
*,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_variable_with_type_error() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foobar: str = true")
|
|
||||||
.unwrap_err()
|
|
||||||
.errors(),
|
|
||||||
&vec![DustError::Validation {
|
|
||||||
error: ValidationError::TypeCheck {
|
|
||||||
conflict: TypeConflict {
|
|
||||||
actual: Type::Boolean,
|
|
||||||
expected: Type::String
|
|
||||||
},
|
|
||||||
actual_position: (14, 18).into(),
|
|
||||||
expected_position: Some((8, 11).into())
|
|
||||||
},
|
|
||||||
position: (0, 18).into()
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_return_type_error() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
foo = fn () -> str { 'foo' }
|
|
||||||
|
|
||||||
bar: int = foo()
|
|
||||||
"
|
|
||||||
)
|
|
||||||
.unwrap_err()
|
|
||||||
.errors(),
|
|
||||||
&vec![DustError::Validation {
|
|
||||||
error: ValidationError::TypeCheck {
|
|
||||||
conflict: TypeConflict {
|
|
||||||
actual: Type::String,
|
|
||||||
expected: Type::Integer
|
|
||||||
},
|
|
||||||
actual_position: (66, 71).into(),
|
|
||||||
expected_position: Some((60, 63).into())
|
|
||||||
},
|
|
||||||
position: (55, 71).into()
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scope() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret(
|
|
||||||
"test",
|
|
||||||
"
|
|
||||||
x = 1
|
|
||||||
|
|
||||||
foo = fn () -> int {
|
|
||||||
x
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
foo()
|
|
||||||
"
|
|
||||||
)
|
|
||||||
.unwrap_err()
|
|
||||||
.errors(),
|
|
||||||
&vec![DustError::Validation {
|
|
||||||
error: ValidationError::VariableNotFound {
|
|
||||||
identifier: Identifier::new("x"),
|
|
||||||
position: (69, 70).into()
|
|
||||||
},
|
|
||||||
position: (32, 102).into()
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,160 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use dust_lang::{
|
|
||||||
error::{DustError, TypeConflict, ValidationError},
|
|
||||||
identifier::Identifier,
|
|
||||||
Type, *,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn none() {
|
|
||||||
assert_eq!(interpret("test", "x = 9"), Ok(None));
|
|
||||||
assert_eq!(interpret("test", "x = 1 + 1"), Ok(None));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn integer() {
|
|
||||||
assert_eq!(interpret("test", "1"), Ok(Some(Value::integer(1))));
|
|
||||||
assert_eq!(interpret("test", "123"), Ok(Some(Value::integer(123))));
|
|
||||||
assert_eq!(interpret("test", "-666"), Ok(Some(Value::integer(-666))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn integer_saturation() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "9223372036854775807 + 1"),
|
|
||||||
Ok(Some(Value::integer(i64::MAX)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "-9223372036854775808 - 1"),
|
|
||||||
Ok(Some(Value::integer(i64::MIN)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn float() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "1.7976931348623157e308"),
|
|
||||||
Ok(Some(Value::float(f64::MAX)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "-1.7976931348623157e308"),
|
|
||||||
Ok(Some(Value::float(f64::MIN)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn float_saturation() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "1.7976931348623157e308 + 1"),
|
|
||||||
Ok(Some(Value::float(f64::MAX)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "-1.7976931348623157e308 - 1"),
|
|
||||||
Ok(Some(Value::float(f64::MIN)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "\"one\""),
|
|
||||||
Ok(Some(Value::string("one".to_string())))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "'one'"),
|
|
||||||
Ok(Some(Value::string("one".to_string())))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "`one`"),
|
|
||||||
Ok(Some(Value::string("one".to_string())))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "`'one'`"),
|
|
||||||
Ok(Some(Value::string("'one'".to_string())))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "'`one`'"),
|
|
||||||
Ok(Some(Value::string("`one`".to_string())))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "\"'one'\""),
|
|
||||||
Ok(Some(Value::string("'one'".to_string())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "[1, 2, 3]"),
|
|
||||||
Ok(Some(Value::list(vec![
|
|
||||||
Value::integer(1),
|
|
||||||
Value::integer(2),
|
|
||||||
Value::integer(3),
|
|
||||||
])))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_list() {
|
|
||||||
assert_eq!(interpret("test", "[]"), Ok(Some(Value::list(Vec::new()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn map() {
|
|
||||||
let mut map = BTreeMap::new();
|
|
||||||
|
|
||||||
map.insert(Identifier::new("x"), Value::integer(1));
|
|
||||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "{ x = 1, foo = 'bar' }"),
|
|
||||||
Ok(Some(Value::map(map)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_map() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "{}"),
|
|
||||||
Ok(Some(Value::map(BTreeMap::new())))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn map_types() {
|
|
||||||
let mut map = BTreeMap::new();
|
|
||||||
|
|
||||||
map.insert(Identifier::new("x"), Value::integer(1));
|
|
||||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "{ x : int = 1, foo : str = 'bar' }"),
|
|
||||||
Ok(Some(Value::map(map)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn map_type_errors() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "{ foo : bool = 'bar' }")
|
|
||||||
.unwrap_err()
|
|
||||||
.errors(),
|
|
||||||
&vec![DustError::Validation {
|
|
||||||
error: ValidationError::TypeCheck {
|
|
||||||
conflict: TypeConflict {
|
|
||||||
actual: Type::String,
|
|
||||||
expected: Type::Boolean
|
|
||||||
},
|
|
||||||
actual_position: (15, 20).into(),
|
|
||||||
expected_position: Some((8, 12).into()),
|
|
||||||
},
|
|
||||||
position: (0, 22).into()
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn range() {
|
|
||||||
assert_eq!(interpret("test", "0..100"), Ok(Some(Value::range(0..100))));
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
use dust_lang::{
|
|
||||||
abstract_tree::{Block, Expression, Statement, WithPos},
|
|
||||||
identifier::Identifier,
|
|
||||||
Type, *,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_and_get_variable() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foobar = true; foobar"),
|
|
||||||
Ok(Some(Value::boolean(true)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_variable_with_type() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foobar: bool = true; foobar"),
|
|
||||||
Ok(Some(Value::boolean(true)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn function_variable() {
|
|
||||||
assert_eq!(
|
|
||||||
interpret("test", "foobar = fn (x: int) -> int { x }; foobar"),
|
|
||||||
Ok(Some(Value::function(
|
|
||||||
None,
|
|
||||||
Some(vec![(Identifier::new("x"), Type::Integer)]),
|
|
||||||
Some(Type::Integer),
|
|
||||||
Block::new(vec![Statement::Expression(Expression::Identifier(
|
|
||||||
Identifier::new("x").with_position((30, 31))
|
|
||||||
))]),
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dust-shell"
|
name = "dust-shell"
|
||||||
description = "Command line shell for the Dust programming language"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
@ -9,12 +8,3 @@ readme.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ariadne = "0.4.0"
|
|
||||||
clap = { version = "4.5.3", features = ["derive"] }
|
|
||||||
colored = "2.1.0"
|
|
||||||
dust-lang = { path = "../dust-lang" }
|
|
||||||
env_logger = "0.11.3"
|
|
||||||
log = "0.4.21"
|
|
||||||
nu-ansi-term = "0.50.0"
|
|
||||||
reedline = { version = "0.30.0", features = ["sqlite", "system_clipboard"] }
|
|
||||||
ron = "0.8.1"
|
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
io::{self, stderr},
|
|
||||||
path::PathBuf,
|
|
||||||
process::Command,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use ariadne::sources;
|
|
||||||
use dust_lang::*;
|
|
||||||
use nu_ansi_term::{Color, Style};
|
|
||||||
use reedline::{
|
|
||||||
default_emacs_keybindings, ColumnarMenu, DefaultHinter, EditCommand, Emacs, KeyCode,
|
|
||||||
KeyModifiers, MenuBuilder, Prompt, Reedline, ReedlineEvent, ReedlineMenu, Signal,
|
|
||||||
SqliteBackedHistory,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn run_shell() -> Result<(), io::Error> {
|
|
||||||
let interpreter = Interpreter::new();
|
|
||||||
let mut keybindings = default_emacs_keybindings();
|
|
||||||
|
|
||||||
keybindings.add_binding(
|
|
||||||
KeyModifiers::CONTROL,
|
|
||||||
KeyCode::Char(' '),
|
|
||||||
ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
|
|
||||||
);
|
|
||||||
keybindings.add_binding(
|
|
||||||
KeyModifiers::NONE,
|
|
||||||
KeyCode::Enter,
|
|
||||||
ReedlineEvent::SubmitOrNewline,
|
|
||||||
);
|
|
||||||
keybindings.add_binding(
|
|
||||||
KeyModifiers::NONE,
|
|
||||||
KeyCode::Tab,
|
|
||||||
ReedlineEvent::Edit(vec![EditCommand::InsertString(" ".to_string())]),
|
|
||||||
);
|
|
||||||
keybindings.add_binding(
|
|
||||||
KeyModifiers::NONE,
|
|
||||||
KeyCode::Tab,
|
|
||||||
ReedlineEvent::Multiple(vec![
|
|
||||||
ReedlineEvent::Menu("context menu".to_string()),
|
|
||||||
ReedlineEvent::MenuNext,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let edit_mode = Box::new(Emacs::new(keybindings));
|
|
||||||
let history = Box::new(
|
|
||||||
SqliteBackedHistory::with_file(PathBuf::from("target/history"), None, None)
|
|
||||||
.expect("Error loading history."),
|
|
||||||
);
|
|
||||||
let hinter = Box::new(DefaultHinter::default().with_style(Style::new().dimmed()));
|
|
||||||
|
|
||||||
let mut line_editor = Reedline::create()
|
|
||||||
.with_edit_mode(edit_mode)
|
|
||||||
.with_history(history)
|
|
||||||
.with_hinter(hinter)
|
|
||||||
.use_kitty_keyboard_enhancement(true)
|
|
||||||
.with_menu(ReedlineMenu::EngineCompleter(Box::new(
|
|
||||||
ColumnarMenu::default()
|
|
||||||
.with_name("context menu")
|
|
||||||
.with_text_style(Style::default().fg(Color::White))
|
|
||||||
.with_columns(1)
|
|
||||||
.with_column_padding(10),
|
|
||||||
)));
|
|
||||||
let mut prompt = StarshipPrompt::new();
|
|
||||||
|
|
||||||
prompt.reload();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let sig = line_editor.read_line(&prompt);
|
|
||||||
|
|
||||||
match sig {
|
|
||||||
Ok(Signal::Success(buffer)) => {
|
|
||||||
if buffer.trim().is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let run_result = interpreter.run(Arc::from("input"), Arc::from(buffer.as_str()));
|
|
||||||
|
|
||||||
match run_result {
|
|
||||||
Ok(Some(value)) => {
|
|
||||||
println!("{value}")
|
|
||||||
}
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(error) => {
|
|
||||||
let reports = error.build_reports();
|
|
||||||
|
|
||||||
for report in reports {
|
|
||||||
let cache = sources(interpreter.sources());
|
|
||||||
report.write_for_stdout(cache, stderr()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt.reload();
|
|
||||||
}
|
|
||||||
Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => {
|
|
||||||
println!("\nLeaving the Dust shell.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
x => {
|
|
||||||
println!("Unknown event: {:?}", x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StarshipPrompt {
|
|
||||||
left: String,
|
|
||||||
right: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StarshipPrompt {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
left: String::new(),
|
|
||||||
right: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reload(&mut self) {
|
|
||||||
let run_starship_left = Command::new("starship").arg("prompt").output();
|
|
||||||
let run_starship_right = Command::new("starship")
|
|
||||||
.args(["prompt", "--right"])
|
|
||||||
.output();
|
|
||||||
let left_prompt = if let Ok(output) = &run_starship_left {
|
|
||||||
String::from_utf8_lossy(&output.stdout).trim().to_string()
|
|
||||||
} else {
|
|
||||||
">".to_string()
|
|
||||||
};
|
|
||||||
let right_prompt = if let Ok(output) = &run_starship_right {
|
|
||||||
String::from_utf8_lossy(&output.stdout).trim().to_string()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.left = left_prompt;
|
|
||||||
self.right = right_prompt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Prompt for StarshipPrompt {
|
|
||||||
fn render_prompt_left(&self) -> Cow<str> {
|
|
||||||
Cow::Borrowed(&self.left)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_prompt_right(&self) -> Cow<str> {
|
|
||||||
Cow::Borrowed(&self.right)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_prompt_indicator(&self, _prompt_mode: reedline::PromptEditMode) -> Cow<str> {
|
|
||||||
Cow::Borrowed(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
|
||||||
Cow::Borrowed("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_prompt_history_search_indicator(
|
|
||||||
&self,
|
|
||||||
_history_search: reedline::PromptHistorySearch,
|
|
||||||
) -> Cow<str> {
|
|
||||||
Cow::Borrowed("")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +1,3 @@
|
|||||||
//! Command line interface for the dust programming language.
|
|
||||||
mod cli;
|
|
||||||
|
|
||||||
use ariadne::sources;
|
|
||||||
use clap::Parser;
|
|
||||||
use cli::run_shell;
|
|
||||||
use colored::Colorize;
|
|
||||||
use log::Level;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::hash_map,
|
|
||||||
fs::read_to_string,
|
|
||||||
io::{stderr, Write},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use dust_lang::Interpreter;
|
|
||||||
|
|
||||||
/// Command-line arguments to be parsed.
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
/// Dust source code to evaluate.
|
|
||||||
#[arg(short, long)]
|
|
||||||
command: Option<String>,
|
|
||||||
|
|
||||||
// Display lexer tokens of the input source.
|
|
||||||
#[arg(short, long)]
|
|
||||||
lex: bool,
|
|
||||||
|
|
||||||
// Display abstract tree of the input source.
|
|
||||||
#[arg(short, long)]
|
|
||||||
parse: bool,
|
|
||||||
|
|
||||||
#[arg(long)]
|
|
||||||
compile: bool,
|
|
||||||
|
|
||||||
/// Location of the file to run.
|
|
||||||
path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::Builder::from_env("DUST_LOG")
|
println!("Hello, world!");
|
||||||
.format(|buffer, record| {
|
|
||||||
let args = record.args();
|
|
||||||
let log_level = match record.level() {
|
|
||||||
Level::Trace => "TRACE".cyan().bold(),
|
|
||||||
Level::Warn => "WARN".yellow().bold(),
|
|
||||||
Level::Debug => "DEBUG".green().bold(),
|
|
||||||
Level::Error => "ERROR".red().bold(),
|
|
||||||
Level::Info => "INFO".white().bold(),
|
|
||||||
};
|
|
||||||
|
|
||||||
writeln!(buffer, "[{}] {}", log_level, args)
|
|
||||||
})
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let args = Args::parse();
|
|
||||||
let interpreter = Interpreter::new();
|
|
||||||
|
|
||||||
let (source_id, source): (Arc<str>, Arc<str>) = if let Some(path) = args.path {
|
|
||||||
let source = read_to_string(&path).unwrap();
|
|
||||||
|
|
||||||
(Arc::from(path.as_str()), Arc::from(source))
|
|
||||||
} else if let Some(command) = args.command {
|
|
||||||
(Arc::from("command"), Arc::from(command))
|
|
||||||
} else {
|
|
||||||
match run_shell() {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(error) => eprintln!("{error}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if args.lex {
|
|
||||||
match interpreter.lex(source_id, &source) {
|
|
||||||
Ok(tokens) => println!("{tokens:?}"),
|
|
||||||
Err(error) => {
|
|
||||||
for report in error.build_reports() {
|
|
||||||
report
|
|
||||||
.write_for_stdout(
|
|
||||||
sources::<Arc<str>, Arc<str>, hash_map::IntoIter<Arc<str>, Arc<str>>>(
|
|
||||||
interpreter.sources(),
|
|
||||||
),
|
|
||||||
stderr(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.parse {
|
|
||||||
match interpreter.parse(source_id, &source) {
|
|
||||||
Ok(abstract_tree) => println!("{abstract_tree:?}"),
|
|
||||||
Err(error) => {
|
|
||||||
for report in error.build_reports() {
|
|
||||||
report
|
|
||||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.compile {
|
|
||||||
match interpreter.parse(source_id, &source) {
|
|
||||||
Ok(abstract_tree) => {
|
|
||||||
let ron = ron::to_string(&abstract_tree).unwrap();
|
|
||||||
|
|
||||||
println!("{ron}")
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
for report in error.build_reports() {
|
|
||||||
report
|
|
||||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let run_result = interpreter.run(source_id.clone(), source);
|
|
||||||
|
|
||||||
match run_result {
|
|
||||||
Ok(value) => {
|
|
||||||
if let Some(value) = value {
|
|
||||||
println!("{value}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
for report in error.build_reports() {
|
|
||||||
report
|
|
||||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user