From 4eb97d4eedd834feb0c708ff0a403ceac0af1aa6 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:47:25 +0000 Subject: [PATCH] style: format code with Rustfmt This commit fixes the style issues introduced in 599fcee according to the output from Rustfmt. Details: None --- src/builtin.rs | 178 +++++++++++++++------------ src/debug_output.rs | 126 +++++++++++-------- src/expr.rs | 88 +++++++------- src/gen_ir.rs | 33 +++-- src/interpreter.rs | 289 +++++++++++++++++++++++++------------------- src/lib.rs | 12 +- src/main.rs | 36 +++--- src/parser.rs | 218 ++++++++++++++++++++------------- src/tokenizer.rs | 113 +++++++++-------- 9 files changed, 619 insertions(+), 474 deletions(-) diff --git a/src/builtin.rs b/src/builtin.rs index 7530ba0..86aa72f 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,20 +1,29 @@ // This file should only be used at runtime - -use std::io::{BufRead, Write}; -use std::rc::Rc; -use log::debug; use crate::debug_output::build_expr_debug_strings; use crate::expr::ExprAST; use crate::interpreter::{GlobalState, IroncamelFileInfo}; +use log::debug; +use std::io::{BufRead, Write}; +use std::rc::Rc; -pub const IRONCAMEL_BUILTIN_FUNCTIONS: &[&str; 7] = &["cons", "hd", "tl", "list", "is_empty", - "atoi", "strtok"]; -pub const ARITHMETIC_OPERATORS: &[&str; 8] = &["<=", ">=", "+", "-", "*", "==", ">", "<", ]; +pub const IRONCAMEL_BUILTIN_FUNCTIONS: &[&str; 7] = + &["cons", "hd", "tl", "list", "is_empty", "atoi", "strtok"]; +pub const ARITHMETIC_OPERATORS: &[&str; 8] = &["<=", ">=", "+", "-", "*", "==", ">", "<"]; #[allow(dead_code)] -pub const IO_OPERATIONS: &[&str; 5] = &["readstr", "writeline", "writelist", "fopen_read", "fopen_write"]; +pub const IO_OPERATIONS: &[&str; 5] = &[ + "readstr", + "writeline", + "writelist", + "fopen_read", + "fopen_write", +]; -pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: &mut GlobalState) -> ExprAST { +pub(crate) fn perform_read( + method_name: &str, + file_handler: &str, + global_state: &mut GlobalState, +) -> ExprAST { let fop = global_state.open_file_list.get_mut(file_handler).unwrap(); match method_name { "readstr" => { @@ -23,7 +32,9 @@ pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: & IroncamelFileInfo::FileRead(buf) => { let _ = buf.read_line(&mut s); } - IroncamelFileInfo::FileWrite(_) | IroncamelFileInfo::Stdout => { panic!() } + IroncamelFileInfo::FileWrite(_) | IroncamelFileInfo::Stdout => { + panic!() + } IroncamelFileInfo::Stdin => { let mut t = String::new(); std::io::stdin().read_line(&mut t).unwrap(); @@ -32,22 +43,27 @@ pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: & }; let expr = ExprAST::StringLiteral(s); expr - }, - _ => panic!("No such write function ({})", method_name) + } + _ => panic!("No such write function ({})", method_name), } } -pub fn perform_write(method_name:&str, file_handler: &str, data:&ExprAST, global_state: &mut GlobalState) { +pub fn perform_write( + method_name: &str, + file_handler: &str, + data: &ExprAST, + global_state: &mut GlobalState, +) { let fop: &mut IroncamelFileInfo = global_state.open_file_list.get_mut(file_handler).unwrap(); match method_name { "writeline" => writeline(data, fop), "writelist" => writelist(data, fop), - _ => panic!("No such write function ({})", method_name) + _ => panic!("No such write function ({})", method_name), } } fn writelist(list: &ExprAST, fop: &mut IroncamelFileInfo) { - let mut list = match list { + let mut list = match list { ExprAST::List(l) => l, _ => panic!("Expect a list, got {:?}", build_expr_debug_strings(list)), }; @@ -56,7 +72,7 @@ fn writelist(list: &ExprAST, fop: &mut IroncamelFileInfo) { write_internal(" ", fop); list = &*match &list.next { Some(l) => l, - None => break + None => break, } } write_internal("\n", fop); @@ -66,22 +82,27 @@ fn write_internal(s: &str, fop: &mut IroncamelFileInfo) { match fop { IroncamelFileInfo::FileWrite(fs) => { let _ = fs.write_all(s.as_ref()); - }, + } IroncamelFileInfo::Stdout => { print!("{}", s); - }, - IroncamelFileInfo::FileRead(_) | IroncamelFileInfo::Stdin => {panic!()} - + } + IroncamelFileInfo::FileRead(_) | IroncamelFileInfo::Stdin => { + panic!() + } } } fn write(e: &ExprAST, fop: &mut IroncamelFileInfo) { match e { ExprAST::Int(x) => write_internal(&x.to_string(), fop), ExprAST::Bool(x) => { - if *x {write_internal("true", fop)} else {write_internal("false", fop)} + if *x { + write_internal("true", fop) + } else { + write_internal("false", fop) + } } ExprAST::StringLiteral(s) => write_internal(s, fop), - _ => panic!("Unsupported expr: {:?}", build_expr_debug_strings(e)) + _ => panic!("Unsupported expr: {:?}", build_expr_debug_strings(e)), } } fn writeline(e: &ExprAST, fop: &mut IroncamelFileInfo) { @@ -90,10 +111,16 @@ fn writeline(e: &ExprAST, fop: &mut IroncamelFileInfo) { } enum ArithmeticCalcOp { - Add, Minus, Multiple + Add, + Minus, + Multiple, } enum ArithmeticCmpOp { - Gt, Lt, Geq, Leq, Eq + Gt, + Lt, + Geq, + Leq, + Eq, } pub fn call_builtin_function(func_name: &str, params: Vec) -> ExprAST { @@ -104,70 +131,65 @@ pub fn call_builtin_function(func_name: &str, params: Vec) -> ExprAST { "<" => arithmetic_cmp(ArithmeticCmpOp::Lt, ¶ms), ">=" => arithmetic_cmp(ArithmeticCmpOp::Geq, ¶ms), "<=" => arithmetic_cmp(ArithmeticCmpOp::Leq, ¶ms), - "+" => arithmetic_calc(ArithmeticCalcOp::Add, ¶ms), - "-" => arithmetic_calc(ArithmeticCalcOp::Minus, ¶ms), - "*" => arithmetic_calc(ArithmeticCalcOp::Multiple, ¶ms), - "list" => { - ExprAST::List(Rc::new(IroncamelLinkedList::build_list(params.as_slice()))) - } + "+" => arithmetic_calc(ArithmeticCalcOp::Add, ¶ms), + "-" => arithmetic_calc(ArithmeticCalcOp::Minus, ¶ms), + "*" => arithmetic_calc(ArithmeticCalcOp::Multiple, ¶ms), + "list" => ExprAST::List(Rc::new(IroncamelLinkedList::build_list(params.as_slice()))), "cons" => { assert_eq!(params.len(), 2); let tail = match ¶ms[1] { ExprAST::List(l) => l, - _ => panic!("Expect a list as the second param, got {:?}",¶ms[1]) + _ => panic!("Expect a list as the second param, got {:?}", ¶ms[1]), }; - let result = IroncamelLinkedList::cons(params[0].clone(), - tail); + let result = IroncamelLinkedList::cons(params[0].clone(), tail); ExprAST::List(Rc::new(result)) - }, + } "hd" => { assert_eq!(params.len(), 1); match ¶ms[0] { ExprAST::List(l) => l.hd().clone(), - _ => panic!("Expect a list, got {:?}",¶ms[0]) + _ => panic!("Expect a list, got {:?}", ¶ms[0]), } - }, + } "tl" => { assert_eq!(params.len(), 1); match ¶ms[0] { - ExprAST::List(l) => { - match l.tl() { - Some(t) => ExprAST::List(t), - None => build_empty_list_expr() - } + ExprAST::List(l) => match l.tl() { + Some(t) => ExprAST::List(t), + None => build_empty_list_expr(), }, - _ => panic!("Expect a list, got {:?}",¶ms[0]) + _ => panic!("Expect a list, got {:?}", ¶ms[0]), } - }, + } "is_empty" => { assert_eq!(params.len(), 1); match ¶ms[0] { ExprAST::List(l) => { let r = l.len == 0; ExprAST::Bool(r) - }, - _ => panic!("Expect a list, got {:?}",¶ms[0]) + } + _ => panic!("Expect a list, got {:?}", ¶ms[0]), } - }, + } "atoi" => { assert_eq!(params.len(), 1); match ¶ms[0] { ExprAST::StringLiteral(s) => { - let x:i64 = s.parse().unwrap(); + let x: i64 = s.parse().unwrap(); ExprAST::Int(x) - }, - _ => panic!("Expect a String, got {:?}",¶ms[0]) + } + _ => panic!("Expect a String, got {:?}", ¶ms[0]), } - }, + } "strtok" => { assert_eq!(params.len(), 2); let origin_str = match ¶ms[0] { ExprAST::StringLiteral(s) => s, - _ => panic!("Expect a String, got {:?}",¶ms[0]) + _ => panic!("Expect a String, got {:?}", ¶ms[0]), }; let delims = match ¶ms[1] { ExprAST::StringLiteral(s) => s, - _ => panic!("Expect a String, got {:?}",¶ms[0]) + _ => panic!("Expect a String, got {:?}", ¶ms[0]), }; debug!("got delims {}", delims); let delim_list: Vec = delims.chars().collect(); @@ -176,21 +198,18 @@ pub fn call_builtin_function(func_name: &str, params: Vec) -> ExprAST { debug!("split: {:?}", split_str); let mut result: Vec = Vec::new(); for s in split_str { - if s.is_empty() { continue } + if s.is_empty() { + continue; + } let e = ExprAST::StringLiteral(s.to_owned()); result.push(e); } - ExprAST::List( - Rc::new(IroncamelLinkedList::build_list(&result) - )) - - }, - _ => panic!("Builtin function ({}) not found", func_name) + ExprAST::List(Rc::new(IroncamelLinkedList::build_list(&result))) + } + _ => panic!("Builtin function ({}) not found", func_name), } } - - fn arithmetic_calc(op: ArithmeticCalcOp, p: &Vec) -> ExprAST { assert_eq!(p.len(), 2); let a = unpack_num(&p[0]); @@ -212,7 +231,7 @@ fn arithmetic_cmp(op: ArithmeticCmpOp, p: &Vec) -> ExprAST { ArithmeticCmpOp::Gt => a > b, ArithmeticCmpOp::Lt => a < b, ArithmeticCmpOp::Geq => a >= b, - ArithmeticCmpOp::Leq => a <= b + ArithmeticCmpOp::Leq => a <= b, }; ExprAST::Bool(result) } @@ -220,26 +239,25 @@ fn arithmetic_cmp(op: ArithmeticCmpOp, p: &Vec) -> ExprAST { fn unpack_num(e: &ExprAST) -> i64 { match e { ExprAST::Int(x) => *x, - _ => panic!("Expected int, got {:?}", build_expr_debug_strings(e)) + _ => panic!("Expected int, got {:?}", build_expr_debug_strings(e)), } } - pub struct IroncamelLinkedList { value: Box, pub(crate) len: usize, // Allows us to calculate list size with O(1) cost - next: Option> + next: Option>, } pub fn build_empty_list_expr() -> ExprAST { ExprAST::List(Rc::new(IroncamelLinkedList::build_empty_list())) } impl IroncamelLinkedList { - pub(crate) fn build_empty_list() -> IroncamelLinkedList{ + pub(crate) fn build_empty_list() -> IroncamelLinkedList { IroncamelLinkedList { value: Box::new(ExprAST::Error), len: 0, - next: None + next: None, } } pub fn build_list(exprs: &[ExprAST]) -> IroncamelLinkedList { @@ -250,7 +268,7 @@ impl IroncamelLinkedList { IroncamelLinkedList::build(exprs[0].clone()) } else { let tail_exprs = &exprs[1..]; - assert_eq!(exprs.len(), tail_exprs.len()+1); + assert_eq!(exprs.len(), tail_exprs.len() + 1); let tail_link = IroncamelLinkedList::build_list(tail_exprs); assert_eq!(tail_link.len, tail_exprs.len()); IroncamelLinkedList::cons(exprs[0].clone(), &Rc::new(tail_link)) @@ -261,7 +279,7 @@ impl IroncamelLinkedList { IroncamelLinkedList { value: Box::new(expr), len: 1, - next: None + next: None, } } pub fn cons(expr: ExprAST, tail: &Rc) -> IroncamelLinkedList { @@ -290,9 +308,11 @@ impl IroncamelLinkedList { vec![self.value.clone()] } else { match &self.next { - None => { panic!("Expected to have a tail") } + None => { + panic!("Expected to have a tail") + } Some(next) => { - assert_eq!(next.len+1, self.len); + assert_eq!(next.len + 1, self.len); let mut result = next.as_vector(); result.push(self.value.clone()); result @@ -307,7 +327,7 @@ impl IroncamelLinkedList { for i in 0..self.len { let v = match *exprs[i] { ExprAST::Int(x) => x, - _ => panic!("Expect an integer") + _ => panic!("Expect an integer"), }; result.push(v); } @@ -321,23 +341,23 @@ impl Clone for IroncamelLinkedList { // https://stackoverflow.com/a/61950053/1166518 let next = match &self.next { Some(s) => Some(std::rc::Rc::clone(s)), - None => None + None => None, }; IroncamelLinkedList { value: self.value.clone(), len: self.len, - next + next, } } } - #[cfg(test)] mod tests { use crate::builtin::IroncamelLinkedList; use crate::expr::ExprAST; - fn gei(x:i64) -> ExprAST { //generate expr int + fn gei(x: i64) -> ExprAST { + //generate expr int ExprAST::Int(x) } #[test] @@ -364,15 +384,13 @@ mod tests { let v = l2.hd(); match v { ExprAST::Int(x) => assert_eq!(*x, 42), - _ => assert!(false) + _ => assert!(false), }; let l3 = l2.tl(); match l3 { Some(l3) => assert_eq!(l3.as_vector_i64(), vec![5]), - None => assert!(false) + None => assert!(false), }; - } } - diff --git a/src/debug_output.rs b/src/debug_output.rs index 5d3186e..4cbe50c 100644 --- a/src/debug_output.rs +++ b/src/debug_output.rs @@ -1,86 +1,99 @@ -use std::fmt; use crate::expr::{ExprAST, IfElseExpr}; use crate::interpreter::CallableObject; -use crate::parser::{StatementAST, LetBindingAST, AST, DEBUG_TREE_INDENT, FunctionAST, BlockAST, ReadAst, ProgramAST, WriteAst}; +use crate::parser::{ + AST, BlockAST, DEBUG_TREE_INDENT, FunctionAST, LetBindingAST, ProgramAST, ReadAst, + StatementAST, WriteAst, +}; +use std::fmt; pub fn build_statement_debug_strings(statement: &StatementAST) -> Vec { - return match statement { StatementAST::Bind(lb) => lb.debug_strings(), // StatementAST::EmptyStatement => vec![String::from("EmptyStatemt")], StatementAST::Read(r) => build_read_operation_debug_strings(r), StatementAST::Write(w) => build_write_operation_debug_strings(w), - StatementAST::Error=> vec![String::from("ERROR!!")], + StatementAST::Error => vec![String::from("ERROR!!")], StatementAST::FileOpen(o) => { - let s = format!("{p} ( {v} ) as {f} ", - p = o.impure_procedure_name, - f = o.file_handler, - v = o.file_path); + let s = format!( + "{p} ( {v} ) as {f} ", + p = o.impure_procedure_name, + f = o.file_handler, + v = o.file_path + ); vec![s] } - } - + }; } fn build_write_operation_debug_strings(write: &WriteAst) -> Vec { let mut debug = Vec::with_capacity(3); - let s = format!("{p} to {f} :", - p = write.impure_procedure_name, - f = write.file_handler); + let s = format!( + "{p} to {f} :", + p = write.impure_procedure_name, + f = write.file_handler + ); debug.push(s); - for debug_str in build_expr_debug_strings(&write.expr){ - let s:String = DEBUG_TREE_INDENT.to_owned() + &debug_str; + for debug_str in build_expr_debug_strings(&write.expr) { + let s: String = DEBUG_TREE_INDENT.to_owned() + &debug_str; debug.push(s); } debug } fn build_read_operation_debug_strings(read: &ReadAst) -> Vec { - let s = format!("{p} from {f} to >> {v}", - p = read.impure_procedure_name, - f = read.file_handler, - v = read.write_to_variable); + let s = format!( + "{p} from {f} to >> {v}", + p = read.impure_procedure_name, + f = read.file_handler, + v = read.write_to_variable + ); vec![s] } - impl AST for IfElseExpr { fn debug_strings(&self) -> Vec { let mut debug = Vec::with_capacity(3); - debug.push(format!("if ({con})", con=build_expr_debug_strings(&self.condition).join(" "))); - debug.push(format!("{ind}then {con}", - ind=DEBUG_TREE_INDENT, - con=self.then_case.debug_strings().join(" "))); - debug.push(format!("{ind}else {con}", - ind=DEBUG_TREE_INDENT, - con=self.else_case.debug_strings().join(" "))); + debug.push(format!( + "if ({con})", + con = build_expr_debug_strings(&self.condition).join(" ") + )); + debug.push(format!( + "{ind}then {con}", + ind = DEBUG_TREE_INDENT, + con = self.then_case.debug_strings().join(" ") + )); + debug.push(format!( + "{ind}else {con}", + ind = DEBUG_TREE_INDENT, + con = self.else_case.debug_strings().join(" ") + )); debug } } - - - pub fn build_expr_debug_strings(expr: &ExprAST) -> Vec { return match expr { ExprAST::If(s) => s.debug_strings(), - ExprAST::Int(i) => vec![ format!("Integer: {val}", val=i) ], - ExprAST::Bool(b) => vec![ format!("Bool: {val}", val=if *b {"true"} else {"false"}) ], - ExprAST::Variable(v) => vec![ format!("Variable: {val}", val=v) ], - ExprAST::StringLiteral(v) => vec![ format!("Str: {val}", val=v) ], + ExprAST::Int(i) => vec![format!("Integer: {val}", val = i)], + ExprAST::Bool(b) => vec![format!( + "Bool: {val}", + val = if *b { "true" } else { "false" } + )], + ExprAST::Variable(v) => vec![format!("Variable: {val}", val = v)], + ExprAST::StringLiteral(v) => vec![format!("Str: {val}", val = v)], ExprAST::CallCallableObjectByname(func_name, args) => { let mut debug = Vec::with_capacity(1 + args.len()); - debug.push( format!("Call: {val}", val=func_name) ); + debug.push(format!("Call: {val}", val = func_name)); for expr in args { let single_line = build_expr_debug_strings(expr).join(" "); debug.push(DEBUG_TREE_INDENT.to_owned() + &single_line); } debug - }, + } ExprAST::Block(block) => block.debug_strings(), ExprAST::CallBuiltinFunction(func_name, args) => { let mut debug = Vec::with_capacity(1 + args.len()); - debug.push( format!("CallBuiltin: {val}", val=func_name) ); + debug.push(format!("CallBuiltin: {val}", val = func_name)); for expr in args { let single_line = build_expr_debug_strings(expr).join(" "); debug.push(DEBUG_TREE_INDENT.to_owned() + &single_line); @@ -98,19 +111,24 @@ pub fn build_expr_debug_strings(expr: &ExprAST) -> Vec { debug.push(DEBUG_TREE_INDENT.to_owned() + &single_line); } debug - }, - + } }; } pub fn build_callable_object_debug_string(co: &CallableObject) -> String { match co { - CallableObject::GlobalFunction(g) => { format!("Global function [{v}]", v=g)} - CallableObject::BuiltinFunction(b) => { format!("Builtin function [{v}]", v=b)} + CallableObject::GlobalFunction(g) => { + format!("Global function [{v}]", v = g) + } + CallableObject::BuiltinFunction(b) => { + format!("Builtin function [{v}]", v = b) + } CallableObject::Closure(clos, local_env) => { - format!("BindedClosure [{c}] in [{e}]", - c=build_expr_debug_strings(&ExprAST::Closure(clos.clone())).join(", "), - e=local_env.keys().cloned().collect::>().join(",")) + format!( + "BindedClosure [{c}] in [{e}]", + c = build_expr_debug_strings(&ExprAST::Closure(clos.clone())).join(", "), + e = local_env.keys().cloned().collect::>().join(",") + ) } } } @@ -118,9 +136,9 @@ pub fn build_callable_object_debug_string(co: &CallableObject) -> String { impl AST for LetBindingAST { fn debug_strings(&self) -> Vec { let mut debug = Vec::new(); - debug.push(format!("Let {var} = ", var=&self.variable)); + debug.push(format!("Let {var} = ", var = &self.variable)); for dbgs in build_expr_debug_strings(&self.expr) { - let s:String = DEBUG_TREE_INDENT.to_owned() + &dbgs; + let s: String = DEBUG_TREE_INDENT.to_owned() + &dbgs; debug.push(s); } debug @@ -130,16 +148,19 @@ impl AST for LetBindingAST { impl AST for FunctionAST { fn debug_strings(&self) -> Vec { let mut debug = Vec::with_capacity(1 + self.statements.len()); - debug.push(format!("Function: {fname} Args: {args}", - fname=&self.function_name, args=self.arguments.join(","))); + debug.push(format!( + "Function: {fname} Args: {args}", + fname = &self.function_name, + args = self.arguments.join(",") + )); for statement in &self.statements { for debug_str in build_statement_debug_strings(statement) { - let s:String = DEBUG_TREE_INDENT.to_owned() + &debug_str; + let s: String = DEBUG_TREE_INDENT.to_owned() + &debug_str; debug.push(s); } } for debug_str in build_expr_debug_strings(&self.return_expr) { - let s:String = DEBUG_TREE_INDENT.to_owned() + &debug_str; + let s: String = DEBUG_TREE_INDENT.to_owned() + &debug_str; debug.push(s); } debug @@ -152,14 +173,13 @@ impl fmt::Debug for FunctionAST { } } - impl AST for BlockAST { fn debug_strings(&self) -> Vec { let mut debug = Vec::with_capacity(1 + self.statements.len()); for statement in &self.statements { debug.extend(build_statement_debug_strings(statement)); } - debug.extend(build_expr_debug_strings(&self.return_expr) ); + debug.extend(build_expr_debug_strings(&self.return_expr)); debug } } @@ -174,7 +194,7 @@ impl AST for ProgramAST { debug.push(format!("Program")); for f in &self.functions { for dbgs in f.debug_strings() { - let s:String = DEBUG_TREE_INDENT.to_owned() + &dbgs; + let s: String = DEBUG_TREE_INDENT.to_owned() + &dbgs; debug.push(s); } } diff --git a/src/expr.rs b/src/expr.rs index 19d2d68..e1cf016 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,15 +1,18 @@ // This is part of parser. However, as Expr is the most complicated part when building the AST // I'm separating it to a new file -use std::fmt::{Debug, Formatter}; -use std::rc::Rc; -use log::{error, warn,debug}; use crate::builtin::IroncamelLinkedList; use crate::debug_output::build_expr_debug_strings; use crate::interpreter::CallableObject; -use crate::parser::{BlockAST, read_block, read_argument_list}; +use crate::parser::{BlockAST, read_argument_list, read_block}; use crate::tokenizer::Token; -use crate::tokenizer::Token::{Integer64, LiteralTrue, LiteralFalse, KeywordIf, KeywordThen, KeywordElse, LeftParentheses, RightParentheses}; +use crate::tokenizer::Token::{ + Integer64, KeywordElse, KeywordIf, KeywordThen, LeftParentheses, LiteralFalse, LiteralTrue, + RightParentheses, +}; +use log::{debug, error, warn}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; #[derive(Clone)] pub enum ExprAST { @@ -24,7 +27,6 @@ pub enum ExprAST { CallCallableObjectByname(String, Vec>), Error, - // Below in involved by interpreter CallBuiltinFunction(String, Vec>), Callable(CallableObject), @@ -37,7 +39,6 @@ impl Debug for ExprAST { } } - /* In other parts in my parsr, I would use Option to wrap the AST object However, I just found that I can't warp a dyn trait: https://users.rust-lang.org/t/why-doesnt-option-support-dyn-trait/45353/11 I don't like this inconsistency (well the structure s not perfect) @@ -49,13 +50,11 @@ pub fn try_read_expr(tokens: &Vec, pos: usize) -> (ExprAST, Option match &tokens[pos] { Integer64(x) => { return (ExprAST::Int(*x), Some(1)); - }, - Token::LiteralString(s) => { - return (ExprAST::StringLiteral(s.to_owned()), Some(1)) - }, + } + Token::LiteralString(s) => return (ExprAST::StringLiteral(s.to_owned()), Some(1)), LiteralTrue => { return (ExprAST::Bool(true), Some(1)); - }, + } LiteralFalse => { return (ExprAST::Bool(false), Some(1)); } @@ -67,10 +66,8 @@ pub fn try_read_expr(tokens: &Vec, pos: usize) -> (ExprAST, Option let (call, len) = try_read_function_call(tokens, pos); match &call { ExprAST::Error => return (ExprAST::Variable(s.to_owned()), Some(1)), - ExprAST::CallCallableObjectByname(_callee, _args) => { - return (call, Some(len)) - }, - _ => panic!("Unexpected read result for identifier!") + ExprAST::CallCallableObjectByname(_callee, _args) => return (call, Some(len)), + _ => panic!("Unexpected read result for identifier!"), } } Token::VerticalBar => { @@ -93,20 +90,22 @@ fn try_read_function_call(tokens: &Vec, pos: usize) -> (ExprAST, usize) { let func_name; match &tokens[pos] { Token::IdentifierToken(s) => func_name = s, - _ => panic!("Unexpected token") + _ => panic!("Unexpected token"), }; len += 1; - - match tokens[pos+len] { + match tokens[pos + len] { LeftParentheses => (), // It means this is not a function call - _ => return (ExprAST::Error, 0) + _ => return (ExprAST::Error, 0), }; len += 1; - while tokens[pos+len] != Token::RightParentheses { - if tokens[pos+len] == Token::Comma { len += 1; continue; } - let (expr, expr_len) = try_read_expr(tokens, pos+len); + while tokens[pos + len] != Token::RightParentheses { + if tokens[pos + len] == Token::Comma { + len += 1; + continue; + } + let (expr, expr_len) = try_read_expr(tokens, pos + len); match expr_len { None => panic!("There should be a valid expr!"), Some(el) => { @@ -116,11 +115,18 @@ fn try_read_function_call(tokens: &Vec, pos: usize) -> (ExprAST, usize) { }; } - debug!("Found such function call {}, ({:?})", func_name, parameters.len()); - assert_eq!(tokens[pos+len], RightParentheses); + debug!( + "Found such function call {}, ({:?})", + func_name, + parameters.len() + ); + assert_eq!(tokens[pos + len], RightParentheses); len += 1; - (ExprAST::CallCallableObjectByname(func_name.to_owned(), parameters), len) + ( + ExprAST::CallCallableObjectByname(func_name.to_owned(), parameters), + len, + ) } fn read_if_expr(tokens: &Vec, pos: usize) -> (IfElseExpr, usize) { @@ -128,26 +134,26 @@ fn read_if_expr(tokens: &Vec, pos: usize) -> (IfElseExpr, usize) { assert_eq!(KeywordIf, tokens[pos + len]); len += 1; - let (condition, con_len) = try_read_expr(tokens, len+pos); + let (condition, con_len) = try_read_expr(tokens, len + pos); let con_len = con_len.unwrap(); len += con_len; assert_eq!(KeywordThen, tokens[pos + len]); len += 1; - let (then_case, con_len) = read_block(tokens, len+pos); + let (then_case, con_len) = read_block(tokens, len + pos); len += con_len; assert_eq!(KeywordElse, tokens[pos + len]); len += 1; - let (else_case, con_len) = read_block(tokens, len+pos); + let (else_case, con_len) = read_block(tokens, len + pos); len += con_len; - let ast = IfElseExpr{ + let ast = IfElseExpr { condition: Box::new(condition), then_case, - else_case + else_case, }; (ast, len) } @@ -158,37 +164,35 @@ fn read_closure(tokens: &Vec, pos: usize) -> (ClosureAST, usize) { assert_eq!(Token::VerticalBar, tokens[pos + len]); len += 1; - let (arguments, len_args) = read_argument_list(tokens, pos+len); + let (arguments, len_args) = read_argument_list(tokens, pos + len); len += len_args; warn!("Get argument list {:?}, consumed {}", &arguments, len_args); - assert_eq!(Token::VerticalBar, tokens[pos + len]); len += 1; - let (block, len_block) = read_block(tokens, pos+len); + let (block, len_block) = read_block(tokens, pos + len); len += len_block; - let result = ClosureAST{ + let result = ClosureAST { params: arguments, - block + block, }; (result, len) } - pub struct IntegerLiteral { - pub value: i64 + pub value: i64, } #[derive(Clone)] pub struct IfElseExpr { pub condition: Box, pub then_case: BlockAST, - pub else_case: BlockAST + pub else_case: BlockAST, } #[derive(Clone)] -pub struct ClosureAST{ +pub struct ClosureAST { pub params: Vec, - pub block: BlockAST -} \ No newline at end of file + pub block: BlockAST, +} diff --git a/src/gen_ir.rs b/src/gen_ir.rs index def9015..3d68452 100644 --- a/src/gen_ir.rs +++ b/src/gen_ir.rs @@ -1,27 +1,24 @@ - - -use crate::parser::{ProgramAST, FunctionAST}; use crate::expr::ExprAST; +use crate::parser::{FunctionAST, ProgramAST}; use log::info; +use inkwell::builder::Builder; use inkwell::context::Context; use inkwell::module::Module; -use inkwell::builder::Builder; -use inkwell::values::InstructionValue; -use inkwell::values::AnyValue; use inkwell::support::LLVMString; +use inkwell::values::AnyValue; +use inkwell::values::InstructionValue; struct Compiler<'a> { context: &'a Context, - module: Module<'a>, + module: Module<'a>, builder: Builder<'a>, } - -fn compile_fn<'a>(compiler: &Compiler<'a>, fnast: &FunctionAST) -> LLVMString{ -//InstructionValue<'a>{ +fn compile_fn<'a>(compiler: &Compiler<'a>, fnast: &FunctionAST) -> LLVMString { + //InstructionValue<'a>{ let context = &compiler.context; - let module = &compiler.module; + let module = &compiler.module; let builder = &compiler.builder; info!("building function {:?}", &fnast); @@ -37,8 +34,7 @@ fn compile_fn<'a>(compiler: &Compiler<'a>, fnast: &FunctionAST) -> LLVMString{ builder.position_at_end(entry); let return_value = match &*fnast.return_expr { - ExprAST::Int(x) => - context.i64_type().const_int( (*x) as u64, false), + ExprAST::Int(x) => context.i64_type().const_int((*x) as u64, false), _ => unimplemented!(), }; @@ -50,12 +46,16 @@ fn compile_fn<'a>(compiler: &Compiler<'a>, fnast: &FunctionAST) -> LLVMString{ pub fn compile(ast: &ProgramAST) -> String { info!("to compile to llvm IR"); - let mut str_builder: Vec = vec!(); + let mut str_builder: Vec = vec![]; let context = Context::create(); - let module = context.create_module("iron"); + let module = context.create_module("iron"); let builder = context.create_builder(); - let compiler = Compiler {context:&context, module, builder}; + let compiler = Compiler { + context: &context, + module, + builder, + }; for fnast in &ast.functions { let code = compile_fn(&compiler, &fnast); @@ -65,4 +65,3 @@ pub fn compile(ast: &ProgramAST) -> String { return str_builder.join(" \n"); } - diff --git a/src/interpreter.rs b/src/interpreter.rs index 839dab0..fc8be3e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,27 +1,25 @@ -use std::collections::HashMap; -use std::io::BufReader; -use std::rc::Rc; -use log::{debug, info}; use crate::builtin; -use crate::parser::{BlockAST, function2block, FunctionAST, ProgramAST, StatementAST}; -use crate::parser::AST; use crate::debug_output::build_expr_debug_strings; use crate::expr::{ClosureAST, ExprAST}; - +use crate::parser::AST; +use crate::parser::{BlockAST, FunctionAST, ProgramAST, StatementAST, function2block}; +use log::{debug, info}; +use std::collections::HashMap; +use std::io::BufReader; +use std::rc::Rc; use crate::builtin::{IroncamelLinkedList, perform_write}; use crate::interpreter::CallableObject::Closure; - pub struct GlobalState { - pub global_scope: HashMap, - pub open_file_list: HashMap + pub global_scope: HashMap, + pub open_file_list: HashMap, } impl GlobalState { pub(crate) fn has_builtin_function(&self, func_name: &str) -> bool { - builtin::ARITHMETIC_OPERATORS.contains(&func_name) || - builtin::IRONCAMEL_BUILTIN_FUNCTIONS.contains(&func_name) + builtin::ARITHMETIC_OPERATORS.contains(&func_name) + || builtin::IRONCAMEL_BUILTIN_FUNCTIONS.contains(&func_name) } pub(crate) fn find_global_function(&self, func_name: &String) -> Option<&FunctionAST> { self.global_scope.get(func_name) @@ -41,16 +39,15 @@ impl GlobalState { pub enum CallableObject { GlobalFunction(String), BuiltinFunction(String), - Closure(Rc, Rc>), + Closure(Rc, Rc>), } pub fn eval(ast: &ProgramAST) -> i64 { let mut global_scope = build_global_state(ast); - let main_ast = ast.functions.iter().find( - |&x| x.function_name == "main"); + let main_ast = ast.functions.iter().find(|&x| x.function_name == "main"); match main_ast { None => panic!("function main not found!"), - _ => () + _ => (), } let main_ast = main_ast.unwrap(); debug!("main ast {:?}", main_ast.debug_strings()); @@ -58,7 +55,11 @@ pub fn eval(ast: &ProgramAST) -> i64 { 0 } -fn execute_main_function(global: &mut GlobalState, mut local: HashMap, fun: &FunctionAST) { +fn execute_main_function( + global: &mut GlobalState, + mut local: HashMap, + fun: &FunctionAST, +) { for s in &fun.statements { match &s { StatementAST::Bind(lb) => { @@ -70,31 +71,40 @@ fn execute_main_function(global: &mut GlobalState, mut local: HashMap { debug!("Trying to process write"); let expr = solve(&global, &local, &write.expr); - perform_write(&write.impure_procedure_name, &write.file_handler, - &expr, global); - }, - StatementAST::FileOpen(fo) => { - match fo.impure_procedure_name.as_str() { - "fopen_read" => { - let fin = std::fs::File::open(&fo.file_path).expect("file not found"); - let reader = BufReader::new(fin); - let f_data = IroncamelFileInfo::FileRead(reader); - global.open_file_list.insert(fo.file_handler.to_owned(), f_data); - debug!("Open file {} as handler {}", fo.file_path, fo.file_handler); - }, - "fopen_write" => { - let fout = std::fs::File::create(&fo.file_path).expect("Create file failed"); - let f_data = IroncamelFileInfo::FileWrite(fout); - global.open_file_list.insert(fo.file_handler.to_owned(), f_data); - debug!("Open file {} as handler {}", fo.file_path, fo.file_handler); - }, - _ => { - panic!("No such FileOpen procedure! {}", fo.impure_procedure_name.as_str()); - } + perform_write( + &write.impure_procedure_name, + &write.file_handler, + &expr, + global, + ); + } + StatementAST::FileOpen(fo) => match fo.impure_procedure_name.as_str() { + "fopen_read" => { + let fin = std::fs::File::open(&fo.file_path).expect("file not found"); + let reader = BufReader::new(fin); + let f_data = IroncamelFileInfo::FileRead(reader); + global + .open_file_list + .insert(fo.file_handler.to_owned(), f_data); + debug!("Open file {} as handler {}", fo.file_path, fo.file_handler); + } + "fopen_write" => { + let fout = std::fs::File::create(&fo.file_path).expect("Create file failed"); + let f_data = IroncamelFileInfo::FileWrite(fout); + global + .open_file_list + .insert(fo.file_handler.to_owned(), f_data); + debug!("Open file {} as handler {}", fo.file_path, fo.file_handler); + } + _ => { + panic!( + "No such FileOpen procedure! {}", + fo.impure_procedure_name.as_str() + ); } }, StatementAST::Read(r) => { @@ -110,20 +120,22 @@ fn execute_main_function(global: &mut GlobalState, mut local: HashMap GlobalState { let global_functions = process_global_functions(ast); - let mut open_file_list = HashMap::new(); + let mut open_file_list = HashMap::new(); open_file_list.insert("stdin".to_owned(), IroncamelFileInfo::Stdin); open_file_list.insert("stdout".to_owned(), IroncamelFileInfo::Stdout); GlobalState { global_scope: global_functions, - open_file_list + open_file_list, } } -fn execute_block_with_consumable_env(global: &GlobalState, - mut local: HashMap, - exec: &BlockAST, allow_io: bool) -> ExprAST{ +fn execute_block_with_consumable_env( + global: &GlobalState, + mut local: HashMap, + exec: &BlockAST, + allow_io: bool, +) -> ExprAST { assert!(!allow_io); for s in &exec.statements { match &s { @@ -136,16 +148,19 @@ fn execute_block_with_consumable_env(global: &GlobalState, let expr_ast: &ExprAST = &lb.expr; let expr = solve(&global, &local, expr_ast); local.insert(var.to_owned(), expr); - }, + } _ => panic!("Not supported other statements!"), } } solve(global, &mut local, &exec.return_expr) } -fn execute_block(global: &GlobalState, - local: &HashMap, - exec: &BlockAST, allow_io: bool) -> ExprAST{ +fn execute_block( + global: &GlobalState, + local: &HashMap, + exec: &BlockAST, + allow_io: bool, +) -> ExprAST { if exec.statements.len() == 0 { // info!("Fast solve block {:?}", exec.return_expr); // return lazy_solve_no_update(global, local, &exec.return_expr) @@ -154,29 +169,31 @@ fn execute_block(global: &GlobalState, execute_block_with_consumable_env(global, local.clone(), exec, allow_io) } - -fn execute_function(global: &GlobalState, fun: &FunctionAST, params: &Vec, - allow_io: bool) -> ExprAST{ +fn execute_function( + global: &GlobalState, + fun: &FunctionAST, + params: &Vec, + allow_io: bool, +) -> ExprAST { assert_eq!(fun.arguments.len(), params.len()); let mut new_env = HashMap::new(); for i in 0..fun.arguments.len() { let var_name = &fun.arguments[i]; new_env.insert(var_name.to_owned(), params[i].to_owned()); } - execute_block_with_consumable_env( - global, new_env, - &function2block(fun.clone()), allow_io) + execute_block_with_consumable_env(global, new_env, &function2block(fun.clone()), allow_io) } - -fn solve(global: &GlobalState, local: &HashMap, - ast: &ExprAST) -> ExprAST { - - debug!("Eager solving {:?} with env {:?}", build_expr_debug_strings(&ast), local.keys()); +fn solve(global: &GlobalState, local: &HashMap, ast: &ExprAST) -> ExprAST { + debug!( + "Eager solving {:?} with env {:?}", + build_expr_debug_strings(&ast), + local.keys() + ); // info!("Local env {:?}", local.keys()); let result = match ast { - ExprAST::Int(_) | ExprAST::Bool(_) | ExprAST::StringLiteral(_) => ast.clone(), + ExprAST::Int(_) | ExprAST::Bool(_) | ExprAST::StringLiteral(_) => ast.clone(), // TODO the implementation for lookup is not correct ExprAST::Variable(v) => { if global.global_scope.contains_key(v) { @@ -193,22 +210,29 @@ fn solve(global: &GlobalState, local: &HashMap, ExprAST::CallCallableObjectByname(func_name, params) => { // Is this a local function? - let callee : ExprAST = find_callee(global, local, func_name, params); + let callee: ExprAST = find_callee(global, local, func_name, params); solve(global, local, &callee) } ExprAST::If(if_expr) => { let cond = solve(global, local, &if_expr.condition); let cond = match cond { ExprAST::Bool(x) => x, - _ => panic!("Expect a boolean value, got {:?}", build_expr_debug_strings(&cond)) + _ => panic!( + "Expect a boolean value, got {:?}", + build_expr_debug_strings(&cond) + ), + }; + let selected = if cond { + &if_expr.then_case + } else { + &if_expr.else_case }; - let selected = if cond { &if_expr.then_case} else { &if_expr.else_case}; // for s in build_expr_debug_strings(ast) {eprintln!("{}",s);} // info!("Condition is {}", cond); // info!("Selected {:?}", selected.debug_strings()); // info!("Local env is {:?}", local); execute_block(global, local, selected, false) - }, + } ExprAST::CallBuiltinFunction(func_name, params) => { let mut solved_params = Vec::with_capacity(params.len()); for p in params { @@ -216,34 +240,39 @@ fn solve(global: &GlobalState, local: &HashMap, solved_params.push(rp); } builtin::call_builtin_function(&func_name, solved_params) - }, - ExprAST::List(list) => { - ExprAST::List(solve_list(global, local, list)) - }, + } + ExprAST::List(list) => ExprAST::List(solve_list(global, local, list)), - ExprAST::Closure(clos) => { - ExprAST::Callable(Closure( - clos.clone(), - Rc::new(local.clone()) - )) - }, + ExprAST::Closure(clos) => ExprAST::Callable(Closure(clos.clone(), Rc::new(local.clone()))), // _ => { // panic!("Not supported ast yet : {:?}", build_expr_debug_strings(ast)); // } - ExprAST::Block(_) => { panic!("Not supported ast yet : {:?}", build_expr_debug_strings(ast)) } - ExprAST::Error => {panic!("Error!")}, - ExprAST::Callable(_) => { - ast.clone() + ExprAST::Block(_) => { + panic!( + "Not supported ast yet : {:?}", + build_expr_debug_strings(ast) + ) + } + ExprAST::Error => { + panic!("Error!") } + ExprAST::Callable(_) => ast.clone(), }; - debug!("solving {:?} -> {:?}", build_expr_debug_strings(ast), result); + debug!( + "solving {:?} -> {:?}", + build_expr_debug_strings(ast), + result + ); result } -fn solve_list(global: &GlobalState, local: &HashMap, - head: &Rc) -> Rc{ +fn solve_list( + global: &GlobalState, + local: &HashMap, + head: &Rc, +) -> Rc { if head.len == 0 { - return Rc::new(IroncamelLinkedList::build_empty_list()) + return Rc::new(IroncamelLinkedList::build_empty_list()); } let solved_head = solve(global, local, head.hd()); match head.tl() { @@ -251,59 +280,65 @@ fn solve_list(global: &GlobalState, local: &HashMap, let rest = solve_list(global, local, &t); Rc::new(IroncamelLinkedList::cons(solved_head, &rest)) } - None => { - Rc::new(IroncamelLinkedList::build(solved_head)) - } + None => Rc::new(IroncamelLinkedList::build(solved_head)), } - } -fn find_callee(global: &GlobalState, local: &HashMap, func_name: &String, params: &Vec>) -> ExprAST { +fn find_callee( + global: &GlobalState, + local: &HashMap, + func_name: &String, + params: &Vec>, +) -> ExprAST { match local.get(func_name) { Some(x) => { let callee = match x { ExprAST::Callable(co) => co, - _ => panic!("Expect a callable object, got {:?}", x) + _ => panic!("Expect a callable object, got {:?}", x), }; let solved_params = solve_parameters(global, local, params); return match callee { CallableObject::GlobalFunction(f) => { - ExprAST::CallCallableObjectByname(f.to_owned(), - box_expr(&solved_params)) + ExprAST::CallCallableObjectByname(f.to_owned(), box_expr(&solved_params)) } CallableObject::BuiltinFunction(f) => { - ExprAST::CallBuiltinFunction(f.to_owned(), - box_expr( - &solved_params)) + ExprAST::CallBuiltinFunction(f.to_owned(), box_expr(&solved_params)) } CallableObject::Closure(clos, local_env) => { let mut local_env_new = (**local_env).clone(); assert_eq!(clos.params.len(), solved_params.len()); for i in 0..solved_params.len() { - local_env_new.insert(clos.params[i].to_owned(), solved_params[i].to_owned()); + local_env_new + .insert(clos.params[i].to_owned(), solved_params[i].to_owned()); } execute_block_with_consumable_env(global, local_env_new, &clos.block, false) } }; } - None => { debug!("Not found variable ({}) in local scope", func_name)} + None => { + debug!("Not found variable ({}) in local scope", func_name) + } } match global.has_builtin_function(func_name) { true => { let lazy_solved_params = solve_parameters(global, local, params); - return ExprAST::CallBuiltinFunction(func_name.to_owned(), - box_expr(&lazy_solved_params)); - }, - false => { debug!("Not a builtin function ({}) ", func_name)} + return ExprAST::CallBuiltinFunction( + func_name.to_owned(), + box_expr(&lazy_solved_params), + ); + } + false => { + debug!("Not a builtin function ({}) ", func_name) + } } match global.find_global_function(func_name) { Some(fun) => { - - return execute_function(global, fun, - &solve_parameters(global, local, params), false); + return execute_function(global, fun, &solve_parameters(global, local, params), false); + } + None => { + info!("Not found variable ({}) in local scope", func_name) } - None => { info!("Not found variable ({}) in local scope", func_name)} } panic!("Can't find a callable object called ({})", func_name) } @@ -317,34 +352,48 @@ fn box_expr(input: &Vec) -> Vec> { } // This function is not lazy enough -fn lookup_local_variable(global: &GlobalState, local: &HashMap, v: &str) -> ExprAST { +fn lookup_local_variable( + global: &GlobalState, + local: &HashMap, + v: &str, +) -> ExprAST { let x = match local.get(v) { Some(a) => a, - None =>{ panic!("Not found variable ({}) in local scope", v)} + None => { + panic!("Not found variable ({}) in local scope", v) + } }; let x = x.clone(); // let mut dirty = false; let result = match x { - ExprAST::Int(_) | ExprAST::Bool(_) | ExprAST::StringLiteral(_)=> { x }, + ExprAST::Int(_) | ExprAST::Bool(_) | ExprAST::StringLiteral(_) => x, ExprAST::Variable(_) => { // dirty = true; solve(global, local, &x) } - ExprAST::Block(_) => {todo!()} - ExprAST::If(_) => {todo!()} + ExprAST::Block(_) => { + todo!() + } + ExprAST::If(_) => { + todo!() + } ExprAST::CallCallableObjectByname(func_name, params) => { let rp = solve_parameters(global, local, ¶ms); ExprAST::CallCallableObjectByname(func_name.to_owned(), box_expr(&rp)) } - ExprAST::Error => {todo!()} + ExprAST::Error => { + todo!() + } ExprAST::CallBuiltinFunction(func_name, params) => { let rp = solve_parameters(global, local, ¶ms); ExprAST::CallBuiltinFunction(func_name.to_owned(), box_expr(&rp)) } - ExprAST::Callable(co) => {ExprAST::Callable(co.clone())} - ExprAST::List(_) => { x } - ExprAST::Closure(_) => { todo!() } + ExprAST::Callable(co) => ExprAST::Callable(co.clone()), + ExprAST::List(_) => x, + ExprAST::Closure(_) => { + todo!() + } }; // if dirty { @@ -356,10 +405,11 @@ fn lookup_local_variable(global: &GlobalState, local: &HashMap, result } -fn solve_parameters(global: &GlobalState, - local: &HashMap, - params: &Vec>) - -> Vec{ +fn solve_parameters( + global: &GlobalState, + local: &HashMap, + params: &Vec>, +) -> Vec { let mut solved = Vec::with_capacity(params.len()); for p in params { let rp = solve(global, local, p); @@ -368,8 +418,7 @@ fn solve_parameters(global: &GlobalState, solved } - -fn process_global_functions(prog: &ProgramAST) -> HashMap { +fn process_global_functions(prog: &ProgramAST) -> HashMap { let mut result = HashMap::new(); for func in &prog.functions { @@ -385,7 +434,6 @@ fn process_global_functions(prog: &ProgramAST) -> HashMap { result } - pub enum IroncamelFileInfo { FileRead(BufReader), FileWrite(std::fs::File), @@ -393,6 +441,5 @@ pub enum IroncamelFileInfo { Stdout, } - // I think using enum in rust is better than using Java-like interfaces // At interpreter level, everything is almost expr diff --git a/src/lib.rs b/src/lib.rs index ee0849f..d8bf633 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ extern crate core; -pub mod tokenizer; -pub mod parser; -pub mod expr; -pub mod pipeline; -pub mod interpreter; -pub mod debug_output; mod builtin; +pub mod debug_output; +pub mod expr; pub mod gen_ir; +pub mod interpreter; +pub mod parser; +pub mod pipeline; +pub mod tokenizer; // pub mod runtime::builtin; // pub mod runtime; diff --git a/src/main.rs b/src/main.rs index 7f50cab..c4b7cc8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ -use std::fs; -use log::{debug, error, info}; +use clap::Parser; use ironcamel::pipeline; +use log::{debug, error, info}; +use std::fs; use std::io::Write; -use clap::Parser; - enum RunMode { AdHoc, @@ -17,7 +16,7 @@ struct Args { #[clap(short, long)] run: Option, - #[clap(short,long)] + #[clap(short, long)] compile: Option, /// Libaraies to be included @@ -25,8 +24,8 @@ struct Args { include: Vec, } -fn read_source_code(args: &Args)->(RunMode, String){ - if (!args.run.is_none()) && (!args.compile.is_none()) { +fn read_source_code(args: &Args) -> (RunMode, String) { + if (!args.run.is_none()) && (!args.compile.is_none()) { panic!("We can't define both --run and --compile"); } if !args.run.is_none() { @@ -48,8 +47,12 @@ fn main() { writeln!( buf, "{}:{} [{}] - {}", - record.file().unwrap_or("unknown") - .replace("src","").replace("\\","").replace("/",""), + record + .file() + .unwrap_or("unknown") + .replace("src", "") + .replace("\\", "") + .replace("/", ""), record.line().unwrap_or(0), record.level(), record.args() @@ -64,8 +67,12 @@ fn main() { let mut source_vec = Vec::with_capacity(args.include.len() + 1); for lib_path in &args.include { match fs::read_to_string(&lib_path) { - Ok(s) => { source_vec.push(s); } - Err(e) => { error!("Read lib {} failed: {}, skipping\n", lib_path, e) } + Ok(s) => { + source_vec.push(s); + } + Err(e) => { + error!("Read lib {} failed: {}, skipping\n", lib_path, e) + } } } let (run_mode, main_code) = read_source_code(&args); @@ -85,14 +92,11 @@ fn main() { match run_mode { RunMode::AdHoc => { ironcamel::interpreter::eval(&ast); - }, + } RunMode::CompileToLLVMIR => { info!("to compile to llvm IR"); let ir = ironcamel::gen_ir::compile(&ast); println!("{}", &ir); - }, + } } - - } - diff --git a/src/parser.rs b/src/parser.rs index 43e65fb..739fa53 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,11 @@ -use log::{debug, info, warn}; use crate::debug_output::build_statement_debug_strings; use crate::expr::{ExprAST, try_read_expr}; use crate::tokenizer::Token; -use crate::tokenizer::Token::{IdentifierToken, KeywordFn, KeywordLet, LeftCurlyBracket, LeftParentheses, OperatorAssign, RightCurlyBracket, RightParentheses, Semicolon, SpaceToken}; +use crate::tokenizer::Token::{ + IdentifierToken, KeywordFn, KeywordLet, LeftCurlyBracket, LeftParentheses, OperatorAssign, + RightCurlyBracket, RightParentheses, Semicolon, SpaceToken, +}; +use log::{debug, info, warn}; pub const DEBUG_TREE_INDENT: &'static str = "|-- "; const INVALID_PLACEHOLDER: &str = "stub"; @@ -11,25 +14,25 @@ pub trait AST { } pub struct ProgramAST { - pub functions : Vec + pub functions: Vec, } #[derive(Clone)] pub struct FunctionAST { - pub function_name : String, + pub function_name: String, pub arguments: Vec, - pub statements : Vec, - pub return_expr: Box + pub statements: Vec, + pub return_expr: Box, } #[derive(Clone)] pub struct BlockAST { - pub statements : Vec, - pub return_expr: Box + pub statements: Vec, + pub return_expr: Box, } pub fn function2block(ast: FunctionAST) -> BlockAST { BlockAST { statements: ast.statements, - return_expr: ast.return_expr + return_expr: ast.return_expr, } } #[derive(Clone)] @@ -38,11 +41,9 @@ pub enum StatementAST { Read(ReadAst), Write(WriteAst), FileOpen(FileOpenAst), - Error + Error, } - - pub fn build_ast(tokens: &Vec) -> ProgramAST { warn!("Building ast"); debug!("{:?}", tokens); @@ -50,7 +51,7 @@ pub fn build_ast(tokens: &Vec) -> ProgramAST { let mut pos = 0; while pos < tokens.len() { if tokens[pos] == SpaceToken { - pos +=1 ; + pos += 1; continue; } let (fun_ast, len) = read_function(tokens, pos); @@ -58,7 +59,7 @@ pub fn build_ast(tokens: &Vec) -> ProgramAST { functions.push(fun_ast); pos += len; } - ProgramAST{functions} + ProgramAST { functions } } fn read_function(tokens: &Vec, pos: usize) -> (FunctionAST, usize) { @@ -68,10 +69,13 @@ fn read_function(tokens: &Vec, pos: usize) -> (FunctionAST, usize) { len += 1; let function_name; - if let IdentifierToken(name) = &tokens[pos+len] { + if let IdentifierToken(name) = &tokens[pos + len] { function_name = name; } else { - panic!("Expect IdentfierToken for function name, got {:?}", tokens[pos+len]); + panic!( + "Expect IdentfierToken for function name, got {:?}", + tokens[pos + len] + ); } debug!("Function name is {}", function_name); len += 1; @@ -79,47 +83,53 @@ fn read_function(tokens: &Vec, pos: usize) -> (FunctionAST, usize) { assert_eq!(tokens[pos + len], LeftParentheses); len += 1; - let (arguments, len_args) = read_argument_list(tokens, pos+len); + let (arguments, len_args) = read_argument_list(tokens, pos + len); len += len_args; debug!("Get argument list {:?}, consumed {}", &arguments, len_args); assert_eq!(tokens[pos + len], RightParentheses); len += 1; - - - let (block, block_len) = read_block(tokens, pos+len); + let (block, block_len) = read_block(tokens, pos + len); len += block_len; - let fun = FunctionAST{ + let fun = FunctionAST { function_name: function_name.clone(), arguments, - statements : block.statements, - return_expr: block.return_expr + statements: block.statements, + return_expr: block.return_expr, }; info!("Read a function \n{:?}", fun.debug_strings()); (fun, len) - } pub fn read_argument_list(tokens: &Vec, pos: usize) -> (Vec, usize) { let mut result = Vec::new(); let mut len = 0; 'each_token: loop { - debug!("Try {:?} for read argument list, pos={}, len={}", tokens[pos+len], pos, len); - if tokens[pos+len] == RightParentheses - || tokens[pos+len] == Token::VerticalBar{ + debug!( + "Try {:?} for read argument list, pos={}, len={}", + tokens[pos + len], + pos, + len + ); + if tokens[pos + len] == RightParentheses || tokens[pos + len] == Token::VerticalBar { break 'each_token; } match &tokens[pos + len] { Token::SpaceToken => (), Token::Comma => (), - Token::IdentifierToken(id) => { - debug!("Find a Identifier {:?}", tokens[pos+len]); + Token::IdentifierToken(id) => { + debug!("Find a Identifier {:?}", tokens[pos + len]); result.push(id.to_owned()) - }, - _ => { panic!("Unexpected token when reading argument list {:?}", tokens[pos+len]) } + } + _ => { + panic!( + "Unexpected token when reading argument list {:?}", + tokens[pos + len] + ) + } }; len += 1; } @@ -132,51 +142,67 @@ pub(crate) fn read_block(tokens: &Vec, pos: usize) -> (BlockAST, usize) { assert_eq!(tokens[pos + len], LeftCurlyBracket); len += 1; - let mut statements: Vec = Vec::new(); loop { - let (statement, sta_len) = try_read_statement_ast(tokens, pos+len); + let (statement, sta_len) = try_read_statement_ast(tokens, pos + len); match sta_len { None => break, - _ => () + _ => (), } // let statement = statement.unwrap(); let sta_len = sta_len.unwrap(); statements.push(statement); assert!(sta_len > 0); - debug!("The statement consumed {} tokens: {:?}", - sta_len, &tokens[pos+len..pos+len+sta_len]); + debug!( + "The statement consumed {} tokens: {:?}", + sta_len, + &tokens[pos + len..pos + len + sta_len] + ); len += sta_len; } - let (return_expr, return_val_len) = try_read_expr(tokens, pos+len); + let (return_expr, return_val_len) = try_read_expr(tokens, pos + len); match &return_val_len { - None => panic!("This block has no return expression! Got {:?}", tokens[pos+len]), + None => panic!( + "This block has no return expression! Got {:?}", + tokens[pos + len] + ), Some(_) => (), } len += return_val_len.unwrap(); assert_eq!(tokens[pos + len], RightCurlyBracket); len += 1; - let block = BlockAST{ statements, return_expr: Box::new(return_expr) }; + let block = BlockAST { + statements, + return_expr: Box::new(return_expr), + }; (block, len) } -fn generate_stub_statement() -> (StatementAST, Option) { (StatementAST::Error, None) } +fn generate_stub_statement() -> (StatementAST, Option) { + (StatementAST::Error, None) +} fn try_read_statement_ast(tokens: &Vec, pos: usize) -> (StatementAST, Option) { // Try read an assignment let (assignment, len) = try_read_let_binding(tokens, pos); match len { Some(_) => return (StatementAST::Bind(assignment), len), - None => { debug!("Not an assignment"); ()} + None => { + debug!("Not an assignment"); + () + } }; let (io, len) = try_read_io_operation(tokens, pos); match len { Some(_) => { info!("IO Operation: {:?}", build_statement_debug_strings(&io)); return (io, len); - }, - None => { debug!("Not IO"); ()} + } + None => { + debug!("Not IO"); + () + } }; debug!("Not a statement"); @@ -186,84 +212,104 @@ fn try_read_statement_ast(tokens: &Vec, pos: usize) -> (StatementAST, Opt // Read operation would consume exactly 6 tokens // Write operation would consume 5 tokens, then read an expression, finally a semicolon fn try_read_io_operation(tokens: &Vec, pos: usize) -> (StatementAST, Option) { - if tokens.len() - pos < 6 { return generate_stub_statement(); } + if tokens.len() - pos < 6 { + return generate_stub_statement(); + } let procedure = match &tokens[pos] { IdentifierToken(s) => s, - _ => { debug!("Not IO operation"); return generate_stub_statement();} + _ => { + debug!("Not IO operation"); + return generate_stub_statement(); + } }; - if tokens[pos+1] != Token::AddressSign { return generate_stub_statement(); } - let file_handler = match &tokens[pos+2] { + if tokens[pos + 1] != Token::AddressSign { + return generate_stub_statement(); + } + let file_handler = match &tokens[pos + 2] { IdentifierToken(s) => s, - _ => { panic!("Expect a file handle after @"); } + _ => { + panic!("Expect a file handle after @"); + } }; - match &tokens[pos+3] { + match &tokens[pos + 3] { //read Token::OperatorGetFrom => { - let var = match &tokens[pos+4] { + let var = match &tokens[pos + 4] { IdentifierToken(s) => s, - _ => { panic!("Expect a new variable"); } + _ => { + panic!("Expect a new variable"); + } }; - let result = ReadAst{ - impure_procedure_name : procedure.to_owned(), - file_handler : file_handler.to_owned(), - write_to_variable: var.to_owned() + let result = ReadAst { + impure_procedure_name: procedure.to_owned(), + file_handler: file_handler.to_owned(), + write_to_variable: var.to_owned(), }; - assert_eq!(tokens[pos+5], Token::Semicolon); + assert_eq!(tokens[pos + 5], Token::Semicolon); debug!("Reading to var {}", var); (StatementAST::Read(result), Some(6)) - }, + } //write Token::OperatorPutTo => { let mut len = 4; let (expr, expr_len) = try_read_expr(tokens, pos + len); let expr_len = match expr_len { Some(x) => x, - None => { panic!("Expect a valid expr when write!")} + None => { + panic!("Expect a valid expr when write!") + } }; len += expr_len; - assert_eq!(tokens[pos+len], Token::Semicolon); + assert_eq!(tokens[pos + len], Token::Semicolon); len += 1; let result = WriteAst { - impure_procedure_name : procedure.to_owned(), - file_handler : file_handler.to_owned(), - expr: Box::from(expr) + impure_procedure_name: procedure.to_owned(), + file_handler: file_handler.to_owned(), + expr: Box::from(expr), }; info!("Write io"); (StatementAST::Write(result), Some(len)) - }, + } Token::OperatorAssign => { - let filepath = match &tokens[pos+4] { + let filepath = match &tokens[pos + 4] { Token::LiteralString(s) => s, - _ => { panic!("Expect a Strin"); } + _ => { + panic!("Expect a Strin"); + } }; - let result = FileOpenAst{ - impure_procedure_name : procedure.to_owned(), - file_handler : file_handler.to_owned(), - file_path: filepath.to_owned() + let result = FileOpenAst { + impure_procedure_name: procedure.to_owned(), + file_handler: file_handler.to_owned(), + file_path: filepath.to_owned(), }; - assert_eq!(tokens[pos+5], Token::Semicolon); + assert_eq!(tokens[pos + 5], Token::Semicolon); (StatementAST::FileOpen(result), Some(6)) } - _ => { panic!("Expect << or >> or =, got {:?}", &tokens[pos+3]); } + _ => { + panic!("Expect << or >> or =, got {:?}", &tokens[pos + 3]); + } } } fn generate_invalid_let_binding_ast() -> LetBindingAST { - LetBindingAST{ variable: INVALID_PLACEHOLDER.to_string(), expr: Box::new(ExprAST::Error) } + LetBindingAST { + variable: INVALID_PLACEHOLDER.to_string(), + expr: Box::new(ExprAST::Error), + } } fn try_read_let_binding(tokens: &Vec, pos: usize) -> (LetBindingAST, Option) { debug!("try assignment {:?}", tokens[pos]); let mut len = 0; let stub = generate_invalid_let_binding_ast(); - if tokens[pos+len] != KeywordLet { + if tokens[pos + len] != KeywordLet { return (stub, None); } len += 1; let var_name; - if let IdentifierToken(name) = &tokens[pos+len] { + if let IdentifierToken(name) = &tokens[pos + len] { var_name = name; debug!("identifier for assign {:?}", var_name); } else { @@ -271,39 +317,37 @@ fn try_read_let_binding(tokens: &Vec, pos: usize) -> (LetBindingAST, Opti } len += 1; - if tokens[pos+len] != OperatorAssign { + if tokens[pos + len] != OperatorAssign { return (stub, None); } len += 1; - let (expr, expr_len) = try_read_expr(tokens, pos + len); match expr_len { None => { info!("Not a valid expression"); return (stub, None); - }, - _ => () + } + _ => (), }; let expr_len = expr_len.unwrap(); len += expr_len; - if tokens[pos+len] != Semicolon { + if tokens[pos + len] != Semicolon { return (stub, None); } len += 1; let assignment = LetBindingAST { - variable : var_name.clone(), - expr: Box::new(expr) + variable: var_name.clone(), + expr: Box::new(expr), }; (assignment, Some(len)) } - #[derive(Clone)] pub struct LetBindingAST { pub variable: String, - pub expr : Box + pub expr: Box, } /* More formally, I should call it impure function. @@ -313,17 +357,17 @@ However, I would make users safe to assume that all functions are pure pub struct ReadAst { pub impure_procedure_name: String, pub file_handler: String, - pub write_to_variable: String + pub write_to_variable: String, } #[derive(Clone)] pub struct WriteAst { pub impure_procedure_name: String, pub file_handler: String, - pub expr: Box + pub expr: Box, } #[derive(Clone)] pub struct FileOpenAst { pub impure_procedure_name: String, pub file_handler: String, - pub file_path: String -} \ No newline at end of file + pub file_path: String, +} diff --git a/src/tokenizer.rs b/src/tokenizer.rs index da5a451..5c16997 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1,9 +1,8 @@ -use std::iter::FromIterator; +use log::{debug, info}; use phf::phf_map; -use log::{info,debug}; - +use std::iter::FromIterator; -#[derive(Clone,PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug)] pub enum Token { //Bracket LeftParentheses, @@ -20,21 +19,18 @@ pub enum Token { KeywordThen, KeywordElse, - // OperatorEqual, OperatorAssign, VerticalBar, Semicolon, Comma, - AddressSign, //@ - OperatorPutTo, // << + AddressSign, //@ + OperatorPutTo, // << OperatorGetFrom, // >> // https://stackoverflow.com/a/42730916/1166518 - IdentifierToken(String), - Integer64(i64), LiteralString(String), LiteralTrue, @@ -51,7 +47,7 @@ use crate::builtin::ARITHMETIC_OPERATORS; pub fn convert_source_to_tokens(code: &str) -> Vec { let mut result = Vec::new(); let mut pos = 0; - let code_vec:Vec = code.chars().collect(); + let code_vec: Vec = code.chars().collect(); while pos < code.len() { let len = skip_line_comment(&code_vec, pos); pos += len; @@ -66,14 +62,18 @@ pub fn convert_source_to_tokens(code: &str) -> Vec { } fn skip_line_comment(code: &Vec, pos: usize) -> usize { - if pos + 1 >= code.len() { return 0; } - if code[pos] == '/' && code[pos+1] == '/' { + if pos + 1 >= code.len() { + return 0; + } + if code[pos] == '/' && code[pos + 1] == '/' { let mut len = 2; - while pos + len < code.len() && code[pos+len] != '\n' { + while pos + len < code.len() && code[pos + len] != '\n' { len += 1; } len - } else { 0 } + } else { + 0 + } } // Return: length of the token, the token @@ -81,16 +81,14 @@ fn read_next_token(code: &Vec, pos: usize) -> (usize, Token) { assert!(pos < code.len()); match read_next_bracket(code[pos]) { - None => (), - Some(e) => return (1, e), + None => (), + Some(e) => return (1, e), }; match read_next_space(code[pos]) { None => (), Some(e) => return (1, e), }; - - let (len, keyword) = read_next_keyword(code, pos); if keyword.is_some() { return (len, keyword.unwrap()); @@ -120,14 +118,19 @@ fn read_next_string(code: &Vec, pos: usize) -> (usize, Option) { return (0, None); } let mut prim_len = 1; - while code[pos+prim_len] != '\"' { + while code[pos + prim_len] != '\"' { prim_len += 1; } - let str_slice = &code[pos+1..pos+prim_len]; + let str_slice = &code[pos + 1..pos + prim_len]; let result = String::from_iter(str_slice); prim_len += 1; let result = process_backslach_in_string_literal(result); - info!("Got String {}, consumed {} chars, string len {}", result, prim_len, result.len()); + info!( + "Got String {}, consumed {} chars, string len {}", + result, + prim_len, + result.len() + ); let token = Token::LiteralString(result); (prim_len, Some(token)) } @@ -137,40 +140,40 @@ fn process_backslach_in_string_literal(s: String) -> String { let s = s.replace("\\t", "\t"); let s = s.replace("\\n", "\n"); s - } - fn read_next_integer(code: &Vec, pos: usize) -> (usize, Option) { let mut prim_len = 0; let mut result: Vec = Vec::new(); // TODO no overflow detect - while pos + prim_len < code.len() && code[pos+prim_len].is_digit(10) { - result.push(code[pos+prim_len] as u8); + while pos + prim_len < code.len() && code[pos + prim_len].is_digit(10) { + result.push(code[pos + prim_len] as u8); prim_len += 1; } if result.len() == 0 { return (0, None); } - assert!(result.len()>0); + assert!(result.len() > 0); if result[0] == '0' as u8 { assert_eq!(result.len(), 1); //TODO hex support return (1, Some(Token::Integer64(0))); } - let num:i64 = atoi::atoi(&result).unwrap(); + let num: i64 = atoi::atoi(&result).unwrap(); return (result.len(), Some(Token::Integer64(num))); } - - fn read_next_identifier(code: &Vec, pos: usize) -> (usize, Option) { - assert!(is_valid_identifier_first_letter(code[pos]), "got {}", code[pos]); + assert!( + is_valid_identifier_first_letter(code[pos]), + "got {}", + code[pos] + ); let mut result = Vec::new(); let mut len = 0; result.push(code[pos + len]); len += 1; - while pos + len < code.len() && is_valid_identifier_second_letter(code[pos+len]) { + while pos + len < code.len() && is_valid_identifier_second_letter(code[pos + len]) { result.push(code[pos + len]); len += 1; } @@ -181,21 +184,21 @@ fn read_next_identifier(code: &Vec, pos: usize) -> (usize, Option) } fn is_valid_identifier_second_letter(c: char) -> bool { - if is_valid_identifier_first_letter(c){ + if is_valid_identifier_first_letter(c) { return true; } match c { - '0' ..= '9' => true, - _ => false + '0'..='9' => true, + _ => false, } } -fn is_valid_identifier_first_letter(c:char) -> bool { +fn is_valid_identifier_first_letter(c: char) -> bool { match c { - 'a' ..= 'z' => true, - 'A' ..= 'Z' => true, - '_' => true, - _ => false + 'a'..='z' => true, + 'A'..='Z' => true, + '_' => true, + _ => false, } } fn read_next_arithmetic_operator(code: &Vec, pos: usize) -> (usize, Option) { @@ -205,36 +208,40 @@ fn read_next_arithmetic_operator(code: &Vec, pos: usize) -> (usize, Option continue; } let keyword_len = op.len(); - let code_head = &code[pos.. pos + keyword_len]; + let code_head = &code[pos..pos + keyword_len]; let code_head_str: String = code_head.iter().collect(); assert_eq!(code_head_str.len(), keyword_len); if code_head_str == *op { - return (keyword_len, Some( Token::IdentifierToken(String::from(*op)) ) ); + return (keyword_len, Some(Token::IdentifierToken(String::from(*op)))); } } - return (0, None) + return (0, None); } -fn get_next_token_in_map(code:&Vec, pos:usize, map:&phf::Map<&'static str, Token>) -> (usize, Option) { +fn get_next_token_in_map( + code: &Vec, + pos: usize, + map: &phf::Map<&'static str, Token>, +) -> (usize, Option) { for (key, value) in map.entries() { let literal: &str = *key; if remained_chars(code, pos) < literal.len() { continue; } let keyword_len = literal.len(); - let code_head = &code[pos.. pos + keyword_len]; + let code_head = &code[pos..pos + keyword_len]; assert_eq!(code_head.len(), keyword_len); let code_head_str: String = code_head.iter().collect(); assert_eq!(code_head_str.len(), keyword_len); if code_head_str == literal { - return (keyword_len, Some( (*value).clone() ) ); + return (keyword_len, Some((*value).clone())); } } - return (0, None) + return (0, None); } static KEYWORDS: phf::Map<&'static str, Token> = phf_map! { "fn" => Token::KeywordFn, @@ -262,7 +269,10 @@ fn read_next_operator(code: &Vec, pos: usize) -> (usize, Option) { get_next_token_in_map(code, pos, &OPERATORS) } -fn read_next_operator_or_arithmetic_operator(code: &Vec, pos: usize) -> (usize, Option) { +fn read_next_operator_or_arithmetic_operator( + code: &Vec, + pos: usize, +) -> (usize, Option) { // Compare the length seems to be a bit hacky let (len, identifier) = read_next_arithmetic_operator(code, pos); let (len_op, op) = read_next_operator(code, pos); @@ -280,7 +290,7 @@ fn read_next_operator_or_arithmetic_operator(code: &Vec, pos: usize) -> (u } } -fn read_next_bracket(c:char) -> Option { +fn read_next_bracket(c: char) -> Option { match c { '{' => Some(LeftCurlyBracket), '}' => Some(RightCurlyBracket), @@ -292,15 +302,14 @@ fn read_next_bracket(c:char) -> Option { } } -fn read_next_space(c:char) -> Option { +fn read_next_space(c: char) -> Option { match c { - ' ' | '\n' | '\t' | '\r' => Some(SpaceToken), + ' ' | '\n' | '\t' | '\r' => Some(SpaceToken), _ => None, } } - fn remained_chars(code: &Vec, pos: usize) -> usize { assert!(pos < code.len()); code.len() - pos -} \ No newline at end of file +}