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)));
|
||||
//! ```
|
||||
pub mod analyzer;
|
||||
pub mod ast;
|
||||
pub mod built_in_function;
|
||||
pub mod bytecode;
|
||||
pub mod constructor;
|
||||
pub mod context;
|
||||
pub mod core_library;
|
||||
pub mod dust_error;
|
||||
pub mod evaluation;
|
||||
pub mod identifier;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
pub mod r#type;
|
||||
pub mod value;
|
||||
pub mod vm;
|
||||
|
||||
pub use analyzer::{analyze, AnalysisError, Analyzer};
|
||||
pub use ast::{AbstractSyntaxTree, AstError, Expression, Node, Span, Statement};
|
||||
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
|
||||
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 bytecode::*;
|
||||
pub use constructor::*;
|
||||
pub use identifier::*;
|
||||
pub use r#type::*;
|
||||
pub use token::{Token, TokenKind, TokenOwned};
|
||||
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 crate::{Constructor, BuiltInFunction, Identifier};
|
||||
use crate::{Constructor, Identifier};
|
||||
|
||||
/// Description of a kind of value.
|
||||
///
|
||||
@ -258,7 +258,6 @@ impl Type {
|
||||
|
||||
pub fn get_field_type(&self, field: &Identifier) -> Option<Type> {
|
||||
match field.as_str() {
|
||||
"to_string" => Some(BuiltInFunction::ToString.r#type()),
|
||||
"length" => match self {
|
||||
Type::List { .. } => Some(Type::Integer),
|
||||
Type::ListOf { .. } => Some(Type::Integer),
|
||||
|
@ -14,10 +14,7 @@ use serde::{
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType,
|
||||
FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
|
||||
};
|
||||
use crate::{EnumType, FunctionType, Identifier, RangeableType, StructType, Type};
|
||||
|
||||
/// 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 {
|
||||
Value::Raw(ValueData::Range(range.into()))
|
||||
}
|
||||
@ -359,9 +352,6 @@ impl Value {
|
||||
ValueData::Float(float) => Some(Value::boolean(float % 2.0 != 0.0)),
|
||||
_ => None,
|
||||
},
|
||||
"to_string" => Some(Value::function(Function::BuiltIn(
|
||||
BuiltInFunction::ToString,
|
||||
))),
|
||||
"length" => match data {
|
||||
ValueData::List(values) => Some(Value::integer(values.len() as i64)),
|
||||
ValueData::String(string) => Some(Value::integer(string.len() as i64)),
|
||||
@ -934,7 +924,6 @@ pub enum ValueData {
|
||||
Character(char),
|
||||
Enum(Enum),
|
||||
Float(f64),
|
||||
Function(Function),
|
||||
Integer(i64),
|
||||
List(Vec<Value>),
|
||||
Map(HashMap<Identifier, Value>),
|
||||
@ -952,15 +941,6 @@ impl ValueData {
|
||||
ValueData::Character(_) => Type::Character,
|
||||
ValueData::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
||||
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::List(values) => {
|
||||
let item_type = values.first().unwrap().r#type();
|
||||
@ -1202,7 +1182,6 @@ impl Display for ValueData {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
ValueData::Function(function) => write!(f, "{function}"),
|
||||
ValueData::Integer(integer) => write!(f, "{integer}"),
|
||||
ValueData::Map(pairs) => {
|
||||
write!(f, "{{ ")?;
|
||||
@ -1261,7 +1240,6 @@ impl PartialEq for ValueData {
|
||||
(ValueData::Byte(left), ValueData::Byte(right)) => left == right,
|
||||
(ValueData::Character(left), ValueData::Character(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::List(left), ValueData::List(right)) => left == right,
|
||||
(ValueData::Map(left), ValueData::Map(right)) => left == right,
|
||||
@ -1291,8 +1269,6 @@ impl Ord for ValueData {
|
||||
(ValueData::Character(_), _) => Ordering::Greater,
|
||||
(ValueData::Float(left), ValueData::Float(right)) => left.partial_cmp(right).unwrap(),
|
||||
(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(_), _) => Ordering::Greater,
|
||||
(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::Enum(r#emum) => r#emum.serialize(serializer),
|
||||
ValueData::Float(float) => serializer.serialize_f64(*float),
|
||||
ValueData::Function(function) => function.serialize(serializer),
|
||||
ValueData::Integer(integer) => serializer.serialize_i64(*integer),
|
||||
ValueData::List(list) => list.serialize(serializer),
|
||||
ValueData::Map(pairs) => {
|
||||
@ -1385,11 +1360,9 @@ impl<'de> Deserialize<'de> for ValueData {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum Function {
|
||||
BuiltIn(BuiltInFunction),
|
||||
Parsed {
|
||||
name: Identifier,
|
||||
r#type: FunctionType,
|
||||
body: AbstractSyntaxTree,
|
||||
},
|
||||
}
|
||||
|
||||
@ -1398,92 +1371,8 @@ impl Function {
|
||||
self,
|
||||
_type_arguments: Option<Vec<Type>>,
|
||||
value_arguments: Option<Vec<Value>>,
|
||||
context: &Context,
|
||||
) -> Result<Option<Value>, FunctionCallError> {
|
||||
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),
|
||||
}
|
||||
) -> Result<Option<Value>, ()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
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 clap::Parser;
|
||||
use dust_lang::run;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
@ -37,19 +36,9 @@ fn main() {
|
||||
}
|
||||
|
||||
fn parse_and_display_errors(source: &str) {
|
||||
match dust_lang::parse(source) {
|
||||
Ok(ast) => println!("{:#?}", ast),
|
||||
Err(error) => eprintln!("{}", error.report()),
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn run_and_display_errors(source: &str) {
|
||||
match run(source) {
|
||||
Ok(return_value) => {
|
||||
if let Some(value) = return_value {
|
||||
println!("{}", value);
|
||||
}
|
||||
}
|
||||
Err(error) => eprintln!("{}", error.report()),
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user