Skip to content

Commit

Permalink
impl parsing for user defined fn
Browse files Browse the repository at this point in the history
  • Loading branch information
nulluser committed Jun 1, 2024
1 parent ee71ea8 commit da55725
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/backend/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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...
}
}
16 changes: 16 additions & 0 deletions src/backend/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum ValueType {
Bool,
Object,
NativeFunction,
Function,
}

pub trait RuntimeVal: std::fmt::Debug {
Expand Down Expand Up @@ -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 {
Expand All @@ -101,6 +115,7 @@ pub enum Val {
Bool(BoolVal),
Object(ObjectVal),
NativeFunction(NativeFunctionVal),
Function(FunctionVal),
}

impl RuntimeVal for Val {
Expand All @@ -112,6 +127,7 @@ impl RuntimeVal for Val {
Val::Bool(_) => ValueType::Bool,
Val::Object(_) => ValueType::Object,
Val::NativeFunction(_) => ValueType::NativeFunction,
Val::Function(_) => ValueType::Function,
}
}
}
Expand Down
81 changes: 58 additions & 23 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
io::{self, Write},
fs::File,
io::{self, Read, Write},
process,
};

Expand All @@ -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() {
Expand All @@ -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");

Expand All @@ -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);
}
}
1 change: 1 addition & 0 deletions src/frontend/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>),
}

Expand Down
2 changes: 2 additions & 0 deletions src/frontend/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub enum Keyword {
Ok, // Ok
Exit, // exit
Break, // break
Async, // async
}

impl FromStr for Keyword {
Expand All @@ -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)),
}
}
Expand Down
40 changes: 40 additions & 0 deletions src/frontend/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Parser

use core::panic;
use std::process;

use crate::frontend::ast::{BinaryOp, Program, Property};
Expand Down Expand Up @@ -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!(),
Expand All @@ -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.
Expand Down

0 comments on commit da55725

Please sign in to comment.