Overhaul project structure

This commit is contained in:
Jeff 2024-08-03 20:23:52 -04:00
parent 473f0ee075
commit cc188a233b
51 changed files with 413 additions and 11549 deletions

913
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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)
}
}

View File

@ -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}")
}
}

View File

@ -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, "}}")
}
}

View File

@ -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))
)
}
}

View File

@ -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)
}
}

View File

@ -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>>,
}

View File

@ -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),
}
}
}

View File

@ -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!()
}
}

View File

@ -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
}
}
}

View File

@ -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())))
)
}
}

View File

@ -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}]")
}
}

View File

@ -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))))
)
}
}

View File

@ -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, " }}")
}
}

View File

@ -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}")
}
}

View File

@ -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}"),
}
}
}

View File

@ -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>;
}

View File

@ -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),
}
}
}

View File

@ -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, " }}")
}
}

View File

@ -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)
}
}

View File

@ -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(())
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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, "}}")
}
}

View File

@ -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))
)))
)))
))))
);
}
}

View File

@ -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()
}
}

View File

@ -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,
}

View File

@ -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
View 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)))
}
}

View File

@ -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"));
}
}

View File

@ -7,21 +7,16 @@ The [interpreter] module contains the `Interpreter` struct, which is used to lex
interpret Dust code. The `interpret` function is a convenience function that creates a new interpret Dust code. The `interpret` function is a convenience function that creates a new
`Interpreter` and runs the given source code. `Interpreter` and runs the given source code.
*/ */
pub mod abstract_tree;
pub mod bytecode;
pub mod context;
pub mod error;
pub mod identifier; pub mod identifier;
pub mod interpreter; pub mod lex;
pub mod lexer; pub mod parse;
pub mod parser; pub mod token;
pub mod standard_library;
pub mod r#type; pub mod r#type;
pub mod value; pub mod value;
pub use context::Context; pub use identifier::Identifier;
pub use interpreter::{interpret, Interpreter, InterpreterError};
pub use lexer::{lex, lexer};
pub use parser::{parse, parser};
pub use r#type::Type; pub use r#type::Type;
pub use token::Token;
pub use value::Value; pub use value::Value;
pub type Span = (usize, usize);

243
dust-lang/src/parse.rs Normal file
View 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)
))
);
}
}

View File

@ -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

View File

@ -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
View 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,
}

View File

