From da55725fb4d4377eb9fb56f43fdff8736ed96571 Mon Sep 17 00:00:00 2001
From: nulluser <diuyeung@gmail.com>
Date: Sun, 2 Jun 2024 00:06:19 +1000
Subject: [PATCH] impl parsing for user defined fn

---
 src/backend/interpreter.rs |  1 +
 src/backend/values.rs      | 16 ++++++++
 src/bin/main.rs            | 81 +++++++++++++++++++++++++++-----------
 src/frontend/ast.rs        |  1 +
 src/frontend/lexer.rs      |  2 +
 src/frontend/parser.rs     | 40 +++++++++++++++++++
 6 files changed, 118 insertions(+), 23 deletions(-)

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<String>,
+    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<String> = std::env::args().collect();
 
-    if args.len() == 2 && args[1] == "repl" {
-        repl();
-    } else {
-        println!("Usage: quiklang <command>");
-        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 <command>");
+    println!("Commands:");
+    println!("  repl - Start the QuikLang REPL");
+    println!("  <file> - 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<Expr>), // Name, is_mutable, expr
     ReturnStmt(Option<Expr>),
+    FunctionDeclaration(Vec<String>, String, Vec<Stmt>, bool), // Parameters, Name, Body, Is async?
     IfStmt(Expr, Vec<Stmt>, Vec<Stmt>),
 }
 
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<Expr> = self.parse_args();
+        let mut params: Vec<String> = 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<Stmt> = 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.