From 2d7f302d427f9f4e5b11a50b63b5236716a126dd Mon Sep 17 00:00:00 2001 From: MCorange Date: Thu, 13 Apr 2023 04:57:28 +0300 Subject: [PATCH] Added function features Added features: - Exported functions (very experimental and disabled by default with hardcoded toggle) - Inline functions --- src/compile/linux_x86_64.rs | 94 ++++++++++++++++++++++++++----- src/config.rs | 8 ++- src/constants.rs | 27 +++++---- src/interpret/linux_x86_64/mod.rs | 10 ++-- src/main.rs | 3 + src/parser.rs | 2 +- src/preprocessor.rs | 65 ++++++++++++++++++++- src/typechecker.rs | 20 ++++--- test.mcl | 13 +++-- 9 files changed, 199 insertions(+), 43 deletions(-) diff --git a/src/compile/linux_x86_64.rs b/src/compile/linux_x86_64.rs index 79418af..dc12d42 100644 --- a/src/compile/linux_x86_64.rs +++ b/src/compile/linux_x86_64.rs @@ -1,5 +1,5 @@ use std::{fs, path::PathBuf, io::{Write, BufWriter}, collections::HashMap}; -use crate::{constants::{Operator, OpType, KeywordType}, Args}; +use crate::{constants::{Operator, OpType, KeywordType}, Args, warn, error, lerror}; use color_eyre::Result; use crate::compile::commands::linux_x86_64_compile_and_link; use crate::constants::InstructionType; @@ -25,6 +25,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ of_o.set_extension("o"); of_a.set_extension("nasm"); + let mut should_push_ret = false; let file = fs::File::create(&of_a)?; let mut writer = BufWriter::new(&file); @@ -71,11 +72,14 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ writeln!(writer, " add rsp, 40")?; writeln!(writer, " ret")?; - writeln!(writer, "global _start")?; - writeln!(writer, "_start:")?; - writeln!(writer, " lea rbp, [rel ret_stack]")?; - writeln!(writer, " call main")?; - writeln!(writer, " jmp end")?; + if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode { + writeln!(writer, "global _start")?; + writeln!(writer, "_start:")?; + writeln!(writer, " lea rbp, [rel ret_stack]")?; + writeln!(writer, " call main")?; + writeln!(writer, " jmp end")?; + + } let mut ti = 0; @@ -416,6 +420,12 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ ti += 1; }, InstructionType::Return => { + + if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret { + writeln!(writer, " pop rdx")?; + should_push_ret = false; + } + writeln!(writer, " sub rbp, 8")?; writeln!(writer, " mov rbx, qword [rbp]")?; writeln!(writer, " push rbx")?; @@ -430,7 +440,6 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ InstructionType::TypePtr | InstructionType::TypeInt | InstructionType::TypeVoid | - InstructionType::TypeStr | InstructionType::TypeAny | InstructionType::Returns | InstructionType::With => { @@ -501,6 +510,12 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ ti += 1; }, KeywordType::FunctionDone => { + + if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret { + writeln!(writer, " pop rdx")?; + should_push_ret = false; + } + writeln!(writer, " sub rbp, 8")?; writeln!(writer, " mov rbx, qword [rbp]")?; writeln!(writer, " push rbx")?; @@ -510,18 +525,71 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result{ KeywordType::FunctionThen => ti += 1, KeywordType::Function | KeywordType::Include | - KeywordType::FunctionDefInline | + KeywordType::Inline | + KeywordType::Export | KeywordType::Constant => unreachable!(), - KeywordType::Inline => todo!(), + KeywordType::FunctionDefExported => { + + if !crate::config::ENABLE_EXPORTED_FUNCTIONS { + lerror!(&token.loc, "Experimental feature 'exported functions' is not enabled"); + return Err(eyre!("")); + } + + writeln!(writer, "global {}", token.text)?; + writeln!(writer, "{}:", token.text)?; + + writeln!(writer, " pop rbx")?; + writeln!(writer, " mov qword [rbp], rbx")?; + writeln!(writer, " add rbp, 8")?; + warn!("External functions are highly experimental and should be treated as such"); + if token.types.0 == 0 { + writeln!(writer, " ; no arguments")?; + } else { + if token.types.0 >= 1 { + writeln!(writer, " push rdi")?; + } + if token.types.0 >= 2 { + writeln!(writer, " push rsi")?; + } + if token.types.0 >= 3 { + writeln!(writer, " push rdx")?; + } + if token.types.0 >= 4 { + writeln!(writer, " push rcx")?; + } + if token.types.0 >= 5 { + writeln!(writer, " push r8")?; + } + if token.types.0 >= 6 { + writeln!(writer, " push r9")?; + } + if token.types.0 >= 7 { + lerror!(&token.loc, "More than 6 arguments in an external function is not supported"); + return Err(eyre!("")); + } + } + + if token.types.1 == 1 { + should_push_ret = true; + } else if token.types.1 > 1 { + lerror!(&token.loc, "More than 1 return arguments in an external function is not supported"); + return Err(eyre!("")); + } + + functions.push(Function { loc: token.loc.clone(), name: token.text.clone(), exter: false}); + ti += 1; + }, } } } } writeln!(writer, "addr_{ti}:")?; - writeln!(writer, "end:")?; - writeln!(writer, " mov rax, 60")?; - writeln!(writer, " mov rdi, 0")?; - writeln!(writer, " syscall")?; + if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode { + writeln!(writer, "end:")?; + writeln!(writer, " mov rax, 60")?; + writeln!(writer, " mov rdi, 0")?; + writeln!(writer, " syscall")?; + } writeln!(writer, "segment .data")?; for (i, s) in strings.iter().enumerate() { let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::>(); diff --git a/src/config.rs b/src/config.rs index 55ff8b4..c6c1b77 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,4 +17,10 @@ pub const DEFAULT_INCLUDES: [&str;1] = [ * if you have buffer overflow consider increasing these */ pub const MEM_SZ: usize = 640 * 1000; // 4kb -pub const STRING_SZ: usize = 640 * 1000; // 4kb \ No newline at end of file +pub const STRING_SZ: usize = 640 * 1000; // 4kb + + +/** + * Experimental options + */ +pub const ENABLE_EXPORTED_FUNCTIONS: bool = false; \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index b87b3f1..9027aa6 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -58,7 +58,7 @@ pub enum InstructionType { TypePtr, TypeInt, TypeVoid, - TypeStr, + // TypeStr, TypeAny, Returns, With, @@ -84,10 +84,11 @@ pub enum KeywordType { ConstantDef, Function, FunctionDef, - FunctionDefInline, + FunctionDefExported, FunctionThen, FunctionDone, - Inline + Inline, + Export } #[derive(Debug, Clone, PartialEq)] @@ -104,7 +105,8 @@ pub struct Operator{ pub text: String, //? only used for OpType::PushStr pub addr: Option, //? only used for OpType::PushStr pub jmp: usize, - pub loc: Loc + pub loc: Loc, + pub types: (usize, usize) } impl Operator { @@ -117,13 +119,19 @@ impl Operator { text, loc: (file, row, col), tok_typ, + types: (0, 0) } } - pub fn set_addr(mut self, addr: usize) -> Self { + pub fn set_addr(&mut self, addr: usize) -> Self { self.addr = Some(addr); - self + (*self).clone() } + // pub fn set_types(&mut self, args: usize, rets: usize) -> Self { + // self.types = (args, rets); + // (*self).clone() + // } + } impl OpType { @@ -180,7 +188,6 @@ impl OpType { InstructionType::TypePtr => "ptr", InstructionType::TypeInt => "int", InstructionType::TypeVoid => "void", - InstructionType::TypeStr => "str", InstructionType::Returns => "returns", InstructionType::With => "with", InstructionType::TypeAny => "any", @@ -201,8 +208,9 @@ impl OpType { KeywordType::FunctionDone => "done", KeywordType::ConstantDef => "constant Definition (internal)", KeywordType::FunctionDef => "function definition (internal)", - KeywordType::FunctionDefInline => "inline function definition (internal)", - KeywordType::Inline => "inline" + KeywordType::FunctionDefExported => "extern function definition (internal)", + KeywordType::Inline => "inline", + KeywordType::Export => "export", } } @@ -259,7 +267,6 @@ pub enum Types { Ptr, Int, Void, - Str, Any // U8, // U16, diff --git a/src/interpret/linux_x86_64/mod.rs b/src/interpret/linux_x86_64/mod.rs index 08f89d4..62c686e 100644 --- a/src/interpret/linux_x86_64/mod.rs +++ b/src/interpret/linux_x86_64/mod.rs @@ -328,7 +328,6 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result{ InstructionType::TypePtr | InstructionType::TypeInt | InstructionType::TypeVoid | - InstructionType::TypeStr | InstructionType::TypeAny | InstructionType::Returns | InstructionType::With => ip += 1, @@ -362,7 +361,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result{ KeywordType::While | //* exept this one, this one should just skip over KeywordType::Memory | KeywordType::FunctionDef | - KeywordType::FunctionDefInline | + KeywordType::FunctionDefExported | KeywordType::ConstantDef => { //? Disabled since we now pre run the whole program // constants.insert(op.text.clone(), Constant { loc: op.loc.clone(), name: op.text.clone(), value_i: Some(op.value), value_s: None, used: false }); @@ -380,6 +379,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result{ KeywordType::Constant | KeywordType::Function | KeywordType::Inline | + KeywordType::Export | KeywordType::Include => unreachable!(), } } @@ -409,8 +409,10 @@ pub fn pre_run(ops: &[Operator]) -> Defineds { OpType::Keyword(KeywordType::Memory) => { defineds.memories.insert(op.addr.unwrap(), Memory { size: op.value, loc: op.loc.clone(), id: op.addr.unwrap() }); }, - OpType::Keyword(KeywordType::FunctionDefInline) | - OpType::Keyword(KeywordType::FunctionDef) => { + OpType::Keyword(KeywordType::FunctionDefExported) => { + + } + OpType::Keyword(KeywordType::FunctionDef) => { defineds.functions.insert(op.text.clone(), Function { loc: op.loc.clone(), name: op.text.clone(), id: ip }); }, OpType::Keyword(KeywordType::ConstantDef) => { diff --git a/src/main.rs b/src/main.rs index 5aa2839..9de7469 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,6 +57,9 @@ pub struct Args { #[arg(long, short='O', default_value_t=String::from("0"))] optimisation: String, + // disables the main function + #[arg(long="lib")] + lib_mode: bool //#[arg(long, short='F')] //features: Vec, diff --git a/src/parser.rs b/src/parser.rs index 0fa7abe..61ffe43 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -207,6 +207,7 @@ pub fn lookup_word>(s: &str, _pos: P) -> OpType { "then" => OpType::Keyword(KeywordType::FunctionThen), "done" => OpType::Keyword(KeywordType::FunctionDone), "inline" => OpType::Keyword(KeywordType::Inline), + "export" => OpType::Keyword(KeywordType::Export), "return" => OpType::Instruction(InstructionType::Return), "returns" => OpType::Instruction(InstructionType::Returns), "bool" => OpType::Instruction(InstructionType::TypeBool), @@ -214,7 +215,6 @@ pub fn lookup_word>(s: &str, _pos: P) -> OpType { "ptr" => OpType::Instruction(InstructionType::TypePtr), "void" => OpType::Instruction(InstructionType::TypeVoid), "any" => OpType::Instruction(InstructionType::TypeAny), - "str" => OpType::Instruction(InstructionType::TypeStr), "with" => OpType::Instruction(InstructionType::With), _ => OpType::Instruction(InstructionType::None) } diff --git a/src/preprocessor.rs b/src/preprocessor.rs index 1a68f6b..360e47b 100644 --- a/src/preprocessor.rs +++ b/src/preprocessor.rs @@ -212,6 +212,7 @@ impl<'a> Preprocessor<'a> { if f_inline { + f_inline = false; let mut prog: Vec = Vec::new(); let mut depth = -1; while !rtokens.is_empty() { @@ -224,7 +225,6 @@ impl<'a> Preprocessor<'a> { InstructionType::TypeBool | InstructionType::TypeInt | InstructionType::TypePtr | - InstructionType::TypeStr | InstructionType::With | InstructionType::Returns | InstructionType::TypeVoid => { @@ -271,7 +271,52 @@ impl<'a> Preprocessor<'a> { tokens: Some(prog) }); + } else if f_extern { + f_extern = false; + self.functions.insert(name.text.clone(), Function{ + loc: name.loc.clone(), + name: name.text.clone(), + inline: false, + tokens: None + }); + let mut a: Vec = Vec::new(); + let mut fn_def = op.clone(); + a.push(rtokens.pop().unwrap()); + let mut ret = false; + while !rtokens.is_empty() { + let op = rtokens.pop().unwrap(); + // println!("{:?}",op); + a.push(op.clone()); + if op.typ == OpType::Instruction(InstructionType::Returns) { + ret = true; + } + + if op.typ == OpType::Keyword(KeywordType::FunctionThen) { + break; + } + + if op.typ == OpType::Instruction(InstructionType::TypeBool) || + op.typ == OpType::Instruction(InstructionType::TypeInt) || + op.typ == OpType::Instruction(InstructionType::TypePtr) { + + if ret { + fn_def.types.1 += 1; + } else { + fn_def.types.0 += 1; + } + } + } + + fn_def.typ = OpType::Keyword(KeywordType::FunctionDefExported); + fn_def.text = name.text; + // fn_def.set_types(args, rets); + // println!("{:?}", fn_def.types); + program.push(fn_def); + program.append(&mut a); + + } else { + self.functions.insert(name.text.clone(), Function{ loc: name.loc.clone(), name: name.text.clone(), @@ -357,7 +402,10 @@ impl<'a> Preprocessor<'a> { } OpType::Keyword(KeywordType::Inline) => { - if f_inline { + if f_extern { + lerror!(&op.loc, "Function is already marked as extern, function cannot be inline and extern at the same time"); + return Err(eyre!("")); + } else if f_inline { lerror!(&op.loc, "Function is already marked as inline, remove this inline Keyword"); return Err(eyre!("")); } else { @@ -365,6 +413,18 @@ impl<'a> Preprocessor<'a> { } } + OpType::Keyword(KeywordType::Export) => { + if f_inline { + lerror!(&op.loc, "Function is already marked as inline, function cannot be inline and extern at the same time"); + return Err(eyre!("")); + } else if f_extern { + lerror!(&op.loc, "Function is already marked as extern, remove this extern Keyword"); + return Err(eyre!("")); + } else { + f_extern = true; + } + } + _ => { program.push(op); } @@ -381,6 +441,7 @@ impl<'a> Preprocessor<'a> { f.typ != OpType::Instruction(InstructionType::FnCall) && f.typ != OpType::Instruction(InstructionType::MemUse) && f.typ != OpType::Keyword(KeywordType::FunctionDef) && + f.typ != OpType::Keyword(KeywordType::FunctionDefExported) && f.typ != OpType::Keyword(KeywordType::ConstantDef) && f.typ != OpType::Instruction(InstructionType::ConstUse) { lookup_word(&f.text, &f.loc) diff --git a/src/typechecker.rs b/src/typechecker.rs index 2716ded..1d256b0 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -62,10 +62,10 @@ pub fn typecheck(ops: Vec, args: &Args, init_types: Option> stack_pop(&mut stack, &op, &[Types::Bool])?; }, - KeywordType::FunctionDefInline | + KeywordType::FunctionDefExported | KeywordType::FunctionDef => { let name = op.text.clone(); - + // println!("{:?}", name); if let Some(p) = rtokens.pop() { if p.typ != OpType::Instruction(InstructionType::With){ lerror!(&op.loc, "Expected {:?}, got {:?}", OpType::Instruction(InstructionType::With), p.typ); @@ -106,8 +106,6 @@ pub fn typecheck(ops: Vec, args: &Args, init_types: Option> continue; } Types::Void - } else if op.typ == OpType::Instruction(InstructionType::TypeStr) { - Types::Str } else if op.typ == OpType::Instruction(InstructionType::TypeAny) { Types::Any } else { @@ -177,8 +175,12 @@ pub fn typecheck(ops: Vec, args: &Args, init_types: Option> }, KeywordType::FunctionThen | KeywordType::FunctionDone | - KeywordType::Function => unreachable!(), - KeywordType::Inline => (), + KeywordType::Inline | + KeywordType::Export | + KeywordType::Function => { + println!("{:?}", op); + unreachable!() + }, } }, OpType::Instruction(instruction) => { @@ -332,7 +334,10 @@ pub fn typecheck(ops: Vec, args: &Args, init_types: Option> InstructionType::FnCall => { stack_snapshots.push(stack.clone()); - let f = functions.get(&op.text).unwrap().clone(); + let f = if let Some(f) = functions.get(&op.text) {f} else { + lerror!(&op.loc, "Could not find function {}", op.text); + return Err(eyre!("")); + }; // in_function = (op.text.clone(), f.clone(), op.loc.clone()); @@ -362,7 +367,6 @@ pub fn typecheck(ops: Vec, args: &Args, init_types: Option> InstructionType::TypeInt | InstructionType::TypeVoid | InstructionType::TypeAny | - InstructionType::TypeStr | InstructionType::Returns | InstructionType::With => (), InstructionType::ConstUse => { diff --git a/test.mcl b/test.mcl index c6336ff..4e424cf 100644 --- a/test.mcl +++ b/test.mcl @@ -1,7 +1,12 @@ // include "std.mcl" - -fn main with void returns void then - 69 test +fn mcl_print with int ptr returns void then + 1 1 syscall3 drop done -inline fn test with void returns void then _dbg_print done \ No newline at end of file +fn mcl_dump with int returns void then + _dbg_print +done + +fn main with void returns void then + "hi\n" mcl_print +done