@ -19,7 +19,13 @@ use std::{
use clap::error::Result; use clap::error::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::TypeConflict, identifier::Identifier}; use crate::identifier::Identifier;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TypeConflict {
pub expected: Type,
pub actual: Type,
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
/// Description of a kind of value. /// Description of a kind of value.

View File

@ -7,20 +7,13 @@ use std::{
}; };
use chumsky::container::Container; use chumsky::container::Container;
use log::{debug, trace};
use serde::{ use serde::{
de::Visitor, de::Visitor,
ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple}, ser::{SerializeMap, SerializeSeq, SerializeTuple},
Deserialize, Deserializer, Serialize, Deserialize, Deserializer, Serialize,
}; };
use crate::{ use crate::{identifier::Identifier, Type};
abstract_tree::{AbstractNode, Block, BuiltInFunction, Evaluation, SourcePosition},
context::Context,
error::{RuntimeError, ValidationError},
identifier::Identifier,
Type,
};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Value(Arc<ValueInner>); pub struct Value(Arc<ValueInner>);
@ -34,24 +27,6 @@ impl Value {
Value(Arc::new(ValueInner::Boolean(boolean))) Value(Arc::new(ValueInner::Boolean(boolean)))
} }
pub fn built_in_function(function: BuiltInFunction) -> Self {
Value(Arc::new(ValueInner::BuiltInFunction(function)))
}
pub fn enum_instance(
type_name: Identifier,
type_arguments: Option<Vec<Type>>,
variant: Identifier,
content: Option<Value>,
) -> Self {
Value(Arc::new(ValueInner::EnumInstance {
type_name,
type_arguments,
variant,
content,
}))
}
pub fn float(float: f64) -> Self { pub fn float(float: f64) -> Self {
Value(Arc::new(ValueInner::Float(float))) Value(Arc::new(ValueInner::Float(float)))
} }
@ -76,26 +51,8 @@ impl Value {
Value(Arc::new(ValueInner::String(to_string.to_string()))) Value(Arc::new(ValueInner::String(to_string.to_string())))
} }
pub fn function( pub fn r#type(&self) -> Type {
type_parameters: Option<Vec<Identifier>>, self.0.r#type()
value_parameters: Option<Vec<(Identifier, Type)>>,
return_type: Option<Type>,
body: Block,
) -> Self {
Value(Arc::new(ValueInner::Function(Function::new(
type_parameters,
value_parameters,
return_type,
body,
))))
}
pub fn structure(name: Identifier, fields: Vec<(Identifier, Value)>) -> Self {
Value(Arc::new(ValueInner::Structure { name, fields }))
}
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
self.0.r#type(context)
} }
pub fn as_boolean(&self) -> Option<bool> { pub fn as_boolean(&self) -> Option<bool> {
@ -127,32 +84,6 @@ impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.inner().as_ref() { match self.inner().as_ref() {
ValueInner::Boolean(boolean) => write!(f, "{boolean}"), ValueInner::Boolean(boolean) => write!(f, "{boolean}"),
ValueInner::EnumInstance {
type_name,
type_arguments,
variant,
content,
} => {
write!(f, "{type_name}::")?;
if let Some(types) = type_arguments {
write!(f, "::<")?;
for r#type in types {
write!(f, "{type}, ")?;
}
write!(f, ">")?;
}
write!(f, "::{variant}(")?;
if let Some(value) = content {
write!(f, "{value}")?;
}
write!(f, ")")
}
ValueInner::Float(float) => { ValueInner::Float(float) => {
write!(f, "{float}")?; write!(f, "{float}")?;
@ -191,55 +122,6 @@ impl Display for Value {
} }
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end), ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
ValueInner::String(string) => write!(f, "{string}"), ValueInner::String(string) => write!(f, "{string}"),
ValueInner::Function(Function {
type_parameters,
value_parameters,
return_type,
body,
..
}) => {
write!(f, "fn ")?;
if let Some(type_parameters) = type_parameters {
write!(f, "<")?;
for (index, identifier) in type_parameters.iter().enumerate() {
if index == type_parameters.len() - 1 {
write!(f, "{}", identifier)?;
} else {
write!(f, "{} ", identifier)?;
}
}
write!(f, ">")?;
}
write!(f, "(")?;
if let Some(value_parameters) = value_parameters {
for (identifier, r#type) in value_parameters {
write!(f, "{identifier}: {}", r#type)?;
}
}
write!(f, ")")?;
if let Some(return_type) = return_type {
write!(f, " -> {return_type}")?
}
write!(f, " {body}")
}
ValueInner::Structure { name, fields } => {
write!(f, "{name} {{")?;
for (key, value) in fields {
write!(f, "{key} = {value},")?;
}
write!(f, "}}")
}
ValueInner::BuiltInFunction(built_in_function) => write!(f, "{built_in_function}"),
} }
} }
} }
@ -265,38 +147,7 @@ impl Serialize for Value {
{ {
match self.0.as_ref() { match self.0.as_ref() {
ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean), ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean),
ValueInner::EnumInstance {
type_name,
type_arguments,
variant,
content,
} => {
let mut struct_ser = serializer.serialize_struct("EnumInstance", 3)?;
struct_ser.serialize_field("type_name", type_name)?;
struct_ser.serialize_field("type_arguments", type_arguments)?;
struct_ser.serialize_field("variant", variant)?;
struct_ser.serialize_field("content", content)?;
struct_ser.end()
}
ValueInner::Float(float) => serializer.serialize_f64(*float), ValueInner::Float(float) => serializer.serialize_f64(*float),
ValueInner::Function(Function {
type_parameters,
value_parameters,
return_type,
body,
..
}) => {
let mut struct_ser = serializer.serialize_struct("Function", 4)?;
struct_ser.serialize_field("type_parameters", type_parameters)?;
struct_ser.serialize_field("value_parameters", value_parameters)?;
struct_ser.serialize_field("return_type", return_type)?;
struct_ser.serialize_field("body", body)?;
struct_ser.end()
}
ValueInner::Integer(integer) => serializer.serialize_i64(*integer), ValueInner::Integer(integer) => serializer.serialize_i64(*integer),
ValueInner::List(list) => { ValueInner::List(list) => {
let mut list_ser = serializer.serialize_seq(Some(list.len()))?; let mut list_ser = serializer.serialize_seq(Some(list.len()))?;
@ -325,15 +176,6 @@ impl Serialize for Value {
tuple_ser.end() tuple_ser.end()
} }
ValueInner::String(string) => serializer.serialize_str(string), ValueInner::String(string) => serializer.serialize_str(string),
ValueInner::Structure { name, fields } => {
let mut struct_ser = serializer.serialize_struct("Structure", 2)?;
struct_ser.serialize_field("name", name)?;
struct_ser.serialize_field("fields", fields)?;
struct_ser.end()
}
ValueInner::BuiltInFunction(_) => todo!(),
} }
} }
} }
@ -583,44 +425,22 @@ impl<'de> Deserialize<'de> for Value {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ValueInner { pub enum ValueInner {
Boolean(bool), Boolean(bool),
BuiltInFunction(BuiltInFunction),
EnumInstance {
type_name: Identifier,
type_arguments: Option<Vec<Type>>,
variant: Identifier,
content: Option<Value>,
},
Float(f64), Float(f64),
Function(Function),
Integer(i64), Integer(i64),
List(Vec<Value>), List(Vec<Value>),
Map(BTreeMap<Identifier, Value>), Map(BTreeMap<Identifier, Value>),
Range(Range<i64>), Range(Range<i64>),
String(String), String(String),
Structure {
name: Identifier,
fields: Vec<(Identifier, Value)>,
},
} }
impl ValueInner { impl ValueInner {
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> { pub fn r#type(&self) -> Type {
let r#type = match self { match self {
ValueInner::Boolean(_) => Type::Boolean, ValueInner::Boolean(_) => Type::Boolean,
ValueInner::EnumInstance { type_name, .. } => {
if let Some(r#type) = context.get_type(type_name)? {
r#type
} else {
return Err(ValidationError::EnumDefinitionNotFound {
identifier: type_name.clone(),
position: None,
});
}
}
ValueInner::Float(_) => Type::Float, ValueInner::Float(_) => Type::Float,
ValueInner::Integer(_) => Type::Integer, ValueInner::Integer(_) => Type::Integer,
ValueInner::List(values) => { ValueInner::List(values) => {
let item_type = values.first().unwrap().r#type(context)?; let item_type = values.first().unwrap().r#type();
Type::List { Type::List {
length: values.len(), length: values.len(),
@ -631,7 +451,7 @@ impl ValueInner {
let mut type_map = BTreeMap::with_capacity(value_map.len()); let mut type_map = BTreeMap::with_capacity(value_map.len());
for (identifier, value) in value_map { for (identifier, value) in value_map {
let r#type = value.r#type(context)?; let r#type = value.r#type();
type_map.insert(identifier.clone(), r#type); type_map.insert(identifier.clone(), r#type);
} }
@ -640,30 +460,8 @@ impl ValueInner {
} }
ValueInner::Range(_) => Type::Range, ValueInner::Range(_) => Type::Range,
ValueInner::String(_) => Type::String, ValueInner::String(_) => Type::String,
ValueInner::Function(function) => {
let return_type = function.return_type.clone().map(Box::new);
Type::Function {
type_parameters: function.type_parameters().clone(),
value_parameters: function.value_parameters().clone(),
return_type,
} }
} }
ValueInner::Structure { name, .. } => {
if let Some(r#type) = context.get_type(name)? {
r#type
} else {
return Err(ValidationError::StructDefinitionNotFound {
identifier: name.clone(),
position: None,
});
}
}
ValueInner::BuiltInFunction(function) => function.r#type(),
};
Ok(r#type)
}
} }
impl Eq for ValueInner {} impl Eq for ValueInner {}
@ -701,199 +499,6 @@ impl Ord for ValueInner {
(Range(_), _) => Ordering::Greater, (Range(_), _) => Ordering::Greater,
(String(left), String(right)) => left.cmp(right), (String(left), String(right)) => left.cmp(right),
(String(_), _) => Ordering::Greater, (String(_), _) => Ordering::Greater,
(
EnumInstance {
type_name: left_name,
type_arguments: left_arguments,
variant: left_variant,
content: left_content,
},
EnumInstance {
type_name: right_name,
type_arguments: right_arguments,
variant: right_variant,
content: right_content,
},
) => {
let name_cmp = left_name.cmp(right_name);
if name_cmp.is_eq() {
let argument_cmp = left_arguments.cmp(right_arguments);
if argument_cmp.is_eq() {
let variant_cmp = left_variant.cmp(right_variant);
if variant_cmp.is_eq() {
left_content.cmp(right_content)
} else {
variant_cmp
}
} else {
argument_cmp
}
} else {
name_cmp
}
}
(EnumInstance { .. }, _) => Ordering::Greater,
(Function(left), Function(right)) => left.cmp(right),
(Function(_), _) => Ordering::Greater,
(
Structure {
name: left_name,
fields: left_fields,
},
Structure {
name: right_name,
fields: right_fields,
},
) => {
let name_cmp = left_name.cmp(right_name);
if name_cmp.is_eq() {
left_fields.cmp(right_fields)
} else {
name_cmp
}
}
(Structure { .. }, _) => Ordering::Greater,
(BuiltInFunction(left), BuiltInFunction(right)) => left.cmp(right),
(BuiltInFunction(_), _) => Ordering::Greater,
}
}
}
#[derive(Debug)]
pub struct Function {
type_parameters: Option<Vec<Identifier>>,
value_parameters: Option<Vec<(Identifier, Type)>>,
return_type: Option<Type>,
body: Block,
context: Context,
}
impl Function {
pub fn new(
type_parameters: Option<Vec<Identifier>>,
value_parameters: Option<Vec<(Identifier, Type)>>,
return_type: Option<Type>,
body: Block,
) -> Self {
Self {
type_parameters,
value_parameters,
return_type,
body,
context: Context::new(),
}
}
pub fn type_parameters(&self) -> &Option<Vec<Identifier>> {
&self.type_parameters
}
pub fn value_parameters(&self) -> &Option<Vec<(Identifier, Type)>> {
&self.value_parameters
}
pub fn body(&self) -> &Block {
&self.body
}
pub fn call(
self,
outer_context: Option<&Context>,
type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>,
) -> Result<Option<Evaluation>, RuntimeError> {
trace!("Setting function call variables");
if let Some(outer_context) = outer_context {
if &self.context == outer_context {
log::debug!("Recursion detected");
}
self.context.inherit_variables_from(outer_context)?;
}
if let (Some(type_parameters), Some(type_arguments)) =
(self.type_parameters, type_arguments)
{
for (identifier, r#type) in type_parameters.into_iter().zip(type_arguments.into_iter())
{
self.context
.set_type(identifier.clone(), r#type, SourcePosition(0, usize::MAX))?;
}
}
if let (Some(value_parameters), Some(value_arguments)) =
(self.value_parameters, value_arguments)
{
for ((identifier, _), value) in value_parameters
.into_iter()
.zip(value_arguments.into_iter())
{
self.context
.set_value(identifier.clone(), value, SourcePosition(0, usize::MAX))?;
}
}
debug!("Calling function");
self.body
.evaluate(&self.context, false, SourcePosition(0, usize::MAX))
}
}
impl Clone for Function {
fn clone(&self) -> Self {
Function {
type_parameters: self.type_parameters.clone(),
value_parameters: self.value_parameters.clone(),
return_type: self.return_type.clone(),
body: self.body.clone(),
context: Context::new(),
}
}
}
impl Eq for Function {}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.type_parameters == other.type_parameters
&& self.value_parameters == other.value_parameters
&& self.return_type == other.return_type
&& self.body == other.body
}
}
impl PartialOrd for Function {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Function {
fn cmp(&self, other: &Self) -> Ordering {
let type_param_cmp = self.type_parameters().cmp(&other.type_parameters);
if type_param_cmp.is_eq() {
let value_param_cmp = self.value_parameters.cmp(&other.value_parameters);
if value_param_cmp.is_eq() {
let return_type_cmp = self.return_type.cmp(&other.return_type);
if return_type_cmp.is_eq() {
self.body.cmp(&other.body)
} else {
return_type_cmp
}
} else {
value_param_cmp
}
} else {
type_param_cmp
} }
} }
} }

