Add a parser test; Pass VM test
This commit is contained in:
parent
8b5eb9977c
commit
cf9a9837c8
@ -6,7 +6,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Context, FunctionType, Identifier, RangeableType, StructType, Type};
|
use crate::{BuiltInFunction, Context, FunctionType, Identifier, RangeableType, StructType, Type};
|
||||||
|
|
||||||
use super::{Node, Span, Statement};
|
use super::{Node, Span, Statement};
|
||||||
|
|
||||||
@ -338,6 +338,9 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Literal(literal_expression) => match literal_expression.inner.as_ref() {
|
Expression::Literal(literal_expression) => match literal_expression.inner.as_ref() {
|
||||||
|
LiteralExpression::BuiltInFunction(built_in_function) => {
|
||||||
|
built_in_function.return_type()
|
||||||
|
}
|
||||||
LiteralExpression::Primitive(primitive_value) => match primitive_value {
|
LiteralExpression::Primitive(primitive_value) => match primitive_value {
|
||||||
PrimitiveValueExpression::Boolean(_) => Some(Type::Boolean),
|
PrimitiveValueExpression::Boolean(_) => Some(Type::Boolean),
|
||||||
PrimitiveValueExpression::Character(_) => Some(Type::Character),
|
PrimitiveValueExpression::Character(_) => Some(Type::Character),
|
||||||
@ -682,6 +685,7 @@ impl From<char> for LiteralExpression {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum LiteralExpression {
|
pub enum LiteralExpression {
|
||||||
|
BuiltInFunction(BuiltInFunction),
|
||||||
Primitive(PrimitiveValueExpression),
|
Primitive(PrimitiveValueExpression),
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
@ -689,6 +693,9 @@ pub enum LiteralExpression {
|
|||||||
impl Display for LiteralExpression {
|
impl Display for LiteralExpression {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
LiteralExpression::BuiltInFunction(built_in_function) => {
|
||||||
|
write!(f, "{built_in_function}")
|
||||||
|
}
|
||||||
LiteralExpression::Primitive(primitive) => {
|
LiteralExpression::Primitive(primitive) => {
|
||||||
write!(f, "{primitive}")
|
write!(f, "{primitive}")
|
||||||
}
|
}
|
||||||
@ -708,6 +715,11 @@ impl PartialOrd for LiteralExpression {
|
|||||||
impl Ord for LiteralExpression {
|
impl Ord for LiteralExpression {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(
|
||||||
|
LiteralExpression::BuiltInFunction(left),
|
||||||
|
LiteralExpression::BuiltInFunction(right),
|
||||||
|
) => left.cmp(right),
|
||||||
|
(LiteralExpression::BuiltInFunction(_), _) => Ordering::Greater,
|
||||||
(LiteralExpression::Primitive(left), LiteralExpression::Primitive(right)) => {
|
(LiteralExpression::Primitive(left), LiteralExpression::Primitive(right)) => {
|
||||||
left.cmp(right)
|
left.cmp(right)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ use crate::{Identifier, Type, Value};
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum BuiltInFunction {
|
pub enum BuiltInFunction {
|
||||||
// String tools
|
// String tools
|
||||||
ToString,
|
ToString { argument: Box<Value> },
|
||||||
|
|
||||||
// Integer and float tools
|
// Integer and float tools
|
||||||
IsEven,
|
IsEven,
|
||||||
@ -34,26 +34,46 @@ impl BuiltInFunction {
|
|||||||
BuiltInFunction::IsOdd => "is_odd",
|
BuiltInFunction::IsOdd => "is_odd",
|
||||||
BuiltInFunction::Length => "length",
|
BuiltInFunction::Length => "length",
|
||||||
BuiltInFunction::ReadLine => "read_line",
|
BuiltInFunction::ReadLine => "read_line",
|
||||||
BuiltInFunction::ToString => "to_string",
|
BuiltInFunction::ToString { .. } => "to_string",
|
||||||
BuiltInFunction::WriteLine => "write_line",
|
BuiltInFunction::WriteLine => "write_line",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_parameters(&self) -> Vec<(Identifier, Type)> {
|
pub fn type_parameters(&self) -> Option<Vec<Identifier>> {
|
||||||
match self {
|
match self {
|
||||||
BuiltInFunction::ToString => vec![("value".into(), Type::Any)],
|
BuiltInFunction::ToString { .. } => None,
|
||||||
BuiltInFunction::IsEven => vec![("value".into(), Type::Number)],
|
BuiltInFunction::IsEven => None,
|
||||||
BuiltInFunction::IsOdd => vec![("value".into(), Type::Number)],
|
BuiltInFunction::IsOdd => None,
|
||||||
BuiltInFunction::Length => {
|
BuiltInFunction::Length => None,
|
||||||
vec![(
|
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::Length => Some(vec![(
|
||||||
"value".into(),
|
"value".into(),
|
||||||
Type::ListOf {
|
Type::ListOf {
|
||||||
item_type: Box::new(Type::Any),
|
item_type: Box::new(Type::Any),
|
||||||
},
|
},
|
||||||
)]
|
)]),
|
||||||
|
BuiltInFunction::ReadLine => None,
|
||||||
|
BuiltInFunction::WriteLine => Some(vec![("output".into(), Type::Any)]),
|
||||||
}
|
}
|
||||||
BuiltInFunction::ReadLine => vec![],
|
}
|
||||||
BuiltInFunction::WriteLine => vec![("output".into(), Type::Any)],
|
|
||||||
|
pub fn return_type(&self) -> Option<Type> {
|
||||||
|
match self {
|
||||||
|
BuiltInFunction::ToString { .. } => Some(Type::String),
|
||||||
|
BuiltInFunction::IsEven => Some(Type::Boolean),
|
||||||
|
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
||||||
|
BuiltInFunction::Length => Some(Type::Number),
|
||||||
|
BuiltInFunction::ReadLine => Some(Type::String),
|
||||||
|
BuiltInFunction::WriteLine => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,17 +83,7 @@ impl BuiltInFunction {
|
|||||||
value_arguments: Option<Vec<Value>>,
|
value_arguments: Option<Vec<Value>>,
|
||||||
) -> Result<Option<Value>, BuiltInFunctionError> {
|
) -> Result<Option<Value>, BuiltInFunctionError> {
|
||||||
match self {
|
match self {
|
||||||
BuiltInFunction::ToString => {
|
BuiltInFunction::ToString { argument } => Ok(Some(Value::string(argument))),
|
||||||
if let Some(value_arguments) = value_arguments {
|
|
||||||
if value_arguments.len() == 1 {
|
|
||||||
Ok(Some(Value::string(value_arguments[0].to_string())))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltInFunction::IsEven => {
|
BuiltInFunction::IsEven => {
|
||||||
if let Some(value_arguments) = value_arguments {
|
if let Some(value_arguments) = value_arguments {
|
||||||
if value_arguments.len() == 1 {
|
if value_arguments.len() == 1 {
|
||||||
@ -145,17 +155,6 @@ impl BuiltInFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expected_return_type(&self) -> Option<Type> {
|
|
||||||
match self {
|
|
||||||
BuiltInFunction::ToString => Some(Type::String),
|
|
||||||
BuiltInFunction::IsEven => Some(Type::Boolean),
|
|
||||||
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
|
||||||
BuiltInFunction::Length => Some(Type::Integer),
|
|
||||||
BuiltInFunction::ReadLine => Some(Type::String),
|
|
||||||
BuiltInFunction::WriteLine => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for BuiltInFunction {
|
impl Display for BuiltInFunction {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Top-level error handling for the Dust language.
|
//! Top-level error handling for the Dust language.
|
||||||
use annotate_snippets::{Level, Renderer, Snippet};
|
use annotate_snippets::{Level, Message, Renderer, Snippet};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::{AnalysisError, LexError, ParseError, RuntimeError};
|
use crate::{ast::Span, AnalysisError, LexError, ParseError, RuntimeError};
|
||||||
|
|
||||||
/// An error that occurred during the execution of the Dust language and its
|
/// An error that occurred during the execution of the Dust language and its
|
||||||
/// corresponding source code.
|
/// corresponding source code.
|
||||||
@ -61,12 +61,12 @@ impl<'src> DustError<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> (usize, usize) {
|
pub fn position(&self) -> Option<Span> {
|
||||||
match self {
|
match self {
|
||||||
DustError::Runtime { runtime_error, .. } => runtime_error.position(),
|
DustError::Runtime { runtime_error, .. } => runtime_error.position(),
|
||||||
DustError::Analysis { analysis_error, .. } => analysis_error.position(),
|
DustError::Analysis { analysis_error, .. } => Some(analysis_error.position()),
|
||||||
DustError::Parse { parse_error, .. } => parse_error.position(),
|
DustError::Parse { parse_error, .. } => Some(parse_error.position()),
|
||||||
DustError::Lex { lex_error, .. } => lex_error.position(),
|
DustError::Lex { lex_error, .. } => Some(lex_error.position()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +83,18 @@ impl<'src> DustError<'src> {
|
|||||||
let title = self.title();
|
let title = self.title();
|
||||||
let span = self.position();
|
let span = self.position();
|
||||||
let label = self.to_string();
|
let label = self.to_string();
|
||||||
let message = Level::Error.title(title).snippet(
|
|
||||||
|
let message = if let Some(span) = span {
|
||||||
|
Level::Error.title(title).snippet(
|
||||||
Snippet::source(self.source())
|
Snippet::source(self.source())
|
||||||
.annotation(Level::Info.span(span.0..span.1).label(&label)),
|
.annotation(Level::Info.span(span.0..span.1).label(&label)),
|
||||||
);
|
)
|
||||||
|
} else {
|
||||||
|
Level::Error
|
||||||
|
.title(title)
|
||||||
|
.snippet(Snippet::source(self.source()))
|
||||||
|
.footer(Level::Info.title("No position information available"))
|
||||||
|
};
|
||||||
let renderer = Renderer::styled();
|
let renderer = Renderer::styled();
|
||||||
|
|
||||||
format!("{}", renderer.render(message))
|
format!("{}", renderer.render(message))
|
||||||
|
@ -437,7 +437,6 @@ impl<'src> Lexer<'src> {
|
|||||||
"mut" => Token::Mut,
|
"mut" => Token::Mut,
|
||||||
"read_line" => Token::ReadLine,
|
"read_line" => Token::ReadLine,
|
||||||
"struct" => Token::Struct,
|
"struct" => Token::Struct,
|
||||||
"to_string" => Token::ToString,
|
|
||||||
"true" => Token::Boolean("true"),
|
"true" => Token::Boolean("true"),
|
||||||
"while" => Token::While,
|
"while" => Token::While,
|
||||||
"write_line" => Token::WriteLine,
|
"write_line" => Token::WriteLine,
|
||||||
|
@ -822,7 +822,7 @@ impl<'src> Parser<'src> {
|
|||||||
Expression::tuple_access(left, index_node, position)
|
Expression::tuple_access(left, index_node, position)
|
||||||
} else {
|
} else {
|
||||||
let field = self.parse_identifier()?;
|
let field = self.parse_identifier()?;
|
||||||
let position = (left.position().0, self.current_position.1);
|
let position = (left.position().0, field.position.1);
|
||||||
|
|
||||||
Expression::field_access(left, field, position)
|
Expression::field_access(left, field, position)
|
||||||
}
|
}
|
||||||
@ -1145,6 +1145,26 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn built_in_function() {
|
||||||
|
let source = "42.to_string()";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(AbstractSyntaxTree::with_statements([
|
||||||
|
Statement::Expression(Expression::call(
|
||||||
|
Expression::field_access(
|
||||||
|
Expression::literal(42, (0, 2)),
|
||||||
|
Node::new(Identifier::new("to_string"), (3, 12)),
|
||||||
|
(0, 12)
|
||||||
|
),
|
||||||
|
vec![],
|
||||||
|
(0, 14)
|
||||||
|
))
|
||||||
|
]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_expression() {
|
fn map_expression() {
|
||||||
let source = "map { x = '1', y = 2, z = 3.0 }";
|
let source = "map { x = '1', y = 2, z = 3.0 }";
|
||||||
|
@ -36,7 +36,6 @@ pub enum Token<'src> {
|
|||||||
ReadLine,
|
ReadLine,
|
||||||
Str,
|
Str,
|
||||||
Struct,
|
Struct,
|
||||||
ToString,
|
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
@ -123,7 +122,6 @@ impl<'src> Token<'src> {
|
|||||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||||
Token::Str => TokenOwned::Str,
|
Token::Str => TokenOwned::Str,
|
||||||
Token::Struct => TokenOwned::Struct,
|
Token::Struct => TokenOwned::Struct,
|
||||||
Token::ToString => TokenOwned::ToString,
|
|
||||||
Token::While => TokenOwned::While,
|
Token::While => TokenOwned::While,
|
||||||
Token::WriteLine => TokenOwned::WriteLine,
|
Token::WriteLine => TokenOwned::WriteLine,
|
||||||
}
|
}
|
||||||
@ -181,7 +179,6 @@ impl<'src> Token<'src> {
|
|||||||
Token::Slash => "/",
|
Token::Slash => "/",
|
||||||
Token::Str => "str",
|
Token::Str => "str",
|
||||||
Token::Struct => "struct",
|
Token::Struct => "struct",
|
||||||
Token::ToString => "to_string",
|
|
||||||
Token::While => "while",
|
Token::While => "while",
|
||||||
Token::WriteLine => "write_line",
|
Token::WriteLine => "write_line",
|
||||||
}
|
}
|
||||||
@ -238,7 +235,6 @@ impl<'src> Token<'src> {
|
|||||||
Token::Str => TokenKind::Str,
|
Token::Str => TokenKind::Str,
|
||||||
Token::String(_) => TokenKind::String,
|
Token::String(_) => TokenKind::String,
|
||||||
Token::Struct => TokenKind::Struct,
|
Token::Struct => TokenKind::Struct,
|
||||||
Token::ToString => TokenKind::ToString,
|
|
||||||
Token::While => TokenKind::While,
|
Token::While => TokenKind::While,
|
||||||
Token::WriteLine => TokenKind::WriteLine,
|
Token::WriteLine => TokenKind::WriteLine,
|
||||||
}
|
}
|
||||||
@ -332,7 +328,6 @@ pub enum TokenOwned {
|
|||||||
Mut,
|
Mut,
|
||||||
ReadLine,
|
ReadLine,
|
||||||
Str,
|
Str,
|
||||||
ToString,
|
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
@ -421,7 +416,6 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Str => Token::Str.fmt(f),
|
TokenOwned::Str => Token::Str.fmt(f),
|
||||||
TokenOwned::String(string) => write!(f, "{string}"),
|
TokenOwned::String(string) => write!(f, "{string}"),
|
||||||
TokenOwned::Struct => Token::Struct.fmt(f),
|
TokenOwned::Struct => Token::Struct.fmt(f),
|
||||||
TokenOwned::ToString => Token::ToString.fmt(f),
|
|
||||||
TokenOwned::While => Token::While.fmt(f),
|
TokenOwned::While => Token::While.fmt(f),
|
||||||
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
||||||
}
|
}
|
||||||
@ -455,7 +449,6 @@ pub enum TokenKind {
|
|||||||
Map,
|
Map,
|
||||||
ReadLine,
|
ReadLine,
|
||||||
Str,
|
Str,
|
||||||
ToString,
|
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
@ -544,7 +537,6 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Slash => Token::Slash.fmt(f),
|
TokenKind::Slash => Token::Slash.fmt(f),
|
||||||
TokenKind::String => write!(f, "string value"),
|
TokenKind::String => write!(f, "string value"),
|
||||||
TokenKind::Struct => Token::Struct.fmt(f),
|
TokenKind::Struct => Token::Struct.fmt(f),
|
||||||
TokenKind::ToString => Token::ToString.fmt(f),
|
|
||||||
TokenKind::While => Token::While.fmt(f),
|
TokenKind::While => Token::While.fmt(f),
|
||||||
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
||||||
}
|
}
|
||||||
@ -555,7 +547,7 @@ impl Display for TokenKind {
|
|||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn all_tokens<'src>() -> [Token<'src>; 52] {
|
pub fn all_tokens<'src>() -> [Token<'src>; 51] {
|
||||||
[
|
[
|
||||||
Token::Identifier("foobar"),
|
Token::Identifier("foobar"),
|
||||||
Token::Boolean("true"),
|
Token::Boolean("true"),
|
||||||
@ -606,7 +598,6 @@ pub(crate) mod tests {
|
|||||||
Token::Star,
|
Token::Star,
|
||||||
Token::Str,
|
Token::Str,
|
||||||
Token::Struct,
|
Token::Struct,
|
||||||
Token::ToString,
|
|
||||||
Token::While,
|
Token::While,
|
||||||
Token::WriteLine,
|
Token::WriteLine,
|
||||||
]
|
]
|
||||||
|
@ -371,7 +371,7 @@ impl Ord for Type {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct FunctionType {
|
pub struct FunctionType {
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub type_parameters: Option<Vec<Type>>,
|
pub type_parameters: Option<Vec<Identifier>>,
|
||||||
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
||||||
pub return_type: Option<Box<Type>>,
|
pub return_type: Option<Box<Type>>,
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ use serde::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractSyntaxTree, Context, EnumType, FunctionType, Identifier, RangeableType, RuntimeError,
|
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier,
|
||||||
StructType, Type, Vm,
|
RangeableType, RuntimeError, StructType, Type, Vm,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
@ -194,7 +194,15 @@ impl Value {
|
|||||||
Value::Character(_) => Type::Character,
|
Value::Character(_) => Type::Character,
|
||||||
Value::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
Value::Enum(Enum { r#type, .. }) => Type::Enum(r#type.clone()),
|
||||||
Value::Float(_) => Type::Float,
|
Value::Float(_) => Type::Float,
|
||||||
Value::Function(function) => Type::Function(function.r#type.clone()),
|
Value::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),
|
||||||
|
}),
|
||||||
|
Value::Function(Function::Parsed { name, r#type, body }) => {
|
||||||
|
Type::Function(r#type.clone())
|
||||||
|
}
|
||||||
Value::Integer(_) => Type::Integer,
|
Value::Integer(_) => Type::Integer,
|
||||||
Value::List(values) => {
|
Value::List(values) => {
|
||||||
let item_type = values.first().unwrap().r#type();
|
let item_type = values.first().unwrap().r#type();
|
||||||
@ -255,6 +263,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_field(&self, field: &Identifier) -> Option<Value> {
|
pub fn get_field(&self, field: &Identifier) -> Option<Value> {
|
||||||
|
if let "to_string" = field.as_str() {
|
||||||
|
return Some(Value::Function(Function::BuiltIn(
|
||||||
|
BuiltInFunction::ToString {
|
||||||
|
argument: Box::new(self.clone()),
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Value::Mutable(inner) => inner.read().unwrap().get_field(field),
|
Value::Mutable(inner) => inner.read().unwrap().get_field(field),
|
||||||
Value::Struct(Struct::Fields { fields, .. }) => fields.get(field).cloned(),
|
Value::Struct(Struct::Fields { fields, .. }) => fields.get(field).cloned(),
|
||||||
@ -1059,41 +1075,54 @@ impl<'de> Deserialize<'de> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Function {
|
pub enum Function {
|
||||||
pub name: Identifier,
|
BuiltIn(BuiltInFunction),
|
||||||
pub r#type: FunctionType,
|
Parsed {
|
||||||
pub body: Arc<AbstractSyntaxTree>,
|
name: Identifier,
|
||||||
|
r#type: FunctionType,
|
||||||
|
body: AbstractSyntaxTree,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
self,
|
||||||
_type_arguments: Option<Vec<Type>>,
|
_type_arguments: Option<Vec<Type>>,
|
||||||
value_arguments: Option<Vec<Value>>,
|
value_arguments: Option<Vec<Value>>,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Result<Option<Value>, RuntimeError> {
|
) -> Result<Option<Value>, RuntimeError> {
|
||||||
|
match self {
|
||||||
|
Function::BuiltIn(built_in_function) => built_in_function
|
||||||
|
.call(_type_arguments, value_arguments)
|
||||||
|
.map_err(|error| RuntimeError::BuiltInFunctionError { error }),
|
||||||
|
Function::Parsed { r#type, body, .. } => {
|
||||||
let new_context = Context::with_data_from(context);
|
let new_context = Context::with_data_from(context);
|
||||||
|
|
||||||
if let (Some(value_parameters), Some(value_arguments)) =
|
if let (Some(value_parameters), Some(value_arguments)) =
|
||||||
(&self.r#type.value_parameters, value_arguments)
|
(&r#type.value_parameters, value_arguments)
|
||||||
{
|
{
|
||||||
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
|
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
|
||||||
new_context.set_variable_value(identifier.clone(), value);
|
new_context.set_variable_value(identifier.clone(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vm = Vm::new(self.body.as_ref().clone(), new_context);
|
let mut vm = Vm::new(body, new_context);
|
||||||
|
|
||||||
vm.run()
|
vm.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Function {
|
impl Display for Function {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "fn {}", self.name)?;
|
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) = &self.r#type.type_parameters {
|
if let Some(type_parameters) = &r#type.type_parameters {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
|
||||||
for (index, type_parameter) in type_parameters.iter().enumerate() {
|
for (index, type_parameter) in type_parameters.iter().enumerate() {
|
||||||
@ -1109,7 +1138,7 @@ impl Display for Function {
|
|||||||
|
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
|
|
||||||
if let Some(value_paramers) = &self.r#type.value_parameters {
|
if let Some(value_paramers) = &r#type.value_parameters {
|
||||||
for (index, (identifier, r#type)) in value_paramers.iter().enumerate() {
|
for (index, (identifier, r#type)) in value_paramers.iter().enumerate() {
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
write!(f, ", ")?;
|
write!(f, ", ")?;
|
||||||
@ -1121,89 +1150,13 @@ impl Display for Function {
|
|||||||
|
|
||||||
write!(f, ") {{")?;
|
write!(f, ") {{")?;
|
||||||
|
|
||||||
for statement in &self.body.statements {
|
for statement in &body.statements {
|
||||||
write!(f, "{}", statement)?;
|
write!(f, "{}", statement)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Function {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let mut ser = serializer.serialize_struct("Function", 3)?;
|
|
||||||
|
|
||||||
ser.serialize_field("name", &self.name)?;
|
|
||||||
ser.serialize_field("type", &self.r#type)?;
|
|
||||||
ser.serialize_field("body", self.body.as_ref())?;
|
|
||||||
|
|
||||||
ser.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Function {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct FunctionVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for FunctionVisitor {
|
|
||||||
type Value = Function;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a function")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<A>(self, mut map: A) -> Result<Function, A::Error>
|
|
||||||
where
|
|
||||||
A: MapAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut name = None;
|
|
||||||
let mut r#type = None;
|
|
||||||
let mut body = None;
|
|
||||||
|
|
||||||
while let Some(key) = map.next_key()? {
|
|
||||||
match key {
|
|
||||||
"name" => {
|
|
||||||
if name.is_some() {
|
|
||||||
return Err(de::Error::duplicate_field("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
name = Some(map.next_value()?);
|
|
||||||
}
|
|
||||||
"type" => {
|
|
||||||
if r#type.is_some() {
|
|
||||||
return Err(de::Error::duplicate_field("type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
r#type = Some(map.next_value()?);
|
|
||||||
}
|
|
||||||
"body" => {
|
|
||||||
if body.is_some() {
|
|
||||||
return Err(de::Error::duplicate_field("body"));
|
|
||||||
}
|
|
||||||
|
|
||||||
body = Some(map.next_value().map(Arc::new)?);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(de::Error::unknown_field(key, &["name", "type", "body"]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
|
|
||||||
let r#type = r#type.ok_or_else(|| de::Error::missing_field("type"))?;
|
|
||||||
let body = body.ok_or_else(|| de::Error::missing_field("body"))?;
|
|
||||||
|
|
||||||
Ok(Function { name, r#type, body })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_struct("Function", &["name", "type", "body"], FunctionVisitor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,8 @@ use crate::{
|
|||||||
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
|
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
|
||||||
StructDefinition, StructExpression,
|
StructDefinition, StructExpression,
|
||||||
},
|
},
|
||||||
constructor::FieldsConstructor,
|
|
||||||
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, DustError,
|
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, DustError,
|
||||||
Expression, Identifier, ParseError, Struct, StructType, Type, Value, ValueError,
|
Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Run the source code and return the result.
|
/// Run the source code and return the result.
|
||||||
@ -609,6 +608,9 @@ impl Vm {
|
|||||||
|
|
||||||
fn run_literal(&self, literal: LiteralExpression) -> Result<Evaluation, RuntimeError> {
|
fn run_literal(&self, literal: LiteralExpression) -> Result<Evaluation, RuntimeError> {
|
||||||
let value = match literal {
|
let value = match literal {
|
||||||
|
LiteralExpression::BuiltInFunction(built_in_function) => {
|
||||||
|
Value::Function(Function::BuiltIn(built_in_function))
|
||||||
|
}
|
||||||
LiteralExpression::String(string) => Value::String(string),
|
LiteralExpression::String(string) => Value::String(string),
|
||||||
LiteralExpression::Primitive(primitive_expression) => match primitive_expression {
|
LiteralExpression::Primitive(primitive_expression) => match primitive_expression {
|
||||||
PrimitiveValueExpression::Boolean(boolean) => Value::Boolean(boolean),
|
PrimitiveValueExpression::Boolean(boolean) => Value::Boolean(boolean),
|
||||||
@ -934,7 +936,6 @@ pub enum RuntimeError {
|
|||||||
// These should be prevented by running the analyzer before the VM
|
// These should be prevented by running the analyzer before the VM
|
||||||
BuiltInFunctionError {
|
BuiltInFunctionError {
|
||||||
error: BuiltInFunctionError,
|
error: BuiltInFunctionError,
|
||||||
position: Span,
|
|
||||||
},
|
},
|
||||||
EnumVariantNotFound {
|
EnumVariantNotFound {
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
@ -1006,8 +1007,9 @@ pub enum RuntimeError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
pub fn position(&self) -> Span {
|
pub fn position(&self) -> Option<Span> {
|
||||||
match self {
|
let position = match self {
|
||||||
|
Self::BuiltInFunctionError { .. } => return None,
|
||||||
Self::ParseError(parse_error) => parse_error.position(),
|
Self::ParseError(parse_error) => parse_error.position(),
|
||||||
Self::Expression { position, .. } => *position,
|
Self::Expression { position, .. } => *position,
|
||||||
Self::Statement { position, .. } => *position,
|
Self::Statement { position, .. } => *position,
|
||||||
@ -1016,7 +1018,6 @@ impl RuntimeError {
|
|||||||
right_position,
|
right_position,
|
||||||
..
|
..
|
||||||
} => (left_position.0, right_position.1),
|
} => (left_position.0, right_position.1),
|
||||||
Self::BuiltInFunctionError { position, .. } => *position,
|
|
||||||
Self::EnumVariantNotFound { position, .. } => *position,
|
Self::EnumVariantNotFound { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position } => *position,
|
Self::ExpectedBoolean { position } => *position,
|
||||||
Self::ExpectedConstructor { position, .. } => *position,
|
Self::ExpectedConstructor { position, .. } => *position,
|
||||||
@ -1042,7 +1043,9 @@ impl RuntimeError {
|
|||||||
Self::UndefinedProperty {
|
Self::UndefinedProperty {
|
||||||
property_position, ..
|
property_position, ..
|
||||||
} => *property_position,
|
} => *property_position,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Some(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user