From 97fdc89316d31779155af64e4d2af33c2a0497f7 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Wed, 28 May 2025 21:58:35 +0200 Subject: [PATCH] add argument reading for lox --- lox/bf.lox | 3 ++- src/cli.rs | 3 +++ src/interpreter.rs | 10 ++++++--- src/lib.rs | 4 ++-- src/main.rs | 3 ++- src/native_functions.rs | 45 ++++++++++++++++++++++++++++++++++++++++- 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/lox/bf.lox b/lox/bf.lox index dd99608..6398d59 100644 --- a/lox/bf.lox +++ b/lox/bf.lox @@ -96,5 +96,6 @@ class Interpreter { } } -var interpreter = Interpreter("bf/game_of_life.bf"); +var bfScript = args(0); +var interpreter = Interpreter(bfScript); interpreter.run(); diff --git a/src/cli.rs b/src/cli.rs index f766d41..8e328fe 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,6 +19,9 @@ pub struct RunConfig { /// Path to Lox source file. #[arg(short, long)] pub source: PathBuf, + /// Arguments to pass to the Lox script (must be after '--'). + #[arg(last = true)] + pub script_args: Vec, } /// Available commands for the Lox interpreter diff --git a/src/interpreter.rs b/src/interpreter.rs index 70b1670..198b4e0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -60,12 +60,18 @@ pub struct Interpreter { /// Default configuration for the interpreter, with builtin native functions. impl Default for Interpreter { fn default() -> Self { + Interpreter::new(Vec::new()) + } +} + +impl Interpreter { + pub fn new(cli_args: Vec) -> Self { let env: Rc> = Default::default(); { // add all native functions to global environment let mut env = env.borrow_mut(); - for (name, fun) in native_functions::all() { + for (name, fun) in native_functions::all(cli_args) { env.define(name, fun); } } @@ -76,9 +82,7 @@ impl Default for Interpreter { locals: HashMap::new(), } } -} -impl Interpreter { /// Execute a list of statements in sequence. /// Log errors but continue execution. pub fn run(&mut self, statements: Vec) -> Result<(), InterpreterError> { diff --git a/src/lib.rs b/src/lib.rs index d27e546..0c2d957 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,9 +53,9 @@ pub enum RoxError { } /// Read the source code in a file and scan it to tokens. -pub fn compile(source: &Path) -> Result<(), io::Error> { +pub fn compile(source: &Path, cli_args: Vec) -> Result<(), io::Error> { let input = fs::read_to_string(source)?; - let mut interpreter = Interpreter::default(); + let mut interpreter = Interpreter::new(cli_args); run(&input, &mut interpreter); diff --git a/src/main.rs b/src/main.rs index 32caf1a..1539ff8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,8 @@ fn main() { exit(1); } - if let Err(e) = rox::compile(&compile_config.source) { + if let Err(e) = rox::compile(&compile_config.source, compile_config.script_args.clone()) + { error!( "failed to compile {}: {}", &compile_config.source.to_string_lossy(), diff --git a/src/native_functions.rs b/src/native_functions.rs index b33ab72..5639664 100644 --- a/src/native_functions.rs +++ b/src/native_functions.rs @@ -224,8 +224,47 @@ impl Callable for PromptAscii { } } +struct CliArgs { + cli_args: Vec, +} +impl Callable for CliArgs { + fn name(&self) -> String { + "args".into() + } + + fn arity(&self) -> usize { + 1 + } + + fn call( + &self, + _interpreter: &mut Interpreter, + args: Vec, + ) -> Result { + if args.len() != self.arity() { + return Err(CallingError::ArgumentMismatch(self.arity(), args.len())); + } + + let idx = args + .first() + .ok_or(CallingError::CallFailed("arg not readable".into()))?; + if let Value::Number(idx) = idx { + let idx: usize = idx.0 as usize; + Ok(self + .cli_args + .get(idx) + .map(|x| Value::String(x.clone())) + .unwrap_or(Value::Nil)) + } else { + Err(CallingError::CallFailed( + "arg index must be a number".into(), + )) + } + } +} + /// Return all native functions available to the Lox interpreter -pub fn all() -> Vec<(String, Value)> { +pub fn all(cli_args: Vec) -> Vec<(String, Value)> { vec![ ( "asciiOut".into(), @@ -247,5 +286,9 @@ pub fn all() -> Vec<(String, Value)> { "promptAscii".into(), Value::Callable((Rc::new(PromptAscii {}), CallableType::Function)), ), + ( + "args".into(), + Value::Callable((Rc::new(CliArgs { cli_args }), CallableType::Function)), + ), ] }