diff --git a/src/backend/interpreter.rs b/src/backend/interpreter.rs index e8f8c60..c25a22e 100644 --- a/src/backend/interpreter.rs +++ b/src/backend/interpreter.rs @@ -15,6 +15,7 @@ pub fn evaluate(stmt: Stmt, env: &mut Environment) -> Val { } Stmt::ReturnStmt(_) => unimplemented!(), Stmt::IfStmt(_, _, _) => unimplemented!(), + Stmt::FunctionDeclaration(parameters, name, body, is_async) => todo!(), // Handle other statement types... } } diff --git a/src/backend/values.rs b/src/backend/values.rs index 66c151d..7be9847 100644 --- a/src/backend/values.rs +++ b/src/backend/values.rs @@ -10,6 +10,7 @@ pub enum ValueType { Bool, Object, NativeFunction, + Function, } pub trait RuntimeVal: std::fmt::Debug { @@ -92,6 +93,19 @@ impl RuntimeVal for NativeFunctionVal { } } +#[derive(Debug, PartialEq, Clone)] +pub struct FunctionVal { + name: String, + parameters: Vec, + declaration_env: &Environment, +} + +impl RuntimeVal for FunctionVal { + fn get_type(&self) -> ValueType { + ValueType::Function + } +} + // This enum encapsulates any RuntimeVal type to handle them generically. #[derive(Debug, PartialEq, Clone)] pub enum Val { @@ -101,6 +115,7 @@ pub enum Val { Bool(BoolVal), Object(ObjectVal), NativeFunction(NativeFunctionVal), + Function(FunctionVal), } impl RuntimeVal for Val { @@ -112,6 +127,7 @@ impl RuntimeVal for Val { Val::Bool(_) => ValueType::Bool, Val::Object(_) => ValueType::Object, Val::NativeFunction(_) => ValueType::NativeFunction, + Val::Function(_) => ValueType::Function, } } } diff --git a/src/bin/main.rs b/src/bin/main.rs index 2df14a1..7f42013 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,5 +1,6 @@ use std::{ - io::{self, Write}, + fs::File, + io::{self, Read, Write}, process, }; @@ -9,16 +10,63 @@ use quiklang::{ }; fn main() { - // Simple implementation for command system. Later, I can port the command system from my other projects... let args: Vec = std::env::args().collect(); - if args.len() == 2 && args[1] == "repl" { - repl(); - } else { - println!("Usage: quiklang "); - println!("Commands:"); - println!(" repl - Start the QuikLang REPL"); + match args.as_slice() { + // If no arguments are passed, print usage + [_] => print_usage(), + + // If "repl" is passed, start the REPL + [_, cmd] if cmd == "repl" => repl(), + + // If a file path is passed, execute the file + [_, file_path] => run_file(file_path), + + // Print usage instructions if the input is invalid + _ => print_usage(), + } +} + +fn print_usage() { + println!("Usage: quiklang "); + println!("Commands:"); + println!(" repl - Start the QuikLang REPL"); + println!(" - Execute the specified QuikLang script file"); +} + +fn run(input: String, env: &mut Environment) { + let mut parser = parser::Parser::new(); + match parser.produce_ast(input) { + Ok(program) => { + // println!("{:#?}", program); + + for stmt in program.statements { + let _ = evaluate(stmt, env); + } + } + Err(e) => { + println!("Error: {:?}", e); + } + } +} + +fn run_file(file_path: &str) { + let mut file = match File::open(file_path) { + Ok(file) => file, + Err(e) => { + println!("Error opening file {}: {}", file_path, e); + process::exit(1); + } + }; + + let mut content = String::new(); + if let Err(e) = file.read_to_string(&mut content) { + println!("Error reading file {}: {}", file_path, e); + process::exit(1); } + + let mut env = Environment::new(); + run(content, &mut env); } fn repl() { @@ -27,7 +75,6 @@ fn repl() { let mut env = Environment::new(); loop { - let mut parser = parser::Parser::new(); print!("quiklang> "); io::stdout().flush().expect("Failed to flush stdout"); @@ -39,20 +86,8 @@ fn repl() { // Exit if user enters "exit" or "quit" if input.trim() == "exit" || input.trim() == "quit" { println!("Exiting QuikLang REPL."); - process::exit(1); - } - - match parser.produce_ast(input) { - Ok(program) => { - // println!("{:#?}", program); - - for stmt in program.statements { - let _ = evaluate(stmt, &mut env); - } - } - Err(e) => { - println!("Error: {:?}", e); - } + process::exit(0); // Exit normally } + run(input, &mut env); } } diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs index 90169e4..b87cef7 100644 --- a/src/frontend/ast.rs +++ b/src/frontend/ast.rs @@ -43,6 +43,7 @@ pub enum Stmt { ExprStmt(Expr), DeclareStmt(String, bool, Option), // Name, is_mutable, expr ReturnStmt(Option), + FunctionDeclaration(Vec, String, Vec, bool), // Parameters, Name, Body, Is async? IfStmt(Expr, Vec, Vec), } diff --git a/src/frontend/lexer.rs b/src/frontend/lexer.rs index 078fae5..8903159 100644 --- a/src/frontend/lexer.rs +++ b/src/frontend/lexer.rs @@ -55,6 +55,7 @@ pub enum Keyword { Ok, // Ok Exit, // exit Break, // break + Async, // async } impl FromStr for Keyword { @@ -74,6 +75,7 @@ impl FromStr for Keyword { "Ok" => Ok(Keyword::Ok), "exit" => Ok(Keyword::Exit), "break" => Ok(Keyword::Break), + "async" => Ok(Keyword::Async), _ => Err(format!("{} is not a keyword.", s)), } } diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 747327d..d67c25e 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -1,5 +1,6 @@ // Parser +use core::panic; use std::process; use crate::frontend::ast::{BinaryOp, Program, Property}; @@ -50,6 +51,14 @@ impl Parser { match self.at() { Token::Keyword(Keyword::Let) => self.parse_var_declaration(false), Token::Keyword(Keyword::Const) => self.parse_var_declaration(true), + Token::Keyword(Keyword::Fn) => self.parse_fn_declaration(false), + Token::Keyword(Keyword::Async) => { + self.eat(); + match self.at() { + Token::Keyword(Keyword::Fn) => self.parse_fn_declaration(true), + _ => panic!("Async: `fn` missing!"), + } + } // Token::Identifier(_) => todo!(), // Token::IntegerLiteral(_) => todo!(), // Token::StringLiteral(_) => todo!(), @@ -60,6 +69,37 @@ impl Parser { } } + fn parse_fn_declaration(&mut self, is_async: bool) -> Stmt { + self.eat(); + let name = match self.eat() { + Token::Identifier(name) => name, + _ => panic!("No identifier for fn!"), + }; + let args: Vec = self.parse_args(); + let mut params: Vec = Vec::new(); + for arg in args { + match arg { + Expr::Identifier(name) => params.push(name), + _ => panic!( + "Inside function declaration expected parameters to be one of type String." + ), + } + } + self.expect( + Token::Symbol(Symbol::LeftBrace), + "Expected function body following declaration.", + ); + let mut body: Vec = Vec::new(); + while self.not_eof() && *self.at() != Token::Symbol(Symbol::RightBrace) { + body.push(self.parse_stmt()); + } + self.expect( + Token::Symbol(Symbol::RightBrace), + "Closing brace expected inside function declaration.", + ); + Stmt::FunctionDeclaration(params, name, body, is_async) + } + // `let (mut) ident(: type) = expr` fn parse_var_declaration(&mut self, is_const: bool) -> Stmt { let _ = self.eat(); // remove unneeded let/const token.