Start new rewrite of Dust
This commit is contained in:
parent
5dcdfe83f9
commit
1ecaac0819
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,192 +0,0 @@
|
|||||||
//! In-memory representation of a Dust program.
|
|
||||||
mod expression;
|
|
||||||
mod statement;
|
|
||||||
|
|
||||||
pub use expression::*;
|
|
||||||
pub use statement::*;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
num::TryFromIntError,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{core_library, Context, ContextError};
|
|
||||||
|
|
||||||
pub type Span = (usize, usize);
|
|
||||||
|
|
||||||
/// In-memory representation of a Dust program.
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
|
||||||
pub struct AbstractSyntaxTree {
|
|
||||||
pub statements: VecDeque<Statement>,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub context: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for AbstractSyntaxTree {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.debug_struct("AbstractSyntaxTree")
|
|
||||||
.field("statements", &self.statements)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for AbstractSyntaxTree {}
|
|
||||||
|
|
||||||
impl PartialEq for AbstractSyntaxTree {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.statements == other.statements
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for AbstractSyntaxTree {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for AbstractSyntaxTree {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.statements.cmp(&other.statements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractSyntaxTree {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
statements: VecDeque::new(),
|
|
||||||
context: Context::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_statements<T: Into<VecDeque<Statement>>>(statements: T) -> Self {
|
|
||||||
Self {
|
|
||||||
statements: statements.into(),
|
|
||||||
context: Context::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_core_library() -> Self {
|
|
||||||
Self {
|
|
||||||
statements: VecDeque::new(),
|
|
||||||
context: core_library().create_child(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AbstractSyntaxTree {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Node<T> {
|
|
||||||
pub inner: T,
|
|
||||||
pub position: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Node<T> {
|
|
||||||
pub fn new(inner: T, position: Span) -> Self {
|
|
||||||
Self { inner, position }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Display> Display for Node<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum AstError {
|
|
||||||
ContextError {
|
|
||||||
error: ContextError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedFunctionOrConstructor {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedInteger {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedListType {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedNonEmptyEvaluation {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedNonEmptyList {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedRangeableType {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedStructFieldsType {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedTupleType {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
FromIntError {
|
|
||||||
error: TryFromIntError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AstError {
|
|
||||||
pub fn position(&self) -> Span {
|
|
||||||
match self {
|
|
||||||
AstError::ContextError { position, .. } => *position,
|
|
||||||
AstError::ExpectedFunctionOrConstructor { position } => *position,
|
|
||||||
AstError::ExpectedInteger { position } => *position,
|
|
||||||
AstError::ExpectedListType { position } => *position,
|
|
||||||
AstError::ExpectedNonEmptyEvaluation { position } => *position,
|
|
||||||
AstError::ExpectedNonEmptyList { position } => *position,
|
|
||||||
AstError::ExpectedRangeableType { position } => *position,
|
|
||||||
AstError::ExpectedStructFieldsType { position } => *position,
|
|
||||||
AstError::ExpectedTupleType { position } => *position,
|
|
||||||
AstError::FromIntError { position, .. } => *position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AstError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
AstError::ContextError { error, position } => {
|
|
||||||
write!(f, "Context error at {:?}: {}", position, error)
|
|
||||||
}
|
|
||||||
AstError::ExpectedFunctionOrConstructor { position } => {
|
|
||||||
write!(f, "Expected a function or constructor at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedInteger { position } => {
|
|
||||||
write!(f, "Expected an integer at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedListType { position } => {
|
|
||||||
write!(f, "Expected a type at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedTupleType { position } => {
|
|
||||||
write!(f, "Expected a tuple type at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedNonEmptyEvaluation { position } => {
|
|
||||||
write!(f, "Expected a type at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedNonEmptyList { position } => {
|
|
||||||
write!(f, "Expected a non-empty list at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedRangeableType { position } => {
|
|
||||||
write!(f, "Expected a rangeable type at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::ExpectedStructFieldsType { position } => {
|
|
||||||
write!(f, "Expected a struct type with fields at {:?}", position)
|
|
||||||
}
|
|
||||||
AstError::FromIntError { error, position } => {
|
|
||||||
write!(f, "Integer conversion error at {:?}: {}", position, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,187 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{Context, Identifier, Type, TypeEvaluation};
|
|
||||||
|
|
||||||
use super::{AstError, Expression, Node, Span};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum Statement {
|
|
||||||
Expression(Expression),
|
|
||||||
ExpressionNullified(Node<Expression>),
|
|
||||||
Let(Node<LetStatement>),
|
|
||||||
StructDefinition(Node<StructDefinition>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Statement {
|
|
||||||
pub fn struct_definition(struct_definition: StructDefinition, position: Span) -> Self {
|
|
||||||
Statement::StructDefinition(Node::new(struct_definition, position))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(&self) -> Span {
|
|
||||||
match self {
|
|
||||||
Statement::Expression(expression) => expression.position(),
|
|
||||||
Statement::ExpressionNullified(expression_node) => expression_node.position,
|
|
||||||
Statement::Let(r#let) => r#let.position,
|
|
||||||
Statement::StructDefinition(definition) => definition.position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_evaluation(&self, context: &Context) -> Result<TypeEvaluation, AstError> {
|
|
||||||
match self {
|
|
||||||
Statement::Expression(expression) => expression.type_evaluation(context),
|
|
||||||
Statement::ExpressionNullified(expression_node) => {
|
|
||||||
let type_evaluation = expression_node.inner.type_evaluation(context)?;
|
|
||||||
|
|
||||||
if let TypeEvaluation::Break(_) = type_evaluation {
|
|
||||||
Ok(type_evaluation)
|
|
||||||
} else {
|
|
||||||
Ok(TypeEvaluation::Return(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::Let(_) => Ok(TypeEvaluation::Return(None)),
|
|
||||||
Statement::StructDefinition(_) => Ok(TypeEvaluation::Return(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Statement {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Statement::Expression(expression) => write!(f, "{}", expression),
|
|
||||||
Statement::ExpressionNullified(expression) => write!(f, "{};", expression),
|
|
||||||
Statement::Let(r#let) => write!(f, "{};", r#let),
|
|
||||||
Statement::StructDefinition(struct_definition) => match &struct_definition.inner {
|
|
||||||
StructDefinition::Unit { name } => write!(f, "struct {};", name),
|
|
||||||
StructDefinition::Tuple { name, items } => {
|
|
||||||
write!(f, "struct {name} {{ ")?;
|
|
||||||
|
|
||||||
for (index, item) in items.iter().enumerate() {
|
|
||||||
write!(f, "{}: {}", item, index)?;
|
|
||||||
|
|
||||||
if index < items.len() - 1 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
StructDefinition::Fields { name, fields } => {
|
|
||||||
write!(f, "struct {name} {{ ")?;
|
|
||||||
|
|
||||||
for (index, (field, r#type)) in fields.iter().enumerate() {
|
|
||||||
write!(f, "{}: {}", field, r#type)?;
|
|
||||||
|
|
||||||
if index < fields.len() - 1 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, " }}")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum LetStatement {
|
|
||||||
Let {
|
|
||||||
identifier: Node<Identifier>,
|
|
||||||
value: Expression,
|
|
||||||
},
|
|
||||||
LetMut {
|
|
||||||
identifier: Node<Identifier>,
|
|
||||||
value: Expression,
|
|
||||||
},
|
|
||||||
LetType {
|
|
||||||
identifier: Node<Identifier>,
|
|
||||||
r#type: Node<Type>,
|
|
||||||
value: Expression,
|
|
||||||
},
|
|
||||||
LetMutType {
|
|
||||||
identifier: Node<Identifier>,
|
|
||||||
r#type: Node<Type>,
|
|
||||||
value: Expression,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for LetStatement {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
LetStatement::Let { identifier, value } => {
|
|
||||||
write!(f, "let {identifier} = {value}")
|
|
||||||
}
|
|
||||||
LetStatement::LetMut { identifier, value } => {
|
|
||||||
write!(f, "let mut {identifier} = {value}")
|
|
||||||
}
|
|
||||||
LetStatement::LetType {
|
|
||||||
identifier,
|
|
||||||
r#type,
|
|
||||||
value,
|
|
||||||
} => {
|
|
||||||
write!(f, "let {identifier}: {type} = {value}")
|
|
||||||
}
|
|
||||||
LetStatement::LetMutType {
|
|
||||||
identifier,
|
|
||||||
r#type,
|
|
||||||
value,
|
|
||||||
} => {
|
|
||||||
write!(f, "let mut {identifier}: {type} = {value}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum StructDefinition {
|
|
||||||
Unit {
|
|
||||||
name: Node<Identifier>,
|
|
||||||
},
|
|
||||||
Tuple {
|
|
||||||
name: Node<Identifier>,
|
|
||||||
items: Vec<Node<Type>>,
|
|
||||||
},
|
|
||||||
Fields {
|
|
||||||
name: Node<Identifier>,
|
|
||||||
fields: Vec<(Node<Identifier>, Node<Type>)>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for StructDefinition {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
StructDefinition::Unit { name } => write!(f, "struct {name}"),
|
|
||||||
StructDefinition::Tuple {
|
|
||||||
name,
|
|
||||||
items: fields,
|
|
||||||
} => {
|
|
||||||
write!(f, "struct {name} {{")?;
|
|
||||||
|
|
||||||
for (i, field) in fields.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{field}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
StructDefinition::Fields { name, fields } => {
|
|
||||||
write!(f, "struct {name} {{")?;
|
|
||||||
|
|
||||||
for (i, (field_name, field_type)) in fields.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{field_name}: {field_type}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,208 +0,0 @@
|
|||||||
//! Integrated functions that can be called from Dust code.
|
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
io::{self, stdin, stdout, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{FunctionType, Identifier, Type, Value, ValueData, ValueError};
|
|
||||||
|
|
||||||
/// Integrated function that can be called from Dust code.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum BuiltInFunction {
|
|
||||||
// String tools
|
|
||||||
ToString,
|
|
||||||
|
|
||||||
// Integer and float tools
|
|
||||||
IsEven,
|
|
||||||
IsOdd,
|
|
||||||
|
|
||||||
// I/O
|
|
||||||
ReadLine,
|
|
||||||
WriteLine,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltInFunction {
|
|
||||||
pub fn name(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::IsEven => "is_even",
|
|
||||||
BuiltInFunction::IsOdd => "is_odd",
|
|
||||||
BuiltInFunction::ReadLine => "read_line",
|
|
||||||
BuiltInFunction::ToString { .. } => "to_string",
|
|
||||||
BuiltInFunction::WriteLine => "write_line",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_parameters(&self) -> Option<Vec<Identifier>> {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::ToString { .. } => None,
|
|
||||||
BuiltInFunction::IsEven => None,
|
|
||||||
BuiltInFunction::IsOdd => None,
|
|
||||||
BuiltInFunction::ReadLine => None,
|
|
||||||
BuiltInFunction::WriteLine => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value_parameters(&self) -> Option<Vec<(Identifier, Type)>> {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]),
|
|
||||||
BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]),
|
|
||||||
BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]),
|
|
||||||
BuiltInFunction::ReadLine => None,
|
|
||||||
BuiltInFunction::WriteLine => Some(vec![("value".into(), Type::Any)]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn return_type(&self) -> Option<Type> {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::ToString { .. } => Some(Type::String { length: None }),
|
|
||||||
BuiltInFunction::IsEven => Some(Type::Boolean),
|
|
||||||
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
|
||||||
BuiltInFunction::ReadLine => Some(Type::String { length: None }),
|
|
||||||
BuiltInFunction::WriteLine => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
Type::Function(FunctionType {
|
|
||||||
name: Identifier::new(self.name()),
|
|
||||||
type_parameters: self.type_parameters(),
|
|
||||||
value_parameters: self.value_parameters(),
|
|
||||||
return_type: self.return_type().map(Box::new),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(
|
|
||||||
&self,
|
|
||||||
_type_arguments: Option<Vec<Type>>,
|
|
||||||
value_arguments: Option<Vec<Value>>,
|
|
||||||
) -> Result<Option<Value>, BuiltInFunctionError> {
|
|
||||||
match (self.value_parameters(), &value_arguments) {
|
|
||||||
(Some(value_parameters), Some(value_arguments)) => {
|
|
||||||
if value_parameters.len() != value_arguments.len() {
|
|
||||||
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(_), None) | (None, Some(_)) => {
|
|
||||||
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::ToString => {
|
|
||||||
Ok(Some(Value::string(value_arguments.unwrap()[0].to_string())))
|
|
||||||
}
|
|
||||||
BuiltInFunction::IsEven => {
|
|
||||||
let is_even = value_arguments.unwrap()[0].is_even()?;
|
|
||||||
|
|
||||||
Ok(Some(is_even))
|
|
||||||
}
|
|
||||||
BuiltInFunction::IsOdd => {
|
|
||||||
let is_odd = value_arguments.unwrap()[0].is_odd()?;
|
|
||||||
|
|
||||||
Ok(Some(is_odd))
|
|
||||||
}
|
|
||||||
BuiltInFunction::ReadLine => {
|
|
||||||
let mut input = String::new();
|
|
||||||
|
|
||||||
stdin().read_line(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Some(Value::string(input.trim_end_matches('\n'))))
|
|
||||||
}
|
|
||||||
BuiltInFunction::WriteLine => {
|
|
||||||
let first_argument = &value_arguments.unwrap()[0];
|
|
||||||
|
|
||||||
match first_argument {
|
|
||||||
Value::Raw(ValueData::String(string)) => {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
|
|
||||||
stdout.write_all(string.as_bytes())?;
|
|
||||||
stdout.write_all(b"\n")?;
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Reference(reference) => match reference.as_ref() {
|
|
||||||
ValueData::String(string) => {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
|
|
||||||
stdout.write_all(string.as_bytes())?;
|
|
||||||
stdout.write_all(b"\n")?;
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
_ => Err(BuiltInFunctionError::ExpectedString),
|
|
||||||
},
|
|
||||||
Value::Mutable(locked) => {
|
|
||||||
let value_data = &*locked.read().unwrap();
|
|
||||||
let string = match value_data {
|
|
||||||
ValueData::String(string) => string,
|
|
||||||
_ => return Err(BuiltInFunctionError::ExpectedString),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut stdout = stdout();
|
|
||||||
|
|
||||||
stdout.write_all(string.as_bytes())?;
|
|
||||||
stdout.write_all(b"\n")?;
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Err(BuiltInFunctionError::ExpectedString),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for BuiltInFunction {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum BuiltInFunctionError {
|
|
||||||
Io(io::ErrorKind),
|
|
||||||
ValueError(ValueError),
|
|
||||||
|
|
||||||
ExpectedString,
|
|
||||||
ExpectedList,
|
|
||||||
ExpectedInteger,
|
|
||||||
|
|
||||||
WrongNumberOfValueArguments,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ValueError> for BuiltInFunctionError {
|
|
||||||
fn from(v: ValueError) -> Self {
|
|
||||||
Self::ValueError(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for BuiltInFunctionError {
|
|
||||||
fn from(error: io::Error) -> Self {
|
|
||||||
Self::Io(error.kind())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for BuiltInFunctionError {}
|
|
||||||
|
|
||||||
impl Display for BuiltInFunctionError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
BuiltInFunctionError::Io(error_kind) => write!(f, "I/O error: {}", error_kind),
|
|
||||||
BuiltInFunctionError::ValueError(value_error) => {
|
|
||||||
write!(f, "Value error: {}", value_error)
|
|
||||||
}
|
|
||||||
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
|
|
||||||
BuiltInFunctionError::ExpectedString => write!(f, "Expected a string"),
|
|
||||||
BuiltInFunctionError::ExpectedList => write!(f, "Expected a list"),
|
|
||||||
BuiltInFunctionError::WrongNumberOfValueArguments => {
|
|
||||||
write!(f, "Wrong number of value arguments")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
186
dust-lang/src/bytecode.rs
Normal file
186
dust-lang/src/bytecode.rs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Span, Value, ValueError};
|
||||||
|
|
||||||
|
const STACK_SIZE: usize = 256;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct Vm {
|
||||||
|
chunk: Chunk,
|
||||||
|
ip: usize,
|
||||||
|
stack: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vm {
|
||||||
|
pub fn new(chunk: Chunk) -> Self {
|
||||||
|
Self {
|
||||||
|
chunk,
|
||||||
|
ip: 0,
|
||||||
|
stack: Vec::with_capacity(STACK_SIZE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interpret(&mut self) -> Result<Option<Value>, VmError> {
|
||||||
|
loop {
|
||||||
|
let instruction = self.read_instruction();
|
||||||
|
|
||||||
|
match instruction {
|
||||||
|
Instruction::Constant(index) => {
|
||||||
|
let value = self.read_constant(*index);
|
||||||
|
|
||||||
|
self.stack.push(value.clone());
|
||||||
|
}
|
||||||
|
Instruction::Negate => {
|
||||||
|
let negated = self.pop()?.negate()?;
|
||||||
|
|
||||||
|
self.stack.push(negated);
|
||||||
|
}
|
||||||
|
Instruction::Return => {
|
||||||
|
let value = self.pop()?;
|
||||||
|
|
||||||
|
return Ok(Some(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, value: Value) -> Result<(), VmError> {
|
||||||
|
if self.stack.len() == STACK_SIZE {
|
||||||
|
Err(VmError::StackOverflow)
|
||||||
|
} else {
|
||||||
|
self.stack.push(value);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Result<Value, VmError> {
|
||||||
|
if let Some(value) = self.stack.pop() {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(VmError::StackUnderflow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_instruction(&self) -> &Instruction {
|
||||||
|
let (instruction, _) = &self.chunk.code[self.ip];
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_constant(&self, index: usize) -> Value {
|
||||||
|
self.chunk.constants[index].clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum VmError {
|
||||||
|
StackUnderflow,
|
||||||
|
StackOverflow,
|
||||||
|
Value(ValueError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ValueError> for VmError {
|
||||||
|
fn from(error: ValueError) -> Self {
|
||||||
|
Self::Value(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Instruction {
|
||||||
|
Constant(usize),
|
||||||
|
Negate,
|
||||||
|
Return,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
||||||
|
match self {
|
||||||
|
Instruction::Constant(index) => {
|
||||||
|
let value = &chunk.constants[*index];
|
||||||
|
|
||||||
|
format!("{:04} CONSTANT {} {}", offset, index, value)
|
||||||
|
}
|
||||||
|
Instruction::Negate => format!("{:04} NEGATE", offset),
|
||||||
|
Instruction::Return => format!("{:04} RETURN", offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Chunk {
|
||||||
|
code: Vec<(Instruction, Span)>,
|
||||||
|
constants: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
code: Vec::new(),
|
||||||
|
constants: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.code.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.code.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
self.code.capacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, instruction: Instruction, position: Span) {
|
||||||
|
self.code.push((instruction, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_constant(&mut self, value: Value) -> usize {
|
||||||
|
self.constants.push(value);
|
||||||
|
|
||||||
|
self.constants.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.code.clear();
|
||||||
|
self.constants.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disassemble(&self, name: &str) {
|
||||||
|
println!("== {} ==", name);
|
||||||
|
|
||||||
|
for (offset, (instruction, position)) in self.code.iter().enumerate() {
|
||||||
|
println!("{} {}", position, instruction.disassemble(self, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chunk {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negation() {
|
||||||
|
let mut chunk = Chunk::new();
|
||||||
|
let constant = chunk.push_constant(Value::integer(42));
|
||||||
|
|
||||||
|
chunk.write(Instruction::Constant(constant), Span(0, 1));
|
||||||
|
chunk.write(Instruction::Negate, Span(4, 5));
|
||||||
|
chunk.write(Instruction::Return, Span(2, 3));
|
||||||
|
|
||||||
|
let mut vm = Vm::new(chunk);
|
||||||
|
let result = vm.interpret();
|
||||||
|
|
||||||
|
assert_eq!(result, Ok(Some(Value::integer(-42))));
|
||||||
|
}
|
||||||
|
}
|
@ -1,651 +0,0 @@
|
|||||||
//! Garbage-collecting context for variables.
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
|
||||||
Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Constructor, Identifier, StructType, Type, Value};
|
|
||||||
|
|
||||||
pub type Associations = HashMap<Identifier, (ContextData, usize)>;
|
|
||||||
|
|
||||||
static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
fn next_id() -> usize {
|
|
||||||
ID_COUNTER.fetch_add(1, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Garbage-collecting context for variables.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Context {
|
|
||||||
inner: Arc<ContextInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::with_data(HashMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_data(data: Associations) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Arc::new(ContextInner {
|
|
||||||
associations: RwLock::new(data),
|
|
||||||
parent: None,
|
|
||||||
is_immutable: false,
|
|
||||||
id: next_id(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_data_immutable(data: Associations) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Arc::new(ContextInner {
|
|
||||||
associations: RwLock::new(data),
|
|
||||||
parent: None,
|
|
||||||
is_immutable: true,
|
|
||||||
id: next_id(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a deep copy of another context.
|
|
||||||
pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
|
|
||||||
let mut associations = HashMap::new();
|
|
||||||
|
|
||||||
for (identifier, (context_data, position)) in other.inner.associations.read()?.iter() {
|
|
||||||
associations.insert(identifier.clone(), (context_data.clone(), *position));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self::with_data(associations))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_child(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Arc::new(ContextInner {
|
|
||||||
associations: RwLock::new(HashMap::new()),
|
|
||||||
parent: Some(Arc::downgrade(&self.inner)),
|
|
||||||
is_immutable: false,
|
|
||||||
id: next_id(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> usize {
|
|
||||||
self.inner.id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of associated identifiers in the context.
|
|
||||||
pub fn association_count(&self) -> Result<usize, ContextError> {
|
|
||||||
self.inner.association_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a boolean indicating whether the identifier is in the context.
|
|
||||||
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
|
||||||
self.inner.contains(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the full ContextData and Span if the context contains the given identifier.
|
|
||||||
pub fn get(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<(ContextData, usize)>, ContextError> {
|
|
||||||
self.inner.get(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the type associated with the given identifier.
|
|
||||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
|
||||||
self.inner.get_type(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the ContextData associated with the identifier.
|
|
||||||
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
|
|
||||||
self.inner.get_data(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the value associated with the identifier.
|
|
||||||
pub fn get_variable_value(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<Value>, ContextError> {
|
|
||||||
self.inner.get_variable_value(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the constructor associated with the identifier.
|
|
||||||
pub fn get_constructor(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<Constructor>, ContextError> {
|
|
||||||
self.inner.get_constructor(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the constructor type associated with the identifier.
|
|
||||||
pub fn get_constructor_type(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<StructType>, ContextError> {
|
|
||||||
self.inner.get_constructor_type(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a variable type, with a position given for garbage collection.
|
|
||||||
pub fn set_variable_type(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
r#type: Type,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
self.inner.set_variable_type(identifier, r#type)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a variable value.
|
|
||||||
pub fn set_variable_value(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
value: Value,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
self.inner.set_variable_value(identifier, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a constructor.
|
|
||||||
pub fn set_constructor(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
constructor: Constructor,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
self.inner.set_constructor(identifier, constructor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a constructor type, with a position given for garbage
|
|
||||||
/// collection.
|
|
||||||
pub fn set_constructor_type(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
struct_type: StructType,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
self.inner.set_constructor_type(identifier, struct_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects garbage up to the given position, removing all variables with lesser positions.
|
|
||||||
pub fn collect_garbage(&self, position: usize) -> Result<(), ContextError> {
|
|
||||||
self.inner.collect_garbage(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
|
||||||
/// program. Returns a boolean indicating whether the identifier was found. If the identifier is
|
|
||||||
/// not found in the current context, the parent context is searched but parent context's
|
|
||||||
/// position is not updated.
|
|
||||||
pub fn set_position(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
position: usize,
|
|
||||||
) -> Result<bool, ContextError> {
|
|
||||||
self.inner.set_position(identifier, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recovers the context from a poisoned state by recovering data from an error.
|
|
||||||
///
|
|
||||||
/// This method is not used.
|
|
||||||
pub fn _recover_from_poison(&mut self, recovered: &RwLockReadGuard<Associations>) {
|
|
||||||
log::debug!("Context is recovering from poison error");
|
|
||||||
|
|
||||||
let mut new_associations = HashMap::new();
|
|
||||||
|
|
||||||
for (identifier, (context_data, position)) in recovered.iter() {
|
|
||||||
new_associations.insert(identifier.clone(), (context_data.clone(), *position));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.inner = Arc::new(ContextInner {
|
|
||||||
associations: RwLock::new(new_associations),
|
|
||||||
parent: None,
|
|
||||||
is_immutable: false,
|
|
||||||
id: next_id(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Context {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ContextInner {
|
|
||||||
id: usize,
|
|
||||||
associations: RwLock<Associations>,
|
|
||||||
parent: Option<Weak<ContextInner>>,
|
|
||||||
is_immutable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextInner {
|
|
||||||
fn parent(&self) -> Option<Arc<ContextInner>> {
|
|
||||||
self.parent.as_ref().and_then(|parent| parent.upgrade())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of associated identifiers in the context.
|
|
||||||
pub fn association_count(&self) -> Result<usize, ContextError> {
|
|
||||||
Ok(self.associations.read()?.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a boolean indicating whether the identifier is in the context.
|
|
||||||
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
|
||||||
if self.associations.read()?.contains_key(identifier) {
|
|
||||||
Ok(true)
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
parent.contains(identifier)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the full ContextData and Span if the context contains the given identifier.
|
|
||||||
pub fn get(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<(ContextData, usize)>, ContextError> {
|
|
||||||
if let Some((variable_data, position)) = self.associations.read()?.get(identifier) {
|
|
||||||
return Ok(Some((variable_data.clone(), *position)));
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the type associated with the given identifier.
|
|
||||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
|
||||||
match self.associations.read()?.get(identifier) {
|
|
||||||
Some((ContextData::VariableType(r#type), _)) => return Ok(Some(r#type.clone())),
|
|
||||||
Some((ContextData::VariableValue(value), _)) => return Ok(Some(value.r#type())),
|
|
||||||
Some((ContextData::ConstructorType(struct_type), _)) => {
|
|
||||||
return Ok(Some(Type::Struct(struct_type.clone())))
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get_type(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the ContextData associated with the identifier.
|
|
||||||
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
|
|
||||||
if let Some((variable_data, _)) = self.associations.read()?.get(identifier) {
|
|
||||||
return Ok(Some(variable_data.clone()));
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get_data(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the value associated with the identifier.
|
|
||||||
pub fn get_variable_value(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<Value>, ContextError> {
|
|
||||||
if let Some((ContextData::VariableValue(value), _)) =
|
|
||||||
self.associations.read()?.get(identifier)
|
|
||||||
{
|
|
||||||
return Ok(Some(value.clone()));
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get_variable_value(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the constructor associated with the identifier.
|
|
||||||
pub fn get_constructor(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<Constructor>, ContextError> {
|
|
||||||
if let Some((ContextData::Constructor(constructor), _)) =
|
|
||||||
self.associations.read()?.get(identifier)
|
|
||||||
{
|
|
||||||
return Ok(Some(constructor.clone()));
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get_constructor(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the constructor type associated with the identifier.
|
|
||||||
pub fn get_constructor_type(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
) -> Result<Option<StructType>, ContextError> {
|
|
||||||
let read_associations = self.associations.read()?;
|
|
||||||
|
|
||||||
if let Some((context_data, _)) = read_associations.get(identifier) {
|
|
||||||
return match context_data {
|
|
||||||
ContextData::Constructor(constructor) => Ok(Some(constructor.struct_type.clone())),
|
|
||||||
ContextData::ConstructorType(struct_type) => Ok(Some(struct_type.clone())),
|
|
||||||
_ => Ok(None),
|
|
||||||
};
|
|
||||||
} else if let Some(parent) = &self.parent {
|
|
||||||
if let Some(parent) = parent.upgrade() {
|
|
||||||
return parent.get_constructor_type(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a variable type.
|
|
||||||
pub fn set_variable_type(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
r#type: Type,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("Setting {identifier} to type {type} in context {}", self.id);
|
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
|
||||||
let last_position = associations
|
|
||||||
.get(&identifier)
|
|
||||||
.map(|(_, last_position)| *last_position)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
associations.insert(
|
|
||||||
identifier,
|
|
||||||
(ContextData::VariableType(r#type), last_position),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a variable value.
|
|
||||||
pub fn set_variable_value(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
value: Value,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Setting {identifier} to value {value} in context {}",
|
|
||||||
self.id
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
|
||||||
let last_position = associations
|
|
||||||
.get(&identifier)
|
|
||||||
.map(|(_, last_position)| *last_position)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
associations.insert(
|
|
||||||
identifier,
|
|
||||||
(ContextData::VariableValue(value), last_position),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a constructor.
|
|
||||||
pub fn set_constructor(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
constructor: Constructor,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Setting {identifier} to constructor {constructor:?} in context {}",
|
|
||||||
self.id
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
|
||||||
let last_position = associations
|
|
||||||
.get(&identifier)
|
|
||||||
.map(|(_, last_position)| *last_position)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
associations.insert(
|
|
||||||
identifier,
|
|
||||||
(ContextData::Constructor(constructor), last_position),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associates an identifier with a constructor type, with a position given for garbage
|
|
||||||
/// collection.
|
|
||||||
pub fn set_constructor_type(
|
|
||||||
&self,
|
|
||||||
identifier: Identifier,
|
|
||||||
struct_type: StructType,
|
|
||||||
) -> Result<(), ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Setting {identifier} to constructor of type {struct_type} in context {}",
|
|
||||||
self.id
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut variables = self.associations.write()?;
|
|
||||||
let last_position = variables
|
|
||||||
.get(&identifier)
|
|
||||||
.map(|(_, last_position)| *last_position)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
variables.insert(
|
|
||||||
identifier,
|
|
||||||
(ContextData::ConstructorType(struct_type), last_position),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects garbage up to the given position, removing all variables with lesser positions.
|
|
||||||
pub fn collect_garbage(&self, position: usize) -> Result<(), ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("Collecting garbage up to {position} in context {}", self.id);
|
|
||||||
|
|
||||||
let mut variables = self.associations.write()?;
|
|
||||||
|
|
||||||
variables.retain(|identifier, (_, last_used)| {
|
|
||||||
let should_drop = position >= *last_used;
|
|
||||||
|
|
||||||
if should_drop {
|
|
||||||
log::trace!("Removing {identifier} from context {}", self.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
!should_drop
|
|
||||||
});
|
|
||||||
variables.shrink_to_fit();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
|
||||||
/// program. Returns a boolean indicating whether the identifier was found. If the identifier is
|
|
||||||
/// not found in the current context, the parent context is searched but parent context's
|
|
||||||
/// position is not updated.
|
|
||||||
pub fn set_position(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
position: usize,
|
|
||||||
) -> Result<bool, ContextError> {
|
|
||||||
let found = self.update_position_if_found(identifier, position)?;
|
|
||||||
|
|
||||||
if found {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let found_in_ancestor = if let Some(parent) = &self.parent() {
|
|
||||||
if parent.is_immutable {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
parent.update_position_if_found(identifier, position)?
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if found_in_ancestor {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Reserving {identifier} at position {position:?} in context {}",
|
|
||||||
self.id
|
|
||||||
);
|
|
||||||
|
|
||||||
associations.insert(identifier.clone(), (ContextData::Reserved, position));
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_position_if_found(
|
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
position: usize,
|
|
||||||
) -> Result<bool, ContextError> {
|
|
||||||
if self.is_immutable {
|
|
||||||
return Err(ContextError::CannotMutateImmutableContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
|
||||||
|
|
||||||
if let Some((_, last_position)) = associations.get_mut(identifier) {
|
|
||||||
log::trace!(
|
|
||||||
"Updating {identifier}'s last position to {position:?} in context {}",
|
|
||||||
self.id
|
|
||||||
);
|
|
||||||
|
|
||||||
*last_position = position;
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ContextData {
|
|
||||||
Constructor(Constructor),
|
|
||||||
ConstructorType(StructType),
|
|
||||||
VariableValue(Value),
|
|
||||||
VariableType(Type),
|
|
||||||
Reserved,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ContextError {
|
|
||||||
CannotMutateImmutableContext,
|
|
||||||
PoisonErrorRecovered(Arc<Associations>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PoisonError<RwLockWriteGuard<'_, Associations>>> for ContextError {
|
|
||||||
fn from(error: PoisonError<RwLockWriteGuard<'_, Associations>>) -> Self {
|
|
||||||
let associations = error.into_inner().clone();
|
|
||||||
|
|
||||||
Self::PoisonErrorRecovered(Arc::new(associations))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PoisonError<RwLockReadGuard<'_, Associations>>> for ContextError {
|
|
||||||
fn from(error: PoisonError<RwLockReadGuard<'_, Associations>>) -> Self {
|
|
||||||
let associations = error.into_inner().clone();
|
|
||||||
|
|
||||||
Self::PoisonErrorRecovered(Arc::new(associations))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for ContextError {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Self::CannotMutateImmutableContext, Self::CannotMutateImmutableContext) => true,
|
|
||||||
(Self::PoisonErrorRecovered(left), Self::PoisonErrorRecovered(right)) => {
|
|
||||||
Arc::ptr_eq(left, right)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ContextError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::CannotMutateImmutableContext => write!(f, "Cannot mutate immutable context"),
|
|
||||||
Self::PoisonErrorRecovered(associations) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Context poisoned with {} associations recovered",
|
|
||||||
associations.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{parse, Vm};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn context_removes_variables() {
|
|
||||||
let source = "
|
|
||||||
let x = 5;
|
|
||||||
let y = 10;
|
|
||||||
let z = x + y;
|
|
||||||
z
|
|
||||||
";
|
|
||||||
let ast = parse(source).unwrap();
|
|
||||||
let context = ast.context.clone();
|
|
||||||
|
|
||||||
assert_eq!(Vm.run(ast), Ok(Some(Value::integer(15))));
|
|
||||||
assert_eq!(context.association_count().unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn garbage_collector_does_not_break_loops() {
|
|
||||||
let source = "
|
|
||||||
let mut z = 0;
|
|
||||||
while z < 10 {
|
|
||||||
z += 1;
|
|
||||||
}
|
|
||||||
";
|
|
||||||
let ast = parse(source).unwrap();
|
|
||||||
let context = ast.context.clone();
|
|
||||||
|
|
||||||
assert_eq!(Vm.run(ast), Ok(None));
|
|
||||||
assert_eq!(context.association_count().unwrap(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
use std::{collections::HashMap, sync::OnceLock};
|
|
||||||
|
|
||||||
use crate::{BuiltInFunction, Context, ContextData, Function, Identifier, Value};
|
|
||||||
|
|
||||||
static CORE_LIBRARY: OnceLock<Context> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn core_library<'a>() -> &'a Context {
|
|
||||||
CORE_LIBRARY.get_or_init(|| {
|
|
||||||
Context::with_data_immutable(HashMap::from([
|
|
||||||
(
|
|
||||||
Identifier::new("to_string"),
|
|
||||||
(
|
|
||||||
ContextData::VariableValue(Value::function(Function::BuiltIn(
|
|
||||||
BuiltInFunction::ToString,
|
|
||||||
))),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Identifier::new("read_line"),
|
|
||||||
(
|
|
||||||
ContextData::VariableValue(Value::function(Function::BuiltIn(
|
|
||||||
BuiltInFunction::ReadLine,
|
|
||||||
))),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Identifier::new("write_line"),
|
|
||||||
(
|
|
||||||
ContextData::VariableValue(Value::function(Function::BuiltIn(
|
|
||||||
BuiltInFunction::WriteLine,
|
|
||||||
))),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]))
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
//! Top-level error handling for the Dust language.
|
|
||||||
use annotate_snippets::{Level, Renderer, Snippet};
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use crate::{AnalysisError, ContextError, LexError, ParseError, RuntimeError};
|
|
||||||
|
|
||||||
/// An error that occurred during the execution of the Dust language and its
|
|
||||||
/// corresponding source code.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum DustError<'src> {
|
|
||||||
ContextError(ContextError),
|
|
||||||
Runtime {
|
|
||||||
runtime_error: RuntimeError,
|
|
||||||
source: &'src str,
|
|
||||||
},
|
|
||||||
Analysis {
|
|
||||||
analysis_errors: Vec<AnalysisError>,
|
|
||||||
source: &'src str,
|
|
||||||
},
|
|
||||||
Parse {
|
|
||||||
parse_error: ParseError,
|
|
||||||
source: &'src str,
|
|
||||||
},
|
|
||||||
Lex {
|
|
||||||
lex_error: LexError,
|
|
||||||
source: &'src str,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> From<ContextError> for DustError<'src> {
|
|
||||||
fn from(error: ContextError) -> Self {
|
|
||||||
Self::ContextError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> DustError<'src> {
|
|
||||||
pub fn runtime(runtime_error: RuntimeError, source: &'src str) -> Self {
|
|
||||||
DustError::Runtime {
|
|
||||||
runtime_error,
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn analysis<T: Into<Vec<AnalysisError>>>(analysis_errors: T, source: &'src str) -> Self {
|
|
||||||
DustError::Analysis {
|
|
||||||
analysis_errors: analysis_errors.into(),
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(parse_error: ParseError, source: &'src str) -> Self {
|
|
||||||
DustError::Parse {
|
|
||||||
parse_error,
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lex(lex_error: LexError, source: &'src str) -> Self {
|
|
||||||
DustError::Lex { lex_error, source }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
DustError::ContextError(_) => "Context error",
|
|
||||||
DustError::Runtime { .. } => "Runtime error",
|
|
||||||
DustError::Analysis { .. } => "Analysis error",
|
|
||||||
DustError::Parse { .. } => "Parse error",
|
|
||||||
DustError::Lex { .. } => "Lex error",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn source(&self) -> &'src str {
|
|
||||||
match self {
|
|
||||||
DustError::ContextError(_) => "",
|
|
||||||
DustError::Runtime { source, .. } => source,
|
|
||||||
DustError::Analysis { source, .. } => source,
|
|
||||||
DustError::Parse { source, .. } => source,
|
|
||||||
DustError::Lex { source, .. } => source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report(&self) -> String {
|
|
||||||
let mut report = String::new();
|
|
||||||
let renderer = Renderer::styled();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
DustError::ContextError(_) => {
|
|
||||||
let message = Level::Error.title("Context error");
|
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
|
||||||
}
|
|
||||||
DustError::Runtime {
|
|
||||||
runtime_error,
|
|
||||||
source,
|
|
||||||
} => {
|
|
||||||
let error = runtime_error.root_error();
|
|
||||||
let position = error.position();
|
|
||||||
let label = error.to_string();
|
|
||||||
let message = Level::Error
|
|
||||||
.title("Runtime error")
|
|
||||||
.snippet(
|
|
||||||
Snippet::source(source)
|
|
||||||
.fold(true)
|
|
||||||
.annotation(Level::Error.span(position.0..position.1).label(&label)),
|
|
||||||
)
|
|
||||||
.footer(
|
|
||||||
Level::Error
|
|
||||||
.title("This error occured during the execution of the Dust program."),
|
|
||||||
);
|
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
|
||||||
report.push_str("\n\n");
|
|
||||||
}
|
|
||||||
DustError::Analysis {
|
|
||||||
analysis_errors,
|
|
||||||
source,
|
|
||||||
} => {
|
|
||||||
for error in analysis_errors {
|
|
||||||
let position = error.position();
|
|
||||||
let label = error.to_string();
|
|
||||||
let message =
|
|
||||||
Level::Warning
|
|
||||||
.title("Analysis error")
|
|
||||||
.snippet(Snippet::source(source).fold(true).annotation(
|
|
||||||
Level::Warning.span(position.0..position.1).label(&label),
|
|
||||||
))
|
|
||||||
.footer(
|
|
||||||
Level::Warning
|
|
||||||
.title("This error was found without running the program."),
|
|
||||||
);
|
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
|
||||||
report.push_str("\n\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DustError::Parse {
|
|
||||||
parse_error,
|
|
||||||
source,
|
|
||||||
} => {
|
|
||||||
if let ParseError::Lex(lex_error) = parse_error {
|
|
||||||
let lex_error_report = DustError::lex(lex_error.clone(), source).report();
|
|
||||||
|
|
||||||
report.push_str(&lex_error_report);
|
|
||||||
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
let position = parse_error.position();
|
|
||||||
let label = parse_error.to_string();
|
|
||||||
let message = Level::Error.title("Parse error").snippet(
|
|
||||||
Snippet::source(source)
|
|
||||||
.fold(true)
|
|
||||||
.annotation(Level::Error.span(position.0..position.1).label(&label)),
|
|
||||||
);
|
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
|
||||||
}
|
|
||||||
DustError::Lex { lex_error, source } => {
|
|
||||||
let position = lex_error.position();
|
|
||||||
let label = lex_error.to_string();
|
|
||||||
let message = Level::Error.title("Lex error").snippet(
|
|
||||||
Snippet::source(source)
|
|
||||||
.fold(true)
|
|
||||||
.annotation(Level::Error.span(position.0..position.1).label(&label)),
|
|
||||||
);
|
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
report
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DustError<'_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
DustError::ContextError(context_error) => write!(f, "{context_error}"),
|
|
||||||
DustError::Runtime { runtime_error, .. } => write!(f, "{runtime_error}"),
|
|
||||||
DustError::Analysis {
|
|
||||||
analysis_errors, ..
|
|
||||||
} => {
|
|
||||||
for error in analysis_errors {
|
|
||||||
write!(f, "{error} ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
DustError::Parse { parse_error, .. } => write!(f, "{parse_error}"),
|
|
||||||
DustError::Lex { lex_error, .. } => write!(f, "{lex_error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
use crate::{Constructor, RuntimeError, Span, StructType, Type, Value};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum Evaluation {
|
|
||||||
Break(Option<Value>),
|
|
||||||
Constructor(Constructor),
|
|
||||||
Return(Option<Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Evaluation {
|
|
||||||
pub fn value(self) -> Option<Value> {
|
|
||||||
match self {
|
|
||||||
Evaluation::Return(value_option) => value_option,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect_value(self, position: Span) -> Result<Value, RuntimeError> {
|
|
||||||
if let Evaluation::Return(Some(value)) = self {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ExpectedValue { position })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum TypeEvaluation {
|
|
||||||
Break(Option<Type>),
|
|
||||||
Constructor(StructType),
|
|
||||||
Return(Option<Type>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeEvaluation {
|
|
||||||
pub fn r#type(self) -> Option<Type> {
|
|
||||||
match self {
|
|
||||||
TypeEvaluation::Return(type_option) => type_option,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -15,34 +15,27 @@
|
|||||||
//!
|
//!
|
||||||
//! assert_eq!(the_answer, Some(Value::integer(42)));
|
//! assert_eq!(the_answer, Some(Value::integer(42)));
|
||||||
//! ```
|
//! ```
|
||||||
pub mod analyzer;
|
pub mod bytecode;
|
||||||
pub mod ast;
|
|
||||||
pub mod built_in_function;
|
|
||||||
pub mod constructor;
|
pub mod constructor;
|
||||||
pub mod context;
|
|
||||||
pub mod core_library;
|
|
||||||
pub mod dust_error;
|
|
||||||
pub mod evaluation;
|
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod token;
|
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
|
||||||
|
|
||||||
pub use analyzer::{analyze, AnalysisError, Analyzer};
|
pub use bytecode::*;
|
||||||
pub use ast::{AbstractSyntaxTree, AstError, Expression, Node, Span, Statement};
|
pub use constructor::*;
|
||||||
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
|
pub use identifier::*;
|
||||||
pub use constructor::{ConstructError, Constructor};
|
|
||||||
pub use context::{Context, ContextData, ContextError};
|
|
||||||
pub use core_library::core_library;
|
|
||||||
pub use dust_error::DustError;
|
|
||||||
pub use evaluation::{Evaluation, TypeEvaluation};
|
|
||||||
pub use identifier::Identifier;
|
|
||||||
pub use lexer::{lex, LexError, Lexer};
|
|
||||||
pub use parser::{parse, ParseError, Parser};
|
|
||||||
pub use r#type::*;
|
pub use r#type::*;
|
||||||
pub use token::{Token, TokenKind, TokenOwned};
|
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
pub use vm::{run, RuntimeError, Vm};
|
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub struct Span(usize, usize);
|
||||||
|
|
||||||
|
impl Display for Span {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,651 +0,0 @@
|
|||||||
//! Token and TokenOwned types.
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Source code token.
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum Token<'src> {
|
|
||||||
// End of file
|
|
||||||
Eof,
|
|
||||||
|
|
||||||
// Hard-coded values
|
|
||||||
Boolean(&'src str),
|
|
||||||
Character(char),
|
|
||||||
Float(&'src str),
|
|
||||||
Identifier(&'src str),
|
|
||||||
Integer(&'src str),
|
|
||||||
String(&'src str),
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
Async,
|
|
||||||
Bool,
|
|
||||||
Break,
|
|
||||||
Else,
|
|
||||||
FloatKeyword,
|
|
||||||
If,
|
|
||||||
Int,
|
|
||||||
Let,
|
|
||||||
Loop,
|
|
||||||
Map,
|
|
||||||
Mut,
|
|
||||||
Str,
|
|
||||||
Struct,
|
|
||||||
While,
|
|
||||||
|
|
||||||
// Symbols
|
|
||||||
BangEqual,
|
|
||||||
Bang,
|
|
||||||
Colon,
|
|
||||||
Comma,
|
|
||||||
Dot,
|
|
||||||
DoubleAmpersand,
|
|
||||||
DoubleDot,
|
|
||||||
DoubleEqual,
|
|
||||||
DoublePipe,
|
|
||||||
Equal,
|
|
||||||
Greater,
|
|
||||||
GreaterEqual,
|
|
||||||
LeftCurlyBrace,
|
|
||||||
LeftParenthesis,
|
|
||||||
LeftSquareBrace,
|
|
||||||
Less,
|
|
||||||
LessEqual,
|
|
||||||
Minus,
|
|
||||||
MinusEqual,
|
|
||||||
Percent,
|
|
||||||
Plus,
|
|
||||||
PlusEqual,
|
|
||||||
RightCurlyBrace,
|
|
||||||
RightParenthesis,
|
|
||||||
RightSquareBrace,
|
|
||||||
Semicolon,
|
|
||||||
Slash,
|
|
||||||
Star,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> Token<'src> {
|
|
||||||
#[allow(clippy::len_without_is_empty)]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Token::Eof => 0,
|
|
||||||
Token::Boolean(text) => text.len(),
|
|
||||||
Token::Character(_) => 3,
|
|
||||||
Token::Float(text) => text.len(),
|
|
||||||
Token::Identifier(text) => text.len(),
|
|
||||||
Token::Integer(text) => text.len(),
|
|
||||||
Token::String(text) => text.len() + 2,
|
|
||||||
Token::Async => 5,
|
|
||||||
Token::Bool => 4,
|
|
||||||
Token::Break => 5,
|
|
||||||
Token::Else => 4,
|
|
||||||
Token::FloatKeyword => 5,
|
|
||||||
Token::If => 2,
|
|
||||||
Token::Int => 3,
|
|
||||||
Token::Let => 3,
|
|
||||||
Token::Loop => 4,
|
|
||||||
Token::Map => 3,
|
|
||||||
Token::Mut => 3,
|
|
||||||
Token::Str => 3,
|
|
||||||
Token::Struct => 6,
|
|
||||||
Token::While => 5,
|
|
||||||
Token::BangEqual => 2,
|
|
||||||
Token::Bang => 1,
|
|
||||||
Token::Colon => 1,
|
|
||||||
Token::Comma => 1,
|
|
||||||
Token::Dot => 1,
|
|
||||||
Token::DoubleAmpersand => 2,
|
|
||||||
Token::DoubleDot => 2,
|
|
||||||
Token::DoubleEqual => 2,
|
|
||||||
Token::DoublePipe => 2,
|
|
||||||
Token::Equal => 1,
|
|
||||||
Token::Greater => 1,
|
|
||||||
Token::GreaterEqual => 2,
|
|
||||||
Token::LeftCurlyBrace => 1,
|
|
||||||
Token::LeftParenthesis => 1,
|
|
||||||
Token::LeftSquareBrace => 1,
|
|
||||||
Token::Less => 1,
|
|
||||||
Token::LessEqual => 2,
|
|
||||||
Token::Minus => 1,
|
|
||||||
Token::MinusEqual => 2,
|
|
||||||
Token::Percent => 1,
|
|
||||||
Token::Plus => 1,
|
|
||||||
Token::PlusEqual => 2,
|
|
||||||
Token::RightCurlyBrace => 1,
|
|
||||||
Token::RightParenthesis => 1,
|
|
||||||
Token::RightSquareBrace => 1,
|
|
||||||
Token::Semicolon => 1,
|
|
||||||
Token::Slash => 1,
|
|
||||||
Token::Star => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_owned(&self) -> TokenOwned {
|
|
||||||
match self {
|
|
||||||
Token::Async => TokenOwned::Async,
|
|
||||||
Token::BangEqual => TokenOwned::BangEqual,
|
|
||||||
Token::Bang => TokenOwned::Bang,
|
|
||||||
Token::Bool => TokenOwned::Bool,
|
|
||||||
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
|
|
||||||
Token::Break => TokenOwned::Break,
|
|
||||||
Token::Character(character) => TokenOwned::Character(*character),
|
|
||||||
Token::Colon => TokenOwned::Colon,
|
|
||||||
Token::Comma => TokenOwned::Comma,
|
|
||||||
Token::Dot => TokenOwned::Dot,
|
|
||||||
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
|
||||||
Token::DoubleDot => TokenOwned::DoubleDot,
|
|
||||||
Token::DoubleEqual => TokenOwned::DoubleEqual,
|
|
||||||
Token::DoublePipe => TokenOwned::DoublePipe,
|
|
||||||
Token::Else => TokenOwned::Else,
|
|
||||||
Token::Eof => TokenOwned::Eof,
|
|
||||||
Token::Equal => TokenOwned::Equal,
|
|
||||||
Token::Float(float) => TokenOwned::Float(float.to_string()),
|
|
||||||
Token::FloatKeyword => TokenOwned::FloatKeyword,
|
|
||||||
Token::Greater => TokenOwned::Greater,
|
|
||||||
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
|
||||||
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
|
||||||
Token::If => TokenOwned::If,
|
|
||||||
Token::Int => TokenOwned::Int,
|
|
||||||
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
|
||||||
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
|
|
||||||
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
|
|
||||||
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
|
|
||||||
Token::Let => TokenOwned::Let,
|
|
||||||
Token::Less => TokenOwned::Less,
|
|
||||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
|
||||||
Token::Loop => TokenOwned::Loop,
|
|
||||||
Token::Map => TokenOwned::Map,
|
|
||||||
Token::Minus => TokenOwned::Minus,
|
|
||||||
Token::MinusEqual => TokenOwned::MinusEqual,
|
|
||||||
Token::Mut => TokenOwned::Mut,
|
|
||||||
Token::Percent => TokenOwned::Percent,
|
|
||||||
Token::Plus => TokenOwned::Plus,
|
|
||||||
Token::PlusEqual => TokenOwned::PlusEqual,
|
|
||||||
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
|
||||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
|
||||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
|
||||||
Token::Semicolon => TokenOwned::Semicolon,
|
|
||||||
Token::Star => TokenOwned::Star,
|
|
||||||
Token::Slash => TokenOwned::Slash,
|
|
||||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
|
||||||
Token::Str => TokenOwned::Str,
|
|
||||||
Token::Struct => TokenOwned::Struct,
|
|
||||||
Token::While => TokenOwned::While,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn kind(&self) -> TokenKind {
|
|
||||||
match self {
|
|
||||||
Token::Async => TokenKind::Async,
|
|
||||||
Token::BangEqual => TokenKind::BangEqual,
|
|
||||||
Token::Bang => TokenKind::Bang,
|
|
||||||
Token::Bool => TokenKind::Bool,
|
|
||||||
Token::Boolean(_) => TokenKind::Boolean,
|
|
||||||
Token::Break => TokenKind::Break,
|
|
||||||
Token::Character(_) => TokenKind::Character,
|
|
||||||
Token::Colon => TokenKind::Colon,
|
|
||||||
Token::Comma => TokenKind::Comma,
|
|
||||||
Token::Dot => TokenKind::Dot,
|
|
||||||
Token::DoubleAmpersand => TokenKind::DoubleAmpersand,
|
|
||||||
Token::DoubleDot => TokenKind::DoubleDot,
|
|
||||||
Token::DoubleEqual => TokenKind::DoubleEqual,
|
|
||||||
Token::DoublePipe => TokenKind::DoublePipe,
|
|
||||||
Token::Else => TokenKind::Else,
|
|
||||||
Token::Eof => TokenKind::Eof,
|
|
||||||
Token::Equal => TokenKind::Equal,
|
|
||||||
Token::Float(_) => TokenKind::Float,
|
|
||||||
Token::FloatKeyword => TokenKind::FloatKeyword,
|
|
||||||
Token::Greater => TokenKind::Greater,
|
|
||||||
Token::GreaterEqual => TokenKind::GreaterOrEqual,
|
|
||||||
Token::Identifier(_) => TokenKind::Identifier,
|
|
||||||
Token::If => TokenKind::If,
|
|
||||||
Token::Int => TokenKind::Int,
|
|
||||||
Token::Integer(_) => TokenKind::Integer,
|
|
||||||
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
|
|
||||||
Token::LeftParenthesis => TokenKind::LeftParenthesis,
|
|
||||||
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
|
|
||||||
Token::Let => TokenKind::Let,
|
|
||||||
Token::Less => TokenKind::Less,
|
|
||||||
Token::LessEqual => TokenKind::LessOrEqual,
|
|
||||||
Token::Loop => TokenKind::Loop,
|
|
||||||
Token::Map => TokenKind::Map,
|
|
||||||
Token::Minus => TokenKind::Minus,
|
|
||||||
Token::MinusEqual => TokenKind::MinusEqual,
|
|
||||||
Token::Mut => TokenKind::Mut,
|
|
||||||
Token::Percent => TokenKind::Percent,
|
|
||||||
Token::Plus => TokenKind::Plus,
|
|
||||||
Token::PlusEqual => TokenKind::PlusEqual,
|
|
||||||
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
|
|
||||||
Token::RightParenthesis => TokenKind::RightParenthesis,
|
|
||||||
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
|
||||||
Token::Semicolon => TokenKind::Semicolon,
|
|
||||||
Token::Star => TokenKind::Star,
|
|
||||||
Token::Slash => TokenKind::Slash,
|
|
||||||
Token::Str => TokenKind::Str,
|
|
||||||
Token::String(_) => TokenKind::String,
|
|
||||||
Token::Struct => TokenKind::Struct,
|
|
||||||
Token::While => TokenKind::While,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_eof(&self) -> bool {
|
|
||||||
matches!(self, Token::Eof)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn precedence(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Token::Dot => 9,
|
|
||||||
Token::LeftParenthesis | Token::LeftSquareBrace => 8,
|
|
||||||
Token::Star | Token::Slash | Token::Percent => 7,
|
|
||||||
Token::Minus | Token::Plus => 6,
|
|
||||||
Token::DoubleEqual
|
|
||||||
| Token::Less
|
|
||||||
| Token::LessEqual
|
|
||||||
| Token::Greater
|
|
||||||
| Token::GreaterEqual => 5,
|
|
||||||
Token::DoubleAmpersand => 4,
|
|
||||||
Token::DoublePipe => 3,
|
|
||||||
Token::DoubleDot => 2,
|
|
||||||
Token::Equal | Token::MinusEqual | Token::PlusEqual => 1,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left_associative(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Token::Dot
|
|
||||||
| Token::DoubleAmpersand
|
|
||||||
| Token::DoublePipe
|
|
||||||
| Token::Plus
|
|
||||||
| Token::Minus
|
|
||||||
| Token::Star
|
|
||||||
| Token::Slash
|
|
||||||
| Token::Percent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_right_associative(&self) -> bool {
|
|
||||||
matches!(self, Token::Equal | Token::MinusEqual | Token::PlusEqual)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_prefix(&self) -> bool {
|
|
||||||
matches!(self, Token::Bang | Token::Minus | Token::Star)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_postfix(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Token::Dot | Token::LeftCurlyBrace | Token::LeftParenthesis | Token::LeftSquareBrace
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> Display for Token<'src> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Token::Async => write!(f, "async"),
|
|
||||||
Token::BangEqual => write!(f, "!="),
|
|
||||||
Token::Bang => write!(f, "!"),
|
|
||||||
Token::Bool => write!(f, "bool"),
|
|
||||||
Token::Boolean(value) => write!(f, "{}", value),
|
|
||||||
Token::Break => write!(f, "break"),
|
|
||||||
Token::Character(value) => write!(f, "'{}'", value),
|
|
||||||
Token::Colon => write!(f, ":"),
|
|
||||||
Token::Comma => write!(f, ","),
|
|
||||||
Token::Dot => write!(f, "."),
|
|
||||||
Token::DoubleAmpersand => write!(f, "&&"),
|
|
||||||
Token::DoubleDot => write!(f, ".."),
|
|
||||||
Token::DoubleEqual => write!(f, "=="),
|
|
||||||
Token::DoublePipe => write!(f, "||"),
|
|
||||||
Token::Else => write!(f, "else"),
|
|
||||||
Token::Eof => write!(f, "EOF"),
|
|
||||||
Token::Equal => write!(f, "="),
|
|
||||||
Token::Float(value) => write!(f, "{}", value),
|
|
||||||
Token::FloatKeyword => write!(f, "float"),
|
|
||||||
Token::Greater => write!(f, ">"),
|
|
||||||
Token::GreaterEqual => write!(f, ">="),
|
|
||||||
Token::Identifier(value) => write!(f, "{}", value),
|
|
||||||
Token::If => write!(f, "if"),
|
|
||||||
Token::Int => write!(f, "int"),
|
|
||||||
Token::Integer(value) => write!(f, "{}", value),
|
|
||||||
Token::LeftCurlyBrace => write!(f, "{{"),
|
|
||||||
Token::LeftParenthesis => write!(f, "("),
|
|
||||||
Token::LeftSquareBrace => write!(f, "["),
|
|
||||||
Token::Let => write!(f, "let"),
|
|
||||||
Token::Less => write!(f, "<"),
|
|
||||||
Token::LessEqual => write!(f, "<="),
|
|
||||||
Token::Loop => write!(f, "loop"),
|
|
||||||
Token::Map => write!(f, "map"),
|
|
||||||
Token::Minus => write!(f, "-"),
|
|
||||||
Token::MinusEqual => write!(f, "-="),
|
|
||||||
Token::Mut => write!(f, "mut"),
|
|
||||||
Token::Percent => write!(f, "%"),
|
|
||||||
Token::Plus => write!(f, "+"),
|
|
||||||
Token::PlusEqual => write!(f, "+="),
|
|
||||||
Token::RightCurlyBrace => write!(f, "}}"),
|
|
||||||
Token::RightParenthesis => write!(f, ")"),
|
|
||||||
Token::RightSquareBrace => write!(f, "]"),
|
|
||||||
Token::Semicolon => write!(f, ";"),
|
|
||||||
Token::Slash => write!(f, "/"),
|
|
||||||
Token::Star => write!(f, "*"),
|
|
||||||
Token::Str => write!(f, "str"),
|
|
||||||
Token::String(value) => write!(f, "\"{}\"", value),
|
|
||||||
Token::Struct => write!(f, "struct"),
|
|
||||||
Token::While => write!(f, "while"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Owned version of `Token`, which owns all the strings.
|
|
||||||
///
|
|
||||||
/// This is used for errors.
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum TokenOwned {
|
|
||||||
Eof,
|
|
||||||
|
|
||||||
Identifier(String),
|
|
||||||
|
|
||||||
// Hard-coded values
|
|
||||||
Boolean(String),
|
|
||||||
Character(char),
|
|
||||||
Float(String),
|
|
||||||
Integer(String),
|
|
||||||
String(String),
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
Bool,
|
|
||||||
Break,
|
|
||||||
Else,
|
|
||||||
FloatKeyword,
|
|
||||||
If,
|
|
||||||
Int,
|
|
||||||
Let,
|
|
||||||
Loop,
|
|
||||||
Map,
|
|
||||||
Mut,
|
|
||||||
Str,
|
|
||||||
While,
|
|
||||||
|
|
||||||
// Symbols
|
|
||||||
Async,
|
|
||||||
Bang,
|
|
||||||
BangEqual,
|
|
||||||
Colon,
|
|
||||||
Comma,
|
|
||||||
Dot,
|
|
||||||
DoubleAmpersand,
|
|
||||||
DoubleDot,
|
|
||||||
DoubleEqual,
|
|
||||||
DoublePipe,
|
|
||||||
Equal,
|
|
||||||
Greater,
|
|
||||||
GreaterOrEqual,
|
|
||||||
LeftCurlyBrace,
|
|
||||||
LeftParenthesis,
|
|
||||||
LeftSquareBrace,
|
|
||||||
Less,
|
|
||||||
LessOrEqual,
|
|
||||||
Minus,
|
|
||||||
MinusEqual,
|
|
||||||
Percent,
|
|
||||||
Plus,
|
|
||||||
PlusEqual,
|
|
||||||
RightCurlyBrace,
|
|
||||||
RightParenthesis,
|
|
||||||
RightSquareBrace,
|
|
||||||
Semicolon,
|
|
||||||
Star,
|
|
||||||
Struct,
|
|
||||||
Slash,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TokenOwned {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
TokenOwned::Async => Token::Async.fmt(f),
|
|
||||||
TokenOwned::Bang => Token::Bang.fmt(f),
|
|
||||||
TokenOwned::BangEqual => Token::BangEqual.fmt(f),
|
|
||||||
TokenOwned::Bool => Token::Bool.fmt(f),
|
|
||||||
TokenOwned::Boolean(boolean) => Token::Boolean(boolean).fmt(f),
|
|
||||||
TokenOwned::Break => Token::Break.fmt(f),
|
|
||||||
TokenOwned::Character(character) => Token::Character(*character).fmt(f),
|
|
||||||
TokenOwned::Colon => Token::Colon.fmt(f),
|
|
||||||
TokenOwned::Comma => Token::Comma.fmt(f),
|
|
||||||
TokenOwned::Dot => Token::Dot.fmt(f),
|
|
||||||
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
|
||||||
TokenOwned::DoubleDot => Token::DoubleDot.fmt(f),
|
|
||||||
TokenOwned::DoubleEqual => Token::DoubleEqual.fmt(f),
|
|
||||||
TokenOwned::DoublePipe => Token::DoublePipe.fmt(f),
|
|
||||||
TokenOwned::Else => Token::Else.fmt(f),
|
|
||||||
TokenOwned::Eof => Token::Eof.fmt(f),
|
|
||||||
TokenOwned::Equal => Token::Equal.fmt(f),
|
|
||||||
TokenOwned::Float(float) => Token::Float(float).fmt(f),
|
|
||||||
TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f),
|
|
||||||
TokenOwned::Greater => Token::Greater.fmt(f),
|
|
||||||
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
|
||||||
TokenOwned::Identifier(text) => Token::Identifier(text).fmt(f),
|
|
||||||
TokenOwned::If => Token::If.fmt(f),
|
|
||||||
TokenOwned::Int => Token::Int.fmt(f),
|
|
||||||
TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
|
|
||||||
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
|
||||||
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
|
||||||
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
|
||||||
TokenOwned::Let => Token::Let.fmt(f),
|
|
||||||
TokenOwned::Less => Token::Less.fmt(f),
|
|
||||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
|
||||||
TokenOwned::Loop => Token::Loop.fmt(f),
|
|
||||||
TokenOwned::Map => Token::Map.fmt(f),
|
|
||||||
TokenOwned::Minus => Token::Minus.fmt(f),
|
|
||||||
TokenOwned::MinusEqual => Token::MinusEqual.fmt(f),
|
|
||||||
TokenOwned::Mut => Token::Mut.fmt(f),
|
|
||||||
TokenOwned::Percent => Token::Percent.fmt(f),
|
|
||||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
|
||||||
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
|
|
||||||
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
|
||||||
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
|
|
||||||
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
|
||||||
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
|
||||||
TokenOwned::Star => Token::Star.fmt(f),
|
|
||||||
TokenOwned::Slash => Token::Slash.fmt(f),
|
|
||||||
TokenOwned::Str => Token::Str.fmt(f),
|
|
||||||
TokenOwned::String(string) => Token::String(string).fmt(f),
|
|
||||||
TokenOwned::Struct => Token::Struct.fmt(f),
|
|
||||||
TokenOwned::While => Token::While.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Token representation that holds no data.
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum TokenKind {
|
|
||||||
Eof,
|
|
||||||
|
|
||||||
Identifier,
|
|
||||||
|
|
||||||
// Hard-coded values
|
|
||||||
Boolean,
|
|
||||||
Character,
|
|
||||||
Float,
|
|
||||||
Integer,
|
|
||||||
String,
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
Async,
|
|
||||||
Bool,
|
|
||||||
Break,
|
|
||||||
Else,
|
|
||||||
FloatKeyword,
|
|
||||||
If,
|
|
||||||
Int,
|
|
||||||
Let,
|
|
||||||
Loop,
|
|
||||||
Map,
|
|
||||||
Str,
|
|
||||||
While,
|
|
||||||
|
|
||||||
// Symbols
|
|
||||||
BangEqual,
|
|
||||||
Bang,
|
|
||||||
Colon,
|
|
||||||
Comma,
|
|
||||||
Dot,
|
|
||||||
DoubleAmpersand,
|
|
||||||
DoubleDot,
|
|
||||||
DoubleEqual,
|
|
||||||
DoublePipe,
|
|
||||||
Equal,
|
|
||||||
Greater,
|
|
||||||
GreaterOrEqual,
|
|
||||||
LeftCurlyBrace,
|
|
||||||
LeftParenthesis,
|
|
||||||
LeftSquareBrace,
|
|
||||||
Less,
|
|
||||||
LessOrEqual,
|
|
||||||
Minus,
|
|
||||||
MinusEqual,
|
|
||||||
Mut,
|
|
||||||
Percent,
|
|
||||||
Plus,
|
|
||||||
PlusEqual,
|
|
||||||
RightCurlyBrace,
|
|
||||||
RightParenthesis,
|
|
||||||
RightSquareBrace,
|
|
||||||
Semicolon,
|
|
||||||
Star,
|
|
||||||
Struct,
|
|
||||||
Slash,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TokenKind {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
TokenKind::Async => Token::Async.fmt(f),
|
|
||||||
TokenKind::Bang => Token::Bang.fmt(f),
|
|
||||||
TokenKind::BangEqual => Token::BangEqual.fmt(f),
|
|
||||||
TokenKind::Bool => Token::Bool.fmt(f),
|
|
||||||
TokenKind::Boolean => write!(f, "boolean value"),
|
|
||||||
TokenKind::Break => Token::Break.fmt(f),
|
|
||||||
TokenKind::Character => write!(f, "character value"),
|
|
||||||
TokenKind::Colon => Token::Colon.fmt(f),
|
|
||||||
TokenKind::Comma => Token::Comma.fmt(f),
|
|
||||||
TokenKind::Dot => Token::Dot.fmt(f),
|
|
||||||
TokenKind::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
|
||||||
TokenKind::DoubleDot => Token::DoubleDot.fmt(f),
|
|
||||||
TokenKind::DoubleEqual => Token::DoubleEqual.fmt(f),
|
|
||||||
TokenKind::DoublePipe => Token::DoublePipe.fmt(f),
|
|
||||||
TokenKind::Else => Token::Else.fmt(f),
|
|
||||||
TokenKind::Eof => Token::Eof.fmt(f),
|
|
||||||
TokenKind::Equal => Token::Equal.fmt(f),
|
|
||||||
TokenKind::Float => write!(f, "float value"),
|
|
||||||
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
|
|
||||||
TokenKind::Greater => Token::Greater.fmt(f),
|
|
||||||
TokenKind::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
|
||||||
TokenKind::Identifier => write!(f, "identifier"),
|
|
||||||
TokenKind::If => Token::If.fmt(f),
|
|
||||||
TokenKind::Int => Token::Int.fmt(f),
|
|
||||||
TokenKind::Integer => write!(f, "integer value"),
|
|
||||||
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
|
||||||
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
|
||||||
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
|
||||||
TokenKind::Let => Token::Let.fmt(f),
|
|
||||||
TokenKind::Less => Token::Less.fmt(f),
|
|
||||||
TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
|
|
||||||
TokenKind::Loop => Token::Loop.fmt(f),
|
|
||||||
TokenKind::Map => Token::Map.fmt(f),
|
|
||||||
TokenKind::Minus => Token::Minus.fmt(f),
|
|
||||||
TokenKind::MinusEqual => Token::MinusEqual.fmt(f),
|
|
||||||
TokenKind::Mut => Token::Mut.fmt(f),
|
|
||||||
TokenKind::Percent => Token::Percent.fmt(f),
|
|
||||||
TokenKind::Plus => Token::Plus.fmt(f),
|
|
||||||
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
|
|
||||||
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
|
||||||
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
|
|
||||||
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
|
||||||
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
|
||||||
TokenKind::Star => Token::Star.fmt(f),
|
|
||||||
TokenKind::Str => Token::Str.fmt(f),
|
|
||||||
TokenKind::Slash => Token::Slash.fmt(f),
|
|
||||||
TokenKind::String => write!(f, "string value"),
|
|
||||||
TokenKind::Struct => Token::Struct.fmt(f),
|
|
||||||
TokenKind::While => Token::While.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn all_tokens<'src>() -> [Token<'src>; 47] {
|
|
||||||
[
|
|
||||||
Token::Async,
|
|
||||||
Token::Bang,
|
|
||||||
Token::BangEqual,
|
|
||||||
Token::Bool,
|
|
||||||
Token::Break,
|
|
||||||
Token::Colon,
|
|
||||||
Token::Comma,
|
|
||||||
Token::Dot,
|
|
||||||
Token::DoubleAmpersand,
|
|
||||||
Token::DoubleDot,
|
|
||||||
Token::DoubleEqual,
|
|
||||||
Token::DoublePipe,
|
|
||||||
Token::Else,
|
|
||||||
Token::Eof,
|
|
||||||
Token::Equal,
|
|
||||||
Token::FloatKeyword,
|
|
||||||
Token::Greater,
|
|
||||||
Token::GreaterEqual,
|
|
||||||
Token::If,
|
|
||||||
Token::Int,
|
|
||||||
Token::LeftCurlyBrace,
|
|
||||||
Token::LeftParenthesis,
|
|
||||||
Token::LeftSquareBrace,
|
|
||||||
Token::Let,
|
|
||||||
Token::Less,
|
|
||||||
Token::LessEqual,
|
|
||||||
Token::Map,
|
|
||||||
Token::Minus,
|
|
||||||
Token::MinusEqual,
|
|
||||||
Token::Mut,
|
|
||||||
Token::Percent,
|
|
||||||
Token::Plus,
|
|
||||||
Token::PlusEqual,
|
|
||||||
Token::RightCurlyBrace,
|
|
||||||
Token::RightParenthesis,
|
|
||||||
Token::RightSquareBrace,
|
|
||||||
Token::Semicolon,
|
|
||||||
Token::Star,
|
|
||||||
Token::Str,
|
|
||||||
Token::Slash,
|
|
||||||
Token::Boolean("true"),
|
|
||||||
Token::Float("0.0"),
|
|
||||||
Token::Integer("0"),
|
|
||||||
Token::String("string"),
|
|
||||||
Token::Identifier("foobar"),
|
|
||||||
Token::Struct,
|
|
||||||
Token::While,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn token_displays() {
|
|
||||||
for token in all_tokens().iter() {
|
|
||||||
let display = token.to_string();
|
|
||||||
|
|
||||||
assert_eq!(display, token.to_owned().to_string());
|
|
||||||
|
|
||||||
if let Token::Boolean(_)
|
|
||||||
| Token::Float(_)
|
|
||||||
| Token::Identifier(_)
|
|
||||||
| Token::Integer(_)
|
|
||||||
| Token::String(_) = token
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
assert_eq!(display, token.kind().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Constructor, BuiltInFunction, Identifier};
|
use crate::{Constructor, Identifier};
|
||||||
|
|
||||||
/// Description of a kind of value.
|
/// Description of a kind of value.
|
||||||
///
|
///
|
||||||
@ -258,7 +258,6 @@ impl Type {
|
|||||||
|
|
||||||
pub fn get_field_type(&self, field: &Identifier) -> Option<Type> {
|
pub fn get_field_type(&self, field: &Identifier) -> Option<Type> {
|
||||||
match field.as_str() {
|
match field.as_str() {
|
||||||
"to_string" => Some(BuiltInFunction::ToString.r#type()),
|
|
||||||
"length" => match self {
|
"length" => match self {
|
||||||
Type::List { .. } => Some(Type::Integer),
|
Type::List { .. } => Some(Type::Integer),
|
||||||
Type::ListOf { .. } => Some(Type::Integer),
|
Type::ListOf { .. } => Some(Type::Integer),
|
||||||
|
@ -14,10 +14,7 @@ use serde::{
|
|||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type};
|
||||||
AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType,
|
|
||||||
FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
@ -98,10 +95,6 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(value: Function) -> Self {
|
|
||||||
Value::Raw(ValueData::Function(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn range<T: Into<RangeValue>>(range: T) -> Self {
|
pub fn range<T: Into<RangeValue>>(range: T) -> Self {
|
||||||
Value::Raw(ValueData::Range(range.into()))
|
Value::Raw(ValueData::Range(range.into()))
|
||||||
}
|
}
|
||||||
@ -359,9 +352,6 @@ impl Value {
|
|||||||
ValueData::Float(float) => Some(Value::boolean(float % 2.0 != 0.0)),
|
ValueData::Float(float) => Some(Value::boolean(float % 2.0 != 0.0)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
"to_string" => Some(Value::function(Function::BuiltIn(
|
|
||||||
BuiltInFunction::ToString,
|
|
||||||
))),
|
|
||||||
"length" => match data {
|
"length" => match data {
|
||||||
ValueData::List(values) => Some(Value::integer(values.len() as i64)),
|
ValueData::List(values) => Some(Value::integer(values.len() as i64)),
|
||||||
ValueData::String(string) => Some(Value::integer(string.len() as i64)),
|
ValueData::String(string) => Some(Value::integer(string.len() as i64)),
|
||||||
@ -934,7 +924,6 @@ pub enum ValueData {
|
|||||||
Character(char),
|
Character(char),
|
||||||
Enum(Enum),
|
Enum(Enum),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Function(Function),
|
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
List(Vec<Value>),
|
List(Vec<Value>),
|
||||||
Map(HashMap<Identifier, Value>),
|
Map(HashMap<Identifier, Value>),
|
||||||
@ -952,15 +941,6 @@ impl ValueData {
|
|||||||
ValueData::Character(_) => Type::Character,
|
ValueData::Character(_) => Type::Character,
|
||||||
ValueData::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
ValueData::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
||||||
ValueData::Float(_) => Type::Float,
|
ValueData::Float(_) => Type::Float,
|
||||||
ValueData::Function(Function::BuiltIn(built_in_function)) => {
|
|
||||||
Type::Function(FunctionType {
|
|
||||||
name: Identifier::new(built_in_function.name()),
|
|
||||||
type_parameters: built_in_function.type_parameters(),
|
|
||||||
value_parameters: built_in_function.value_parameters(),
|
|
||||||
return_type: built_in_function.return_type().map(Box::new),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ValueData::Function(Function::Parsed { r#type, .. }) => Type::Function(r#type.clone()),
|
|
||||||
ValueData::Integer(_) => Type::Integer,
|
ValueData::Integer(_) => Type::Integer,
|
||||||
ValueData::List(values) => {
|
ValueData::List(values) => {
|
||||||
let item_type = values.first().unwrap().r#type();
|
let item_type = values.first().unwrap().r#type();
|
||||||
@ -1202,7 +1182,6 @@ impl Display for ValueData {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ValueData::Function(function) => write!(f, "{function}"),
|
|
||||||
ValueData::Integer(integer) => write!(f, "{integer}"),
|
ValueData::Integer(integer) => write!(f, "{integer}"),
|
||||||
ValueData::Map(pairs) => {
|
ValueData::Map(pairs) => {
|
||||||
write!(f, "{{ ")?;
|
write!(f, "{{ ")?;
|
||||||
@ -1261,7 +1240,6 @@ impl PartialEq for ValueData {
|
|||||||
(ValueData::Byte(left), ValueData::Byte(right)) => left == right,
|
(ValueData::Byte(left), ValueData::Byte(right)) => left == right,
|
||||||
(ValueData::Character(left), ValueData::Character(right)) => left == right,
|
(ValueData::Character(left), ValueData::Character(right)) => left == right,
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => left == right,
|
(ValueData::Float(left), ValueData::Float(right)) => left == right,
|
||||||
(ValueData::Function(left), ValueData::Function(right)) => left == right,
|
|
||||||
(ValueData::Integer(left), ValueData::Integer(right)) => left == right,
|
(ValueData::Integer(left), ValueData::Integer(right)) => left == right,
|
||||||
(ValueData::List(left), ValueData::List(right)) => left == right,
|
(ValueData::List(left), ValueData::List(right)) => left == right,
|
||||||
(ValueData::Map(left), ValueData::Map(right)) => left == right,
|
(ValueData::Map(left), ValueData::Map(right)) => left == right,
|
||||||
@ -1291,8 +1269,6 @@ impl Ord for ValueData {
|
|||||||
(ValueData::Character(_), _) => Ordering::Greater,
|
(ValueData::Character(_), _) => Ordering::Greater,
|
||||||
(ValueData::Float(left), ValueData::Float(right)) => left.partial_cmp(right).unwrap(),
|
(ValueData::Float(left), ValueData::Float(right)) => left.partial_cmp(right).unwrap(),
|
||||||
(ValueData::Float(_), _) => Ordering::Greater,
|
(ValueData::Float(_), _) => Ordering::Greater,
|
||||||
(ValueData::Function(left), ValueData::Function(right)) => left.cmp(right),
|
|
||||||
(ValueData::Function(_), _) => Ordering::Greater,
|
|
||||||
(ValueData::Integer(left), ValueData::Integer(right)) => left.cmp(right),
|
(ValueData::Integer(left), ValueData::Integer(right)) => left.cmp(right),
|
||||||
(ValueData::Integer(_), _) => Ordering::Greater,
|
(ValueData::Integer(_), _) => Ordering::Greater,
|
||||||
(ValueData::List(left), ValueData::List(right)) => left.cmp(right),
|
(ValueData::List(left), ValueData::List(right)) => left.cmp(right),
|
||||||
@ -1320,7 +1296,6 @@ impl Serialize for ValueData {
|
|||||||
ValueData::Character(character) => serializer.serialize_char(*character),
|
ValueData::Character(character) => serializer.serialize_char(*character),
|
||||||
ValueData::Enum(r#emum) => r#emum.serialize(serializer),
|
ValueData::Enum(r#emum) => r#emum.serialize(serializer),
|
||||||
ValueData::Float(float) => serializer.serialize_f64(*float),
|
ValueData::Float(float) => serializer.serialize_f64(*float),
|
||||||
ValueData::Function(function) => function.serialize(serializer),
|
|
||||||
ValueData::Integer(integer) => serializer.serialize_i64(*integer),
|
ValueData::Integer(integer) => serializer.serialize_i64(*integer),
|
||||||
ValueData::List(list) => list.serialize(serializer),
|
ValueData::List(list) => list.serialize(serializer),
|
||||||
ValueData::Map(pairs) => {
|
ValueData::Map(pairs) => {
|
||||||
@ -1385,11 +1360,9 @@ impl<'de> Deserialize<'de> for ValueData {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum Function {
|
pub enum Function {
|
||||||
BuiltIn(BuiltInFunction),
|
|
||||||
Parsed {
|
Parsed {
|
||||||
name: Identifier,
|
name: Identifier,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
body: AbstractSyntaxTree,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1398,92 +1371,8 @@ impl Function {
|
|||||||
self,
|
self,
|
||||||
_type_arguments: Option<Vec<Type>>,
|
_type_arguments: Option<Vec<Type>>,
|
||||||
value_arguments: Option<Vec<Value>>,
|
value_arguments: Option<Vec<Value>>,
|
||||||
context: &Context,
|
) -> Result<Option<Value>, ()> {
|
||||||
) -> Result<Option<Value>, FunctionCallError> {
|
todo!()
|
||||||
match self {
|
|
||||||
Function::BuiltIn(built_in_function) => built_in_function
|
|
||||||
.call(_type_arguments, value_arguments)
|
|
||||||
.map_err(FunctionCallError::BuiltInFunction),
|
|
||||||
Function::Parsed { r#type, body, .. } => {
|
|
||||||
let new_context =
|
|
||||||
Context::with_data_from(context).map_err(FunctionCallError::Context)?;
|
|
||||||
|
|
||||||
if let (Some(value_parameters), Some(value_arguments)) =
|
|
||||||
(&r#type.value_parameters, value_arguments)
|
|
||||||
{
|
|
||||||
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
|
|
||||||
new_context
|
|
||||||
.set_variable_value(identifier.clone(), value)
|
|
||||||
.map_err(FunctionCallError::Context)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vm.run(body)
|
|
||||||
.map_err(|error| FunctionCallError::Runtime(Box::new(error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Function {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Function::BuiltIn(built_in_function) => write!(f, "{}", built_in_function),
|
|
||||||
Function::Parsed { name, r#type, body } => {
|
|
||||||
write!(f, "fn {}", name)?;
|
|
||||||
|
|
||||||
if let Some(type_parameters) = &r#type.type_parameters {
|
|
||||||
write!(f, "<")?;
|
|
||||||
|
|
||||||
for (index, type_parameter) in type_parameters.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{}", type_parameter)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "(")?;
|
|
||||||
|
|
||||||
if let Some(value_paramers) = &r#type.value_parameters {
|
|
||||||
for (index, (identifier, r#type)) in value_paramers.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{identifier}: {type}")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ") {{")?;
|
|
||||||
|
|
||||||
for statement in &body.statements {
|
|
||||||
write!(f, "{}", statement)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum FunctionCallError {
|
|
||||||
BuiltInFunction(BuiltInFunctionError),
|
|
||||||
Context(ContextError),
|
|
||||||
Runtime(Box<RuntimeError>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FunctionCallError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
FunctionCallError::BuiltInFunction(error) => write!(f, "{}", error),
|
|
||||||
FunctionCallError::Context(error) => write!(f, "{}", error),
|
|
||||||
FunctionCallError::Runtime(error) => write!(f, "{}", error),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1850
dust-lang/src/vm.rs
1850
dust-lang/src/vm.rs
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
|||||||
use dust_lang::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_scope_captures_parent() {
|
|
||||||
let source = "let x = 42; { x }";
|
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_scope_does_not_capture_child() {
|
|
||||||
env_logger::builder().is_test(true).try_init().unwrap();
|
|
||||||
|
|
||||||
let source = "{ let x = 42; } x";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
run(source),
|
|
||||||
Err(DustError::analysis(
|
|
||||||
[AnalysisError::UndefinedVariable {
|
|
||||||
identifier: Node::new(Identifier::new("x"), (16, 17))
|
|
||||||
}],
|
|
||||||
source
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_scope_does_not_capture_sibling() {
|
|
||||||
let source = "{ let x = 42; } { x }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
run(source),
|
|
||||||
Err(DustError::analysis(
|
|
||||||
[AnalysisError::UndefinedVariable {
|
|
||||||
identifier: Node::new(Identifier::new("x"), (18, 19))
|
|
||||||
}],
|
|
||||||
source
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_scope_does_not_pollute_parent() {
|
|
||||||
let source = "let x = 42; { let x = \"foo\"; let x = \"bar\"; } x";
|
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dust_lang::run;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
@ -37,19 +36,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_and_display_errors(source: &str) {
|
fn parse_and_display_errors(source: &str) {
|
||||||
match dust_lang::parse(source) {
|
todo!()
|
||||||
Ok(ast) => println!("{:#?}", ast),
|
|
||||||
Err(error) => eprintln!("{}", error.report()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_and_display_errors(source: &str) {
|
fn run_and_display_errors(source: &str) {
|
||||||
match run(source) {
|
todo!()
|
||||||
Ok(return_value) => {
|
|
||||||
if let Some(value) = return_value {
|
|
||||||
println!("{}", value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => eprintln!("{}", error.report()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user