View File

@ -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)),
)))
);
}

View File

@ -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))
);
}

View File

@ -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()
}]
))
);
}

View File

@ -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())))
)
}

View File

@ -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()
// }]
// )
// }

View File

@ -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()
}]
);
}

View File

@ -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))));
}

View File

@ -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))
))]),
)))
);
}

View File

@ -1,6 +1,5 @@
[package] [package]
name = "dust-shell" name = "dust-shell"
description = "Command line shell for the Dust programming language"
version = "0.1.0" version = "0.1.0"
authors.workspace = true authors.workspace = true
edition.workspace = true edition.workspace = true
@ -9,12 +8,3 @@ readme.workspace = true
repository.workspace = true repository.workspace = true
[dependencies] [dependencies]
ariadne = "0.4.0"
clap = { version = "4.5.3", features = ["derive"] }
colored = "2.1.0"
dust-lang = { path = "../dust-lang" }
env_logger = "0.11.3"
log = "0.4.21"
nu-ansi-term = "0.50.0"
reedline = { version = "0.30.0", features = ["sqlite", "system_clipboard"] }
ron = "0.8.1"

View File

@ -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("")
}
}

View File

@ -1,146 +1,3 @@
//! Command line interface for the dust programming language.
mod cli;
use ariadne::sources;
use clap::Parser;
use cli::run_shell;
use colored::Colorize;
use log::Level;
use std::{
collections::hash_map,
fs::read_to_string,
io::{stderr, Write},
sync::Arc,
};
use dust_lang::Interpreter;
/// Command-line arguments to be parsed.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Dust source code to evaluate.
#[arg(short, long)]
command: Option<String>,
// Display lexer tokens of the input source.
#[arg(short, long)]
lex: bool,
// Display abstract tree of the input source.
#[arg(short, long)]
parse: bool,
#[arg(long)]
compile: bool,
/// Location of the file to run.
path: Option<String>,
}
fn main() { fn main() {
env_logger::Builder::from_env("DUST_LOG") println!("Hello, world!");
.format(|buffer, record| {
let args = record.args();
let log_level = match record.level() {
Level::Trace => "TRACE".cyan().bold(),
Level::Warn => "WARN".yellow().bold(),
Level::Debug => "DEBUG".green().bold(),
Level::Error => "ERROR".red().bold(),
Level::Info => "INFO".white().bold(),
};
writeln!(buffer, "[{}] {}", log_level, args)
})
.init();
let args = Args::parse();
let interpreter = Interpreter::new();
let (source_id, source): (Arc<str>, Arc<str>) = if let Some(path) = args.path {
let source = read_to_string(&path).unwrap();
(Arc::from(path.as_str()), Arc::from(source))
} else if let Some(command) = args.command {
(Arc::from("command"), Arc::from(command))
} else {
match run_shell() {
Ok(_) => {}
Err(error) => eprintln!("{error}"),
}
return;
};
if args.lex {
match interpreter.lex(source_id, &source) {
Ok(tokens) => println!("{tokens:?}"),
Err(error) => {
for report in error.build_reports() {
report
.write_for_stdout(
sources::<Arc<str>, Arc<str>, hash_map::IntoIter<Arc<str>, Arc<str>>>(
interpreter.sources(),
),
stderr(),
)
.unwrap();
}
}
}
return;
}
if args.parse {
match interpreter.parse(source_id, &source) {
Ok(abstract_tree) => println!("{abstract_tree:?}"),
Err(error) => {
for report in error.build_reports() {
report
.write_for_stdout(sources(interpreter.sources()), stderr())
.unwrap();
}
}
}
return;
}
if args.compile {
match interpreter.parse(source_id, &source) {
Ok(abstract_tree) => {
let ron = ron::to_string(&abstract_tree).unwrap();
println!("{ron}")
}
Err(error) => {
for report in error.build_reports() {
report
.write_for_stdout(sources(interpreter.sources()), stderr())
.unwrap();
}
}
}
return;
}
let run_result = interpreter.run(source_id.clone(), source);
match run_result {
Ok(value) => {
if let Some(value) = value {
println!("{value}")
}
}
Err(error) => {
for report in error.build_reports() {
report
.write_for_stdout(sources(interpreter.sources()), stderr())
.unwrap();
}
}
}
} }