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
|
||||
`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 interpreter;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod standard_library;
|
||||
pub mod lex;
|
||||
pub mod parse;
|
||||
pub mod token;
|
||||
pub mod r#type;
|
||||
pub mod value;
|
||||
|
||||
pub use context::Context;
|
||||
pub use interpreter::{interpret, Interpreter, InterpreterError};
|
||||
pub use lexer::{lex, lexer};
|
||||
pub use parser::{parse, parser};
|
||||
pub use identifier::Identifier;
|
||||
pub use r#type::Type;
|
||||
pub use token::Token;
|
||||
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 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)]
|
||||
/// Description of a kind of value.
|
||||
|
@ -7,20 +7,13 @@ use std::{
|
||||
};
|
||||
|
||||
use chumsky::container::Container;
|
||||
use log::{debug, trace};
|
||||
use serde::{
|
||||
de::Visitor,
|
||||
ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple},
|
||||
ser::{SerializeMap, SerializeSeq, SerializeTuple},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
abstract_tree::{AbstractNode, Block, BuiltInFunction, Evaluation, SourcePosition},
|
||||
context::Context,
|
||||
error::{RuntimeError, ValidationError},
|
||||
identifier::Identifier,
|
||||
Type,
|
||||
};
|
||||
use crate::{identifier::Identifier, Type};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Value(Arc<ValueInner>);
|
||||
@ -34,24 +27,6 @@ impl Value {
|
||||
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 {
|
||||
Value(Arc::new(ValueInner::Float(float)))
|
||||
}
|
||||
@ -76,26 +51,8 @@ impl Value {
|
||||
Value(Arc::new(ValueInner::String(to_string.to_string())))
|
||||
}
|
||||
|
||||
pub fn function(
|
||||
type_parameters: Option<Vec<Identifier>>,
|
||||
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 r#type(&self) -> Type {
|
||||
self.0.r#type()
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> Option<bool> {
|
||||
@ -127,32 +84,6 @@ impl Display for Value {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self.inner().as_ref() {
|
||||
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) => {
|
||||
write!(f, "{float}")?;
|
||||
|
||||
@ -191,55 +122,6 @@ impl Display for Value {
|
||||
}
|
||||
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
||||
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() {
|
||||
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::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::List(list) => {
|
||||
let mut list_ser = serializer.serialize_seq(Some(list.len()))?;
|
||||
@ -325,15 +176,6 @@ impl Serialize for Value {
|
||||
tuple_ser.end()
|
||||
}
|
||||
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)]
|
||||
pub enum ValueInner {
|
||||
Boolean(bool),
|
||||
BuiltInFunction(BuiltInFunction),
|
||||
EnumInstance {
|
||||
type_name: Identifier,
|
||||
type_arguments: Option<Vec<Type>>,
|
||||
variant: Identifier,
|
||||
content: Option<Value>,
|
||||
},
|
||||
Float(f64),
|
||||
Function(Function),
|
||||
Integer(i64),
|
||||
List(Vec<Value>),
|
||||
Map(BTreeMap<Identifier, Value>),
|
||||
Range(Range<i64>),
|
||||
String(String),
|
||||
Structure {
|
||||
name: Identifier,
|
||||
fields: Vec<(Identifier, Value)>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ValueInner {
|
||||
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||
let r#type = match self {
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
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::Integer(_) => Type::Integer,
|
||||
ValueInner::List(values) => {
|
||||
let item_type = values.first().unwrap().r#type(context)?;
|
||||
let item_type = values.first().unwrap().r#type();
|
||||
|
||||
Type::List {
|
||||
length: values.len(),
|
||||
@ -631,7 +451,7 @@ impl ValueInner {
|
||||
let mut type_map = BTreeMap::with_capacity(value_map.len());
|
||||
|
||||
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);
|
||||
}
|
||||
@ -640,30 +460,8 @@ impl ValueInner {
|
||||
}
|
||||
ValueInner::Range(_) => Type::Range,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ValueInner {}
|
||||
@ -701,199 +499,6 @@ impl Ord for ValueInner {
|
||||
(Range(_), _) => Ordering::Greater,
|
||||
(String(left), String(right)) => left.cmp(right),
|
||||
(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]
|
||||
name = "dust-shell"
|
||||
description = "Command line shell for the Dust programming language"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
@ -9,12 +8,3 @@ readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[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() {
|
||||
env_logger::Builder::from_env("DUST_LOG")
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user