Toy with chumsky and ariadne
This commit is contained in:
parent
6b0bb0016f
commit
966983920e
@ -66,6 +66,18 @@ impl AbstractNode for FunctionCall {
|
||||
})?;
|
||||
}
|
||||
|
||||
for (type_parameter, expression) in parameter_types.iter().zip(self.arguments.iter()) {
|
||||
let actual = expression.node.expected_type(context)?;
|
||||
|
||||
type_parameter.node.check(&actual).map_err(|conflict| {
|
||||
ValidationError::TypeCheck {
|
||||
conflict,
|
||||
actual_position: expression.position,
|
||||
expected_position: type_parameter.position,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ValidationError::ExpectedFunction {
|
||||
|
@ -180,7 +180,7 @@ impl Display for Type {
|
||||
write!(f, ") : {}", return_type.node)
|
||||
}
|
||||
Type::Structure { name, .. } => write!(f, "{name}"),
|
||||
Type::Argument(_) => todo!(),
|
||||
Type::Argument(identifier) => write!(f, "{identifier}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,8 @@ impl AbstractNode for ValueNode {
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let ValueNode::ParsedFunction {
|
||||
@ -150,6 +152,8 @@ impl AbstractNode for ValueNode {
|
||||
actual_position: body.position,
|
||||
expected_position: return_type.position,
|
||||
})?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let ValueNode::Structure {
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum BuiltInFunction {
|
||||
ReadLine,
|
||||
Sleep,
|
||||
@ -23,9 +23,9 @@ pub enum BuiltInFunction {
|
||||
impl BuiltInFunction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
BuiltInFunction::ReadLine => todo!(),
|
||||
BuiltInFunction::Sleep => todo!(),
|
||||
BuiltInFunction::WriteLine => todo!(),
|
||||
BuiltInFunction::ReadLine => "__READ_LINE__",
|
||||
BuiltInFunction::Sleep => "__SLEEP__",
|
||||
BuiltInFunction::WriteLine => "__WRITE_LINE__",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,12 @@ use std::{
|
||||
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::{built_in_functions::BuiltInFunction, error::Error};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Token<'src> {
|
||||
Boolean(bool),
|
||||
BuiltInIdentifier(BuiltInIdentifier),
|
||||
BuiltInFunction(BuiltInFunction),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
String(&'src str),
|
||||
@ -24,7 +24,7 @@ impl<'src> Display for Token<'src> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Token::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
Token::BuiltInIdentifier(built_in_identifier) => write!(f, "{built_in_identifier}"),
|
||||
Token::BuiltInFunction(built_in_identifier) => write!(f, "{built_in_identifier}"),
|
||||
Token::Integer(integer) => write!(f, "{integer}"),
|
||||
Token::Float(float) => write!(f, "{float}"),
|
||||
Token::String(string) => write!(f, "{string}"),
|
||||
@ -36,23 +36,6 @@ impl<'src> Display for Token<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum BuiltInIdentifier {
|
||||
ReadLine,
|
||||
Sleep,
|
||||
WriteLine,
|
||||
}
|
||||
|
||||
impl Display for BuiltInIdentifier {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
BuiltInIdentifier::ReadLine => write!(f, "__READ_LINE__"),
|
||||
BuiltInIdentifier::Sleep => write!(f, "__SLEEP__"),
|
||||
BuiltInIdentifier::WriteLine => write!(f, "__WRITE_LINE__"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Keyword {
|
||||
Any,
|
||||
@ -315,11 +298,11 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
.map(Token::Keyword);
|
||||
|
||||
let built_in_identifier = choice((
|
||||
just("__READ_LINE__").to(BuiltInIdentifier::ReadLine),
|
||||
just("__SLEEP__").to(BuiltInIdentifier::Sleep),
|
||||
just("__WRITE_LINE__").to(BuiltInIdentifier::WriteLine),
|
||||
just(BuiltInFunction::ReadLine.name()).to(BuiltInFunction::ReadLine),
|
||||
just(BuiltInFunction::Sleep.name()).to(BuiltInFunction::Sleep),
|
||||
just(BuiltInFunction::WriteLine.name()).to(BuiltInFunction::WriteLine),
|
||||
))
|
||||
.map(Token::BuiltInIdentifier);
|
||||
.map(Token::BuiltInFunction);
|
||||
|
||||
choice((
|
||||
boolean,
|
||||
|
@ -6,7 +6,7 @@ pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod value;
|
||||
|
||||
use std::{ops::Range, rc::Rc};
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc, vec};
|
||||
|
||||
use abstract_tree::Type;
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||
@ -16,20 +16,88 @@ use lexer::lex;
|
||||
use parser::parse;
|
||||
pub use value::Value;
|
||||
|
||||
pub fn interpret(source_id: Rc<String>, source: &str) -> Result<Option<Value>, InterpreterError> {
|
||||
pub fn interpret<'src>(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
|
||||
let mut interpreter = Interpreter::new(Context::new());
|
||||
|
||||
interpreter.load_std()?;
|
||||
interpreter.run(source_id, source)
|
||||
interpreter.run(Rc::new(source_id.to_string()), Rc::from(source))
|
||||
}
|
||||
|
||||
pub fn interpret_without_std(
|
||||
source_id: Rc<String>,
|
||||
source_id: &str,
|
||||
source: &str,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let mut interpreter = Interpreter::new(Context::new());
|
||||
|
||||
interpreter.run(source_id, source)
|
||||
interpreter.run(Rc::new(source_id.to_string()), Rc::from(source))
|
||||
}
|
||||
|
||||
pub struct Interpreter {
|
||||
context: Context,
|
||||
sources: Rc<RefCell<Vec<(Rc<String>, Rc<str>)>>>,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new(context: Context) -> Self {
|
||||
Interpreter {
|
||||
context,
|
||||
sources: Rc::new(RefCell::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
&mut self,
|
||||
source_id: Rc<String>,
|
||||
source: Rc<str>,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let abstract_tree = parse(&tokens).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let value_option = abstract_tree
|
||||
.run(&self.context)
|
||||
.map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
|
||||
self.sources.borrow_mut().push((source_id, source.into()));
|
||||
|
||||
Ok(value_option)
|
||||
}
|
||||
|
||||
pub fn run_all<T: IntoIterator<Item = (Rc<String>, Rc<str>)>>(
|
||||
&mut self,
|
||||
sources: T,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let mut previous_value_option = None;
|
||||
|
||||
for (source_id, source) in sources {
|
||||
previous_value_option = self.run(source_id.clone(), source)?;
|
||||
}
|
||||
|
||||
Ok(previous_value_option)
|
||||
}
|
||||
|
||||
pub fn load_std(&mut self) -> Result<Option<Value>, InterpreterError> {
|
||||
self.run_all([
|
||||
(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
Rc::from(include_str!("../../std/io.ds")),
|
||||
),
|
||||
(
|
||||
Rc::new("std/thread.ds".to_string()),
|
||||
Rc::from(include_str!("../../std/thread.ds")),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn sources(&self) -> vec::IntoIter<(Rc<String>, Rc<str>)> {
|
||||
self.sources.borrow().clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -45,7 +113,7 @@ impl InterpreterError {
|
||||
}
|
||||
|
||||
impl InterpreterError {
|
||||
pub fn build_reports<'id>(self) -> Vec<Report<'id, (Rc<String>, Range<usize>)>> {
|
||||
pub fn build_reports<'a>(self) -> Vec<Report<'a, (Rc<String>, Range<usize>)>> {
|
||||
let mut reports = Vec::new();
|
||||
|
||||
for error in self.errors {
|
||||
@ -170,8 +238,6 @@ impl InterpreterError {
|
||||
} => {
|
||||
let TypeConflict { actual, expected } = conflict;
|
||||
|
||||
builder = builder.with_message("A type conflict was found.");
|
||||
|
||||
builder.add_labels([
|
||||
Label::new((
|
||||
self.source_id.clone(),
|
||||
@ -257,46 +323,3 @@ impl InterpreterError {
|
||||
reports
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interpreter {
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new(context: Context) -> Self {
|
||||
Interpreter { context }
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
&mut self,
|
||||
source_id: Rc<String>,
|
||||
source: &str,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let tokens = lex(source).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let abstract_tree = parse(&tokens).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let value_option = abstract_tree
|
||||
.run(&self.context)
|
||||
.map_err(|errors| InterpreterError { source_id, errors })?;
|
||||
|
||||
Ok(value_option)
|
||||
}
|
||||
|
||||
pub fn load_std(&mut self) -> Result<(), InterpreterError> {
|
||||
self.run(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
include_str!("../../std/io.ds"),
|
||||
)?;
|
||||
self.run(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
include_str!("../../std/thread.ds"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,8 @@ use chumsky::{input::SpannedInput, pratt::*, prelude::*};
|
||||
|
||||
use crate::{
|
||||
abstract_tree::*,
|
||||
built_in_functions::BuiltInFunction,
|
||||
error::Error,
|
||||
lexer::{BuiltInIdentifier, Control, Keyword, Operator, Token},
|
||||
lexer::{Control, Keyword, Operator, Token},
|
||||
};
|
||||
|
||||
pub type ParserInput<'src> =
|
||||
@ -256,11 +255,8 @@ pub fn parser<'src>() -> impl Parser<
|
||||
|
||||
let built_in_function = {
|
||||
select! {
|
||||
Token::BuiltInIdentifier(built_in_identifier) => {
|
||||
match built_in_identifier {BuiltInIdentifier::ReadLine=>BuiltInFunction::ReadLine,BuiltInIdentifier::WriteLine=>BuiltInFunction::WriteLine,
|
||||
BuiltInIdentifier::Sleep => BuiltInFunction::Sleep, }
|
||||
}
|
||||
}
|
||||
Token::BuiltInFunction(built_in_function) => built_in_function,
|
||||
}
|
||||
}
|
||||
.map_with(|built_in_function, state| {
|
||||
Expression::Value(ValueNode::BuiltInFunction(built_in_function))
|
||||
@ -582,7 +578,7 @@ pub fn parser<'src>() -> impl Parser<
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::lex;
|
||||
use crate::{built_in_functions::BuiltInFunction, lexer::lex};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -1,27 +1,22 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn logic() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1 == 1").unwrap(),
|
||||
interpret("test", "1 == 1").unwrap(),
|
||||
Some(Value::boolean(true))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "('42' == '42') && (42 != 0)").unwrap(),
|
||||
interpret("test", "('42' == '42') && (42 != 0)").unwrap(),
|
||||
Some(Value::boolean(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn math() {
|
||||
assert_eq!(interpret("test", "1 + 1").unwrap(), Some(Value::integer(2)));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1 + 1").unwrap(),
|
||||
Some(Value::integer(2))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "2 * (21 + 19 + 1 * 2) / 2").unwrap(),
|
||||
interpret("test", "2 * (21 + 19 + 1 * 2) / 2").unwrap(),
|
||||
Some(Value::integer(42))
|
||||
);
|
||||
}
|
||||
@ -29,7 +24,7 @@ fn math() {
|
||||
#[test]
|
||||
fn list_index() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foo = [1, 2, 3]; foo[2]").unwrap(),
|
||||
interpret("test", "foo = [1, 2, 3]; foo[2]").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
}
|
||||
@ -37,11 +32,11 @@ fn list_index() {
|
||||
#[test]
|
||||
fn map_index() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "{ x = 3 }.x").unwrap(),
|
||||
interpret("test", "{ x = 3 }.x").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foo = { x = 3 }; foo.x").unwrap(),
|
||||
interpret("test", "foo = { x = 3 }; foo.x").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::Identifier,
|
||||
error::{Error, ValidationError},
|
||||
*,
|
||||
};
|
||||
|
||||
use dust_lang::interpret;
|
||||
|
||||
#[test]
|
||||
fn function_call() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
foobar = (message : str) str { message }
|
||||
foobar('Hiya')
|
||||
@ -24,7 +24,7 @@ fn function_call() {
|
||||
fn call_empty_function() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
foobar = (message : str) none {}
|
||||
foobar('Hiya')
|
||||
@ -38,7 +38,7 @@ fn call_empty_function() {
|
||||
fn callback() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
foobar = (cb: fn() -> str) str {
|
||||
cb()
|
||||
@ -52,17 +52,14 @@ fn callback() {
|
||||
|
||||
#[test]
|
||||
fn built_in_function_call() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "io.write_line('Hiya')"),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(interpret("test", "io.write_line('Hiya')"), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_context_does_not_capture_values() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
x = 1
|
||||
|
||||
@ -79,7 +76,7 @@ fn function_context_does_not_capture_values() {
|
||||
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
x = 1
|
||||
foo = (x: int) int { x }
|
||||
@ -94,7 +91,7 @@ fn function_context_does_not_capture_values() {
|
||||
fn function_context_captures_functions() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
bar = () int { 2 }
|
||||
foo = () int { bar() }
|
||||
@ -109,7 +106,7 @@ fn function_context_captures_functions() {
|
||||
fn recursion() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
fib = (i: int) int {
|
||||
if i <= 1 {
|
||||
|
@ -1,12 +1,10 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn async_block() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
x = 41
|
||||
async {
|
||||
@ -24,7 +22,7 @@ fn async_block() {
|
||||
fn loops_and_breaks() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
i = 0
|
||||
loop {
|
||||
@ -41,7 +39,7 @@ fn loops_and_breaks() {
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
foobar = {
|
||||
while true {
|
||||
@ -60,7 +58,7 @@ fn loops_and_breaks() {
|
||||
#[test]
|
||||
fn r#if() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "if true { 'foobar' }"),
|
||||
interpret("test", "if true { 'foobar' }"),
|
||||
Ok(Some(Value::string("foobar".to_string())))
|
||||
)
|
||||
}
|
||||
@ -68,10 +66,7 @@ fn r#if() {
|
||||
#[test]
|
||||
fn if_else() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"if false { 'foo' } else { 'bar' }"
|
||||
),
|
||||
interpret("test", "if false { 'foo' } else { 'bar' }"),
|
||||
Ok(Some(Value::string("bar".to_string())))
|
||||
)
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{Identifier, Type},
|
||||
error::{Error, TypeConflict, ValidationError},
|
||||
*,
|
||||
interpret, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn simple_structure() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
struct Foo {
|
||||
bar : int,
|
||||
@ -36,7 +35,7 @@ fn simple_structure() {
|
||||
fn field_type_error() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
struct Foo {
|
||||
bar : int,
|
||||
@ -67,7 +66,7 @@ fn field_type_error() {
|
||||
fn nested_structure() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
struct Bar {
|
||||
baz : int
|
||||
@ -100,7 +99,7 @@ fn nested_structure() {
|
||||
fn undefined_struct() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"test",
|
||||
"
|
||||
Foo {
|
||||
bar = 42
|
||||
@ -110,7 +109,7 @@ fn undefined_struct() {
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: error::ValidationError::VariableNotFound(Identifier::new("Foo")),
|
||||
error: ValidationError::VariableNotFound(Identifier::new("Foo")),
|
||||
position: (17, 69).into()
|
||||
}]
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{Identifier, Type, WithPos},
|
||||
@ -8,37 +8,25 @@ use dust_lang::{
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
assert_eq!(interpret(Rc::new("test".to_string()), "x = 9"), Ok(None));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "x = 1 + 1"),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(interpret("test", "x = 9"), Ok(None));
|
||||
assert_eq!(interpret("test", "x = 1 + 1"), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1"),
|
||||
Ok(Some(Value::integer(1)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "123"),
|
||||
Ok(Some(Value::integer(123)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "-666"),
|
||||
Ok(Some(Value::integer(-666)))
|
||||
);
|
||||
assert_eq!(interpret("test", "1"), Ok(Some(Value::integer(1))));
|
||||
assert_eq!(interpret("test", "123"), Ok(Some(Value::integer(123))));
|
||||
assert_eq!(interpret("test", "-666"), Ok(Some(Value::integer(-666))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer_saturation() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "9223372036854775807 + 1"),
|
||||
interpret("test", "9223372036854775807 + 1"),
|
||||
Ok(Some(Value::integer(i64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "-9223372036854775808 - 1"),
|
||||
interpret("test", "-9223372036854775808 - 1"),
|
||||
Ok(Some(Value::integer(i64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -46,11 +34,11 @@ fn integer_saturation() {
|
||||
#[test]
|
||||
fn float() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1.7976931348623157e308"),
|
||||
interpret("test", "1.7976931348623157e308"),
|
||||
Ok(Some(Value::float(f64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "-1.7976931348623157e308"),
|
||||
interpret("test", "-1.7976931348623157e308"),
|
||||
Ok(Some(Value::float(f64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -58,11 +46,11 @@ fn float() {
|
||||
#[test]
|
||||
fn float_saturation() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1.7976931348623157e308 + 1"),
|
||||
interpret("test", "1.7976931348623157e308 + 1"),
|
||||
Ok(Some(Value::float(f64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "-1.7976931348623157e308 - 1"),
|
||||
interpret("test", "-1.7976931348623157e308 - 1"),
|
||||
Ok(Some(Value::float(f64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -70,27 +58,27 @@ fn float_saturation() {
|
||||
#[test]
|
||||
fn string() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "\"one\""),
|
||||
interpret("test", "\"one\""),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "'one'"),
|
||||
interpret("test", "'one'"),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "`one`"),
|
||||
interpret("test", "`one`"),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "`'one'`"),
|
||||
interpret("test", "`'one'`"),
|
||||
Ok(Some(Value::string("'one'".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "'`one`'"),
|
||||
interpret("test", "'`one`'"),
|
||||
Ok(Some(Value::string("`one`".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "\"'one'\""),
|
||||
interpret("test", "\"'one'\""),
|
||||
Ok(Some(Value::string("'one'".to_string())))
|
||||
);
|
||||
}
|
||||
@ -98,7 +86,7 @@ fn string() {
|
||||
#[test]
|
||||
fn list() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "[1, 2, 'foobar']"),
|
||||
interpret("test", "[1, 2, 'foobar']"),
|
||||
Ok(Some(Value::list(vec![
|
||||
Value::integer(1).with_position((1, 2)),
|
||||
Value::integer(2).with_position((4, 5)),
|
||||
@ -109,10 +97,7 @@ fn list() {
|
||||
|
||||
#[test]
|
||||
fn empty_list() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "[]"),
|
||||
Ok(Some(Value::list(Vec::new())))
|
||||
);
|
||||
assert_eq!(interpret("test", "[]"), Ok(Some(Value::list(Vec::new()))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -123,7 +108,7 @@ fn map() {
|
||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "{ x = 1, foo = 'bar' }"),
|
||||
interpret("test", "{ x = 1, foo = 'bar' }"),
|
||||
Ok(Some(Value::map(map)))
|
||||
);
|
||||
}
|
||||
@ -131,7 +116,7 @@ fn map() {
|
||||
#[test]
|
||||
fn empty_map() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "{}"),
|
||||
interpret("test", "{}"),
|
||||
Ok(Some(Value::map(BTreeMap::new())))
|
||||
);
|
||||
}
|
||||
@ -144,10 +129,7 @@ fn map_types() {
|
||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"{ x : int = 1, foo : str = 'bar' }"
|
||||
),
|
||||
interpret("test", "{ x : int = 1, foo : str = 'bar' }"),
|
||||
Ok(Some(Value::map(map)))
|
||||
);
|
||||
}
|
||||
@ -155,7 +137,7 @@ fn map_types() {
|
||||
#[test]
|
||||
fn map_type_errors() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "{ foo : bool = 'bar' }")
|
||||
interpret("test", "{ foo : bool = 'bar' }")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
@ -174,8 +156,5 @@ fn map_type_errors() {
|
||||
|
||||
#[test]
|
||||
fn range() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "0..100"),
|
||||
Ok(Some(Value::range(0..100)))
|
||||
);
|
||||
assert_eq!(interpret("test", "0..100"), Ok(Some(Value::range(0..100))));
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{Block, Expression, Identifier, Statement, Type, WithPos},
|
||||
error::{Error, TypeConflict, ValidationError},
|
||||
@ -9,7 +7,7 @@ use dust_lang::{
|
||||
#[test]
|
||||
fn set_and_get_variable() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foobar = true; foobar"),
|
||||
interpret("test", "foobar = true; foobar"),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
@ -17,7 +15,7 @@ fn set_and_get_variable() {
|
||||
#[test]
|
||||
fn set_variable_with_type() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foobar: bool = true; foobar"),
|
||||
interpret("test", "foobar: bool = true; foobar"),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
@ -25,7 +23,7 @@ fn set_variable_with_type() {
|
||||
#[test]
|
||||
fn set_variable_with_type_error() {
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foobar: str = true")
|
||||
interpret("test", "foobar: str = true")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
@ -45,10 +43,7 @@ fn set_variable_with_type_error() {
|
||||
#[test]
|
||||
fn function_variable() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"foobar = (x: int) int { x }; foobar"
|
||||
),
|
||||
interpret("test", "foobar = (x: int) int { x }; foobar"),
|
||||
Ok(Some(Value::function(
|
||||
Vec::with_capacity(0),
|
||||
vec![(Identifier::new("x"), Type::Integer.with_position((13, 16)))],
|
||||
|
@ -80,7 +80,8 @@ pub fn run_shell(context: Context) -> Result<(), io::Error> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let run_result = interpreter.run(Rc::new("input".to_string()), &buffer);
|
||||
let run_result =
|
||||
interpreter.run(Rc::new("input".to_string()), Rc::from(buffer.as_str()));
|
||||
|
||||
match run_result {
|
||||
Ok(Some(value)) => {
|
||||
@ -88,12 +89,11 @@ pub fn run_shell(context: Context) -> Result<(), io::Error> {
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
let source_id = Rc::new("input".to_string());
|
||||
let reports = error.build_reports();
|
||||
|
||||
for report in reports {
|
||||
report
|
||||
.write_for_stdout(sources([(source_id.clone(), &buffer)]), stderr())
|
||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use dust_lang::{context::Context, interpret, interpret_without_std};
|
||||
use dust_lang::{context::Context, Interpreter};
|
||||
|
||||
/// Command-line arguments to be parsed.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -42,10 +42,16 @@ fn main() {
|
||||
|
||||
let args = Args::parse();
|
||||
let context = Context::new();
|
||||
let (source, source_id) = if let Some(path) = args.path {
|
||||
(read_to_string(&path).unwrap(), Rc::new(path))
|
||||
let mut interpreter = Interpreter::new(context.clone());
|
||||
|
||||
interpreter.load_std().unwrap();
|
||||
|
||||
let (source_id, source) = if let Some(path) = args.path {
|
||||
let source = read_to_string(&path).unwrap();
|
||||
|
||||
(Rc::new(path.to_string()), source)
|
||||
} else if let Some(command) = args.command {
|
||||
(command, Rc::new("input".to_string()))
|
||||
(Rc::new("input".to_string()), command)
|
||||
} else {
|
||||
match run_shell(context) {
|
||||
Ok(_) => {}
|
||||
@ -55,13 +61,9 @@ fn main() {
|
||||
return;
|
||||
};
|
||||
|
||||
let eval_result = if args.no_std {
|
||||
interpret_without_std(source_id.clone(), &source)
|
||||
} else {
|
||||
interpret(source_id.clone(), &source)
|
||||
};
|
||||
let run_result = interpreter.run(source_id, Rc::from(source));
|
||||
|
||||
match eval_result {
|
||||
match run_result {
|
||||
Ok(value) => {
|
||||
if let Some(value) = value {
|
||||
println!("{value}")
|
||||
@ -70,7 +72,7 @@ fn main() {
|
||||
Err(error) => {
|
||||
for report in error.build_reports() {
|
||||
report
|
||||
.write_for_stdout(sources([(source_id.clone(), source.as_str())]), stderr())
|
||||
.write_for_stdout(sources(interpreter.sources()), stderr())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user