Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
crazymerlyn committed Sep 30, 2024
1 parent 3154a17 commit 0860a20
Show file tree
Hide file tree
Showing 274 changed files with 6,552 additions and 94 deletions.
74 changes: 64 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ version = "0.1.0"
edition = "2021"

[dependencies]
error-chain = { version = "0.12.4", default-features = false }
anyhow = "1.0"
thiserror = "1.0"
lazy_static = "1.5.0"

[dev-dependencies]
glob = "0.3.1"
3 changes: 1 addition & 2 deletions src/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ impl Callable for Value {
"Wrong number of arguments: Expected {}, got {}",
arity,
args.len()
))
.into())
)))
} else {
func(args)
}
Expand Down
33 changes: 13 additions & 20 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
#![allow(unknown_lints)]

use crate::scanner::Token;
use thiserror::Error;

error_chain! {
errors {
ScanError(line: usize, t: String) {
description("Invalid syntax")
display("Error at line {}: {}", line, t)
}
ParseError(tok: Token, t: String) {
description("Invalid program")
display("Error at line {} at '{}': {}", tok.line, tok.lexeme, t)
}
EvaluateError(t: String) {
description("Invalid expression")
display("Error: {}", t)
}
}
foreign_links {
IO(::std::io::Error);
}
#[derive(Error, Debug)]
pub enum ErrorKind {
#[error("Error at line {0}: {1}")]
ScanError(usize, String),
#[error("Error at line {} at '{}': {t}", tok.line, tok.lexeme)]
ParseError { tok: Token, t: String },
#[error("Error: {0}")]
EvaluateError(String),
#[error("IO Error: {0}")]
IO(#[from] ::std::io::Error),
}

pub type Result<T> = std::result::Result<T, ErrorKind>;
61 changes: 15 additions & 46 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ use std::fmt;
use std::fs::File;
use std::path::Path;

use std::io::Result;
use std::io::{self, BufRead, Read, Write};

use std::process;
use anyhow::Result;

use std::collections::HashMap;

Expand All @@ -16,80 +15,50 @@ use crate::parser::Parser;
use crate::scanner::Scanner;

pub struct Interpreter {
had_error: bool,
env: Environment,
}

impl Interpreter {
pub fn new() -> Interpreter {
let mut env = Environment::new();
env.insert("clock", Value::BuiltinFunc("clock".to_string(), 0, clock));
Interpreter {
had_error: false,
env,
}
Interpreter { env }
}

pub fn run_prompt(&mut self) -> Result<()> {
pub fn run_prompt(&mut self) -> io::Result<()> {
let stdin = io::stdin();
print!("> ");
io::stdout().flush()?;
for line in stdin.lock().lines() {
self.run(&line.unwrap(), true);
self.had_error = false;
match self.run(&line.unwrap()) {
Ok(v) => println!("{v}"),
Err(e) => eprintln!("{e}"),
}
print!("> ");
io::stdout().flush()?;
}
println!();
Ok(())
}

pub fn run_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
pub fn run_path<P: AsRef<Path>>(&mut self, path: P) -> Result<Value> {
let mut s = String::new();
let mut file = File::open(path)?;
file.read_to_string(&mut s)?;
self.run(&s, false);
if self.had_error {
process::exit(65);
};
Ok(())
self.run(&s)
}

fn run(&mut self, code: &str, print_value: bool) {
pub fn run(&mut self, code: &str) -> Result<Value> {
let scanner = Scanner::new(code);
let stmts = match scanner
let stmts = scanner
.scan_tokens()
.and_then(|tokens| Parser::new(tokens).parse())
{
Ok(x) => x,
Err(e) => {
self.error(e.to_string());
return;
}
};
.and_then(|tokens| Parser::new(tokens).parse())?;

let mut last_val = Value::Nil;
for stmt in stmts {
if self.had_error {
break;
}
match stmt.interpret(&mut self.env) {
Ok(v) => {
if print_value && Value::Nil != v {
println!("{}", v);
}
}
Err(e) => self.error(format!("{}", e)),
}
last_val = stmt.interpret(&mut self.env)?;
}
}

fn error<S: AsRef<str>>(&mut self, message: S) {
self.report("", message.as_ref())
}

fn report<S: AsRef<str>>(&mut self, context: S, message: S) {
eprintln!("{}{}", context.as_ref(), message.as_ref());
self.had_error = true;
Ok(last_val)
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
#[macro_use] extern crate error_chain;
#[macro_use] extern crate lazy_static;

#[macro_use]
extern crate lazy_static;
use std::env;

mod interpreter;
use interpreter::Interpreter;

mod errors;

mod scanner;
mod parser;
#[cfg(test)]
mod tests;

mod ast;
mod builtins;
mod evaluable;
mod callable;
mod evaluable;
mod interpretable;
mod parser;
mod scanner;

fn main() {
if env::args().len() > 2 {
Expand Down
21 changes: 12 additions & 9 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,10 @@ impl Parser {
let value = self.assignment()?;
match expr {
Expr::Variable(id) => Ok(Expr::Assign(id, Box::new(value))),
x => Err(ErrorKind::ParseError(
equals,
format!("Invalid assignment target: {}", x),
)
.into()),
x => Err(ErrorKind::ParseError {
tok: equals,
t: format!("Invalid assignment target: {}", x),
}),
}
} else {
Ok(expr)
Expand Down Expand Up @@ -349,9 +348,10 @@ impl Parser {
self.consume(TokenType::RightParen, "Expect ')' after expression")?;
Ok(Expr::Grouping(Box::new(Grouping { expr })))
}
_ => Err(
ErrorKind::ParseError(self.peek().clone(), "Expect expression".to_string()).into(),
),
_ => Err(ErrorKind::ParseError {
tok: self.peek().clone(),
t: "Expect expression".to_string(),
}),
}
}

Expand Down Expand Up @@ -383,7 +383,10 @@ impl Parser {
self.advance();
Ok(result)
} else {
Err(ErrorKind::ParseError(self.peek().clone(), error.to_string()).into())
Err(ErrorKind::ParseError {
tok: self.peek().clone(),
t: error.to_string(),
})
}
}

Expand Down
1 change: 1 addition & 0 deletions src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ impl Scanner {
if self.peek() == Some('\n') {
self.line += 1;
}
self.advance();
}

if self.is_at_end() {
Expand Down
9 changes: 9 additions & 0 deletions src/tests/loxfiles/assignment/associativity.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var a = "a";
var b = "b";
var c = "c";

// Assignment is right-associative.
a = b = c;
print a; // expect: c
print b; // expect: c
print c; // expect: c
8 changes: 8 additions & 0 deletions src/tests/loxfiles/assignment/global.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
var a = "before";
print a; // expect: before

a = "after";
print a; // expect: after

print a = "arg"; // expect: arg
print a; // expect: arg
2 changes: 2 additions & 0 deletions src/tests/loxfiles/assignment/grouping.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var a = "a";
(a) = "value"; // Error at '=': Invalid assignment target.
3 changes: 3 additions & 0 deletions src/tests/loxfiles/assignment/infix_operator.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var a = "a";
var b = "b";
a + b = "value"; // Error at '=': Invalid assignment target.
10 changes: 10 additions & 0 deletions src/tests/loxfiles/assignment/local.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
var a = "before";
print a; // expect: before

a = "after";
print a; // expect: after

print a = "arg"; // expect: arg
print a; // expect: arg
}
2 changes: 2 additions & 0 deletions src/tests/loxfiles/assignment/prefix_operator.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var a = "a";
!a = "value"; // Error at '=': Invalid assignment target.
5 changes: 5 additions & 0 deletions src/tests/loxfiles/assignment/syntax.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Assignment on RHS of variable.
var a = "before";
var c = a = "var";
print a; // expect: var
print c; // expect: var
7 changes: 7 additions & 0 deletions src/tests/loxfiles/assignment/to_this.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Foo {
Foo() {
this = "value"; // Error at '=': Invalid assignment target.
}
}

Foo();
1 change: 1 addition & 0 deletions src/tests/loxfiles/assignment/undefined.lox
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unknown = "what"; // expect runtime error: Undefined variable 'unknown'.
Loading

0 comments on commit 0860a20

Please sign in to comment.