Skip to content

Commit

Permalink
Self-hosted test suite
Browse files Browse the repository at this point in the history
This starts moving some types of tests to a muse-executed test suite.

This suite executor has benefits over the rsn one because the rsn one
can't track the actual line numbers to report errors when working on the
test suite or changes that are breaking tests. The new runner is able to
accurately map the line numbers, allowing Ctrl+Click in VS Code to just
work. This goes as far as reporting stack traces of exceptions.

The runner needs some more features to be able to support all of what
the rsn runner currently does, but dogfooding the language with the test
suite is a clear win.
  • Loading branch information
ecton committed Mar 3, 2024
1 parent e5e3245 commit 9d4751d
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 222 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ unsafe_code = "forbid"
name = "harness"
harness = false

[[test]]
name = "hosted"
harness = false

# [profile.release]
# debug = true
# lto = true
52 changes: 48 additions & 4 deletions src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::VecDeque;
use std::fmt::{Debug, Display};
use std::ops::{BitOr, BitOrAssign, Range};
use std::sync::Arc;
Expand All @@ -19,7 +20,7 @@ use crate::vm::bitcode::{
};
use crate::vm::{Code, Register, Stack};

#[derive(Default, Debug)]
#[derive(Debug)]
pub struct Compiler {
function_name: Option<Symbol>,
parsed: Vec<Result<Ranged<Expression>, Ranged<syntax::Error>>>,
Expand Down Expand Up @@ -235,15 +236,58 @@ impl Compiler {
}
}

impl Default for Compiler {
fn default() -> Self {
Self {
function_name: None,
parsed: Vec::new(),
errors: Vec::new(),
code: BitcodeBlock::default(),
declarations: Map::new(),
scopes: Vec::new(),
macros: Map::new(),
}
.with_macro("$assert", |mut tokens: VecDeque<Ranged<Token>>| {
let start_range = tokens.front().map(Ranged::range).unwrap_or_default();
let end_range = tokens.back().map(Ranged::range).unwrap_or_default();
tokens.push_back(Ranged::new(
end_range,
Token::Identifier(Symbol::else_symbol().clone()),
));
tokens.push_back(Ranged::new(
end_range,
Token::Identifier(Symbol::throw_symbol().clone()),
));
tokens.push_back(Ranged::new(
end_range,
Token::Symbol(Symbol::from("assertion_failed")),
));
// tokens.push_back(Ranged::default_for(Token::Close(Paired::Paren)));
// tokens.push_front(Ranged::default_for(Token::Open(Paired::Paren)));
tokens.push_front(Ranged::new(start_range, Token::Char('!')));
tokens.push_front(Ranged::new(start_range, Token::Char('=')));
tokens.push_front(Ranged::new(
start_range,
Token::Identifier(Symbol::false_symbol().clone()),
));
tokens.push_front(Ranged::new(
start_range,
Token::Identifier(Symbol::let_symbol().clone()),
));
tokens
})
}
}

pub trait MacroFn: Send + Sync {
fn transform(&mut self, tokens: Vec<Ranged<Token>>) -> Vec<Ranged<Token>>;
fn transform(&mut self, tokens: VecDeque<Ranged<Token>>) -> VecDeque<Ranged<Token>>;
}

impl<F> MacroFn for F
where
F: FnMut(Vec<Ranged<Token>>) -> Vec<Ranged<Token>> + Send + Sync,
F: FnMut(VecDeque<Ranged<Token>>) -> VecDeque<Ranged<Token>> + Send + Sync,
{
fn transform(&mut self, tokens: Vec<Ranged<Token>>) -> Vec<Ranged<Token>> {
fn transform(&mut self, tokens: VecDeque<Ranged<Token>>) -> VecDeque<Ranged<Token>> {
self(tokens)
}
}
Expand Down
21 changes: 11 additions & 10 deletions src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ impl<T> Ranged<T> {
Self(value, range.into())
}

pub fn default_for(value: T) -> Self {
Self::new(SourceRange::default(), value)
}

pub fn bounded(
source_id: SourceId,
range: impl RangeBounds<usize>,
Expand Down Expand Up @@ -376,7 +380,7 @@ pub enum AssignTarget {
#[derive(Debug, Clone, PartialEq)]
pub struct MacroInvocation {
pub name: Symbol,
pub tokens: Vec<Ranged<Token>>,
pub tokens: VecDeque<Ranged<Token>>,
}

#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -451,7 +455,7 @@ pub struct Variable {
pub r#else: Option<Ranged<Expression>>,
}

pub fn parse_tokens(source: Vec<Ranged<Token>>) -> Result<Ranged<Expression>, Ranged<Error>> {
pub fn parse_tokens(source: VecDeque<Ranged<Token>>) -> Result<Ranged<Expression>, Ranged<Error>> {
parse_from_reader(TokenReader::from(source))
}

Expand Down Expand Up @@ -581,8 +585,8 @@ impl<'a> TokenReader<'a> {
}
}

impl From<Vec<Ranged<Token>>> for TokenReader<'_> {
fn from(tokens: Vec<Ranged<Token>>) -> Self {
impl From<VecDeque<Ranged<Token>>> for TokenReader<'_> {
fn from(tokens: VecDeque<Ranged<Token>>) -> Self {
let mut iter = tokens.iter().map(|t| t.1);
let range = iter
.next()
Expand All @@ -602,10 +606,7 @@ impl From<Vec<Ranged<Token>>> for TokenReader<'_> {
})
.unwrap_or_default();
Self {
tokens: TokenStream::List {
tokens: VecDeque::from(tokens),
range,
},
tokens: TokenStream::List { tokens, range },
peeked: VecDeque::new(),
last_index: 0,
}
Expand Down Expand Up @@ -1099,7 +1100,7 @@ impl PrefixParselet for Term {
token.1.start..,
Expression::Macro(Box::new(MacroInvocation {
name: sym,
tokens: contents,
tokens: VecDeque::from(contents),
})),
))
}
Expand All @@ -1113,7 +1114,7 @@ impl PrefixParselet for Term {
// TODO this should use a var-arg join operation to avoid extra
// allocations
for part in format_string.parts {
let mut reader = TokenReader::from(part.expression);
let mut reader = TokenReader::from(VecDeque::from(part.expression));
let right = config.parse_expression(&mut reader)?;

// Ensure the expression was fully consumed
Expand Down
8 changes: 5 additions & 3 deletions src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::VecDeque;

use crate::compiler::Compiler;
use crate::exception::Exception;
use crate::symbol::Symbol;
Expand Down Expand Up @@ -104,7 +106,7 @@ fn invoke() {
#[test]
fn macros() {
let code = Compiler::default()
.with_macro("$test", |mut tokens: Vec<Ranged<Token>>| {
.with_macro("$test", |mut tokens: VecDeque<Ranged<Token>>| {
assert_eq!(tokens[0].0, Token::Open(Paired::Paren));
tokens.insert(2, Ranged::new(SourceRange::default(), Token::Char('+')));
assert_eq!(tokens[4].0, Token::Close(Paired::Paren));
Expand All @@ -127,13 +129,13 @@ fn macros() {
#[test]
fn recursive_macros() {
let code = Compiler::default()
.with_macro("$inner", |mut tokens: Vec<Ranged<Token>>| {
.with_macro("$inner", |mut tokens: VecDeque<Ranged<Token>>| {
assert_eq!(tokens[0].0, Token::Open(Paired::Paren));
tokens.insert(2, Ranged::new(SourceRange::default(), Token::Char('+')));
assert_eq!(tokens[4].0, Token::Close(Paired::Paren));
dbg!(tokens)
})
.with_macro("$test", |mut tokens: Vec<Ranged<Token>>| {
.with_macro("$test", |mut tokens: VecDeque<Ranged<Token>>| {
tokens.insert(
0,
Ranged::new(SourceRange::default(), Token::Sigil(Symbol::from("$inner"))),
Expand Down
22 changes: 22 additions & 0 deletions tests/cases/assignment.muse
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
$case(
var: r#"
var a = 1;
a = 2;
$assert(a == 2);
"#
);
$case(
map_obj: r#"
let a = {,};
a.c = 2 + (a.b = 1);
$assert(a.b + a.c == 4);
"#
);
$case(
let_match: r#"
let (a, b) = (1, 2);
$assert(a + b == 3);
"#
);
25 changes: 0 additions & 25 deletions tests/cases/assignment.rsn

This file was deleted.

12 changes: 12 additions & 0 deletions tests/cases/blocks.muse
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$case(
block: r#"$assert({ 1; 2 } == 2)"#
);
$case(
labeled_block: r#"
let result = @label: {
break @label 42;
};
$assert(result == 42);
"#
)
9 changes: 0 additions & 9 deletions tests/cases/blocks.rsn

This file was deleted.

19 changes: 19 additions & 0 deletions tests/cases/comparisons.muse
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
$case(
chain_range: "let true = 1 < 2 < 3",
);

$case(
chain_range_short_circuit: r#"1 < 0 < nonsense"#
);

$case(
chain_range_long: r#"$assert(1 < 3 > 2 < 4 > 1 < 10 > 2)"#,
);

$case(
int_float: r#"$assert(1 == 1.0)"#,
);
$case(int_bool: r#"$assert(1 == true)"#);
$case(float_bool: r#"$assert(1.0 == true)"#);
29 changes: 0 additions & 29 deletions tests/cases/comparisons.rsn

This file was deleted.

Loading

0 comments on commit 9d4751d

Please sign in to comment.