Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 85 additions & 80 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 12 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
[package]
name = "loxide"
[workspace]
resolver = "3"
members = ["crates/lexer", "./", "crates/parser", "crates/loxide"]


[workspace.package]
version = "0.1.0"
authors = ["Rob Hand <146272+sinon@users.noreply.github.com>"]
edition = "2024"
Expand All @@ -9,15 +13,17 @@ keywords = ["rust", "lox", "interpreter"]
homepage = "https://github.com/sinon/loxide"
repository = "https://github.com/sinon/loxide"

[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
[workspace.dependencies]
miette = { version = "7.4.0", features = ["fancy"] }

[dev-dependencies]
rstest = "0.23.0"
assert_cmd = "2.0.14"
insta = { version = "1.41.1", features = ["yaml"] }

# [dependencies]
# clap = { version = "4.5.23", features = ["derive"] }
# miette = { version = "7.4.0", features = ["fancy"] }


[profile.dev.package]
insta.opt-level = 3
similar.opt-level = 3
12 changes: 12 additions & 0 deletions crates/lexer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lexer"
version = "0.1.0"
edition = "2024"

[dependencies]
miette = { workspace = true }


[dev-dependencies]
rstest = "0.23.0"
insta = { version = "1.41.1", features = ["yaml"] }
19 changes: 3 additions & 16 deletions src/lexer.rs → crates/lexer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! Lexer
//!
//! Responsible for transforming a given input str into a Iterator of `Result<Token>`
#![allow(clippy::too_many_lines)]

use std::{fmt, process::ExitCode};
use std::fmt;

use miette::{Error, LabeledSpan, Result, miette};

Expand All @@ -12,7 +13,7 @@ pub struct Token<'de> {
/// The `TokenType` of `Token`
pub token_type: TokenType,
/// The text reference from the source code
pub origin: &'de str,
pub origin: &'de str, // TODO: Replace this with Span to avoid the lifetime
/// The line number where the token was parsed from
pub line: usize,
}
Expand Down Expand Up @@ -140,20 +141,6 @@ impl<'de> Lexer<'de> {
at_eof: false,
}
}
/// Start lexing on `Lexer` used by `tokenize` command.
pub fn tokenize_lex(&mut self) -> ExitCode {
let mut exit_code = 0;
for t in self {
match t {
Ok(token) => println!("{token}"),
Err(e) => {
eprintln!("{e}");
exit_code = 65;
}
}
}
ExitCode::from(exit_code)
}
fn match_reserved_word(c_str: &str) -> TokenType {
match c_str {
"and" => TokenType::And,
Expand Down
2 changes: 1 addition & 1 deletion tests/lexer_test.rs → crates/lexer/tests/lexer_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use insta::assert_yaml_snapshot;
use loxide::lexer::{Lexer, Token};
use lexer::{Lexer, Token};
use miette::Error;
use std::fmt::Write;

Expand Down
21 changes: 21 additions & 0 deletions crates/loxide/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "loxide"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
keywords.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
lexer = { path = "../lexer" }
parser = { path = "../parser" }
miette = { workspace = true }

[dev-dependencies]
rstest = "0.23.0"
assert_cmd = "2.0.14"
insta = { version = "1.41.1", features = ["yaml"] }
File renamed without changes.
51 changes: 27 additions & 24 deletions src/interpreter.rs → crates/loxide/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@

use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};

use crate::{
builtins,
lexer::{Token, TokenType},
parser::{Expr, LiteralAtom, Parser, Stmt},
value::EvaluatedValue,
};
use crate::{builtins, value::EvaluatedValue};
use lexer::{Token, TokenType};
use parser::{Expr, LiteralAtom, Parser, Stmt};

/// `NativeFunction` is used to represent builtin native functions
#[derive(Clone)]
Expand All @@ -30,7 +27,7 @@ impl fmt::Debug for NativeFunction {
}

impl NativeFunction {
fn arity(&self, _interpreter: &Interpreter) -> u8 {
const fn arity(&self, _interpreter: &Interpreter) -> u8 {
self.arity
}
fn call(
Expand All @@ -54,10 +51,11 @@ pub struct LoxFunction<'de> {
}

impl<'de> LoxFunction<'de> {
#[allow(dead_code)]
fn arity(&self) -> u8 {
u8::try_from(self.parameters.len()).expect("arity < 255 is enforced by parser")
}

#[allow(dead_code)]
fn call(
&self,
interpreter: &mut Interpreter<'de>,
Expand All @@ -67,7 +65,7 @@ impl<'de> LoxFunction<'de> {
.parameters
.iter()
.zip(args.iter())
.map(|(param, arg)| (param.origin.clone(), (Some(arg.clone()),)))
.map(|(param, arg)| (param.origin, (Some(arg.clone()),)))
.collect();
let saved_env = interpreter.environment.clone();
let block = Stmt::Block(self.body.clone());
Expand Down Expand Up @@ -128,17 +126,16 @@ impl<'de> Environment<'de> {
self.data.insert(key, value.clone());
Ok(())
}
None => match &self.enclosing {
Some(parent) => {
None => {
if let Some(parent) = &self.enclosing {
let mut p = parent.borrow_mut();
p.assign(key, value)?;
Ok(())
}
None => {
} else {
self.data.insert(key, value.clone());
Ok(())
}
},
}
}
}
fn var_assign(&mut self, key: &'de str, value: &EvaluatedValue) {
Expand Down Expand Up @@ -177,10 +174,10 @@ impl<'de> Interpreter<'de> {
}

pub(crate) fn get_lox_fn(&self, id: u64) -> &LoxFunction {
match self.lox_functions.get(&id) {
Some(func) => func,
None => panic!("# TODO Improve error handling for `get_lox_fn`",),
}
self.lox_functions.get(&id).map_or_else(
|| panic!("# TODO Improve error handling for `get_lox_fn`",),
|func| func,
)
}
const fn alloc_id(&mut self) -> u64 {
self.counter += 1;
Expand Down Expand Up @@ -268,10 +265,10 @@ fn evaluate_statement<'de>(
};
interpreter.lox_functions.insert(func_id, lox_fun);
interpreter.environment.assign(
&name.origin,
name.origin,
&EvaluatedValue::LoxFunction {
name: name.to_string(),
func_id: func_id,
func_id,
},
)?;
// interpreter.globals.data.insert(&name.to_string(), lox_fun);
Expand Down Expand Up @@ -472,12 +469,18 @@ fn evaluate_expression<'de>(
}

match &callee_fn {
EvaluatedValue::NativeFunction(f) => f.call(interpreter, &args),
EvaluatedValue::LoxFunction { func_id, .. } => {
let f = interpreter.get_lox_fn(*func_id);
// todo!("Fix lifetime issue with f.call")
EvaluatedValue::NativeFunction(f) => {
if f.arity(interpreter) as usize != args.len() {
eprintln!("Expected {} arguments but got {}.", f.arity, args.len());
return Err("Incorrect arity".to_string());
}
f.call(interpreter, &args)
}
EvaluatedValue::LoxFunction { func_id, .. } => {
let _ = interpreter.get_lox_fn(*func_id);
todo!("Fix lifetime issue with f.call")
// f.call(interpreter, &args)
}
_ => Err("not callable".to_string()),
}
}
Expand Down
4 changes: 0 additions & 4 deletions src/lib.rs → crates/loxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,5 @@
#![allow(clippy::too_many_lines)]
#![warn(missing_docs)]
mod builtins;
pub mod eval;
pub mod eval_parser;
pub mod interpreter;
pub mod lexer;
mod parser;
mod value;
41 changes: 41 additions & 0 deletions crates/loxide/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use clap::Subcommand;
use loxide::interpreter::Interpreter;
use miette::{IntoDiagnostic, Result, WrapErr};
use std::fs;
use std::path::PathBuf;
use std::process::ExitCode;

use clap::Parser as ClapParser;

#[derive(ClapParser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[command(subcommand)]
commands: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Run { filename: PathBuf },
}

fn main() -> Result<ExitCode> {
let args = Args::parse();
match args.commands {
Commands::Run { filename } => {
let input = fs::read_to_string(filename)
.into_diagnostic()
.wrap_err_with(|| "reading file".to_string())?;
let mut exit_code = 0;
for res in Interpreter::new(&input) {
match res {
Ok(()) => {}
Err(err_code) => {
exit_code = err_code;
break;
}
}
}
Ok(ExitCode::from(exit_code))
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading