Added function features

Added features:
 - Exported functions (very experimental and disabled by default with
   hardcoded toggle)
 - Inline functions
This commit is contained in:
MCorange 2023-04-13 04:57:28 +03:00
parent c8547af24c
commit 2d7f302d42
9 changed files with 199 additions and 43 deletions

View File

@ -1,5 +1,5 @@
use std::{fs, path::PathBuf, io::{Write, BufWriter}, collections::HashMap}; 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 color_eyre::Result;
use crate::compile::commands::linux_x86_64_compile_and_link; use crate::compile::commands::linux_x86_64_compile_and_link;
use crate::constants::InstructionType; use crate::constants::InstructionType;
@ -25,6 +25,7 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
of_o.set_extension("o"); of_o.set_extension("o");
of_a.set_extension("nasm"); of_a.set_extension("nasm");
let mut should_push_ret = false;
let file = fs::File::create(&of_a)?; let file = fs::File::create(&of_a)?;
let mut writer = BufWriter::new(&file); let mut writer = BufWriter::new(&file);
@ -71,12 +72,15 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
writeln!(writer, " add rsp, 40")?; writeln!(writer, " add rsp, 40")?;
writeln!(writer, " ret")?; writeln!(writer, " ret")?;
if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "global _start")?; writeln!(writer, "global _start")?;
writeln!(writer, "_start:")?; writeln!(writer, "_start:")?;
writeln!(writer, " lea rbp, [rel ret_stack]")?; writeln!(writer, " lea rbp, [rel ret_stack]")?;
writeln!(writer, " call main")?; writeln!(writer, " call main")?;
writeln!(writer, " jmp end")?; writeln!(writer, " jmp end")?;
}
let mut ti = 0; let mut ti = 0;
while ti < tokens.len() { while ti < tokens.len() {
@ -416,6 +420,12 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1; ti += 1;
}, },
InstructionType::Return => { 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, " sub rbp, 8")?;
writeln!(writer, " mov rbx, qword [rbp]")?; writeln!(writer, " mov rbx, qword [rbp]")?;
writeln!(writer, " push rbx")?; writeln!(writer, " push rbx")?;
@ -430,7 +440,6 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
InstructionType::TypePtr | InstructionType::TypePtr |
InstructionType::TypeInt | InstructionType::TypeInt |
InstructionType::TypeVoid | InstructionType::TypeVoid |
InstructionType::TypeStr |
InstructionType::TypeAny | InstructionType::TypeAny |
InstructionType::Returns | InstructionType::Returns |
InstructionType::With => { InstructionType::With => {
@ -501,6 +510,12 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
ti += 1; ti += 1;
}, },
KeywordType::FunctionDone => { 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, " sub rbp, 8")?;
writeln!(writer, " mov rbx, qword [rbp]")?; writeln!(writer, " mov rbx, qword [rbp]")?;
writeln!(writer, " push rbx")?; writeln!(writer, " push rbx")?;
@ -510,18 +525,71 @@ pub fn compile(tokens: &[Operator], args: &Args) -> Result<i32>{
KeywordType::FunctionThen => ti += 1, KeywordType::FunctionThen => ti += 1,
KeywordType::Function | KeywordType::Function |
KeywordType::Include | KeywordType::Include |
KeywordType::FunctionDefInline | KeywordType::Inline |
KeywordType::Export |
KeywordType::Constant => unreachable!(), 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, "addr_{ti}:")?;
if crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "end:")?; writeln!(writer, "end:")?;
writeln!(writer, " mov rax, 60")?; writeln!(writer, " mov rax, 60")?;
writeln!(writer, " mov rdi, 0")?; writeln!(writer, " mov rdi, 0")?;
writeln!(writer, " syscall")?; writeln!(writer, " syscall")?;
}
writeln!(writer, "segment .data")?; writeln!(writer, "segment .data")?;
for (i, s) in strings.iter().enumerate() { for (i, s) in strings.iter().enumerate() {
let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>(); let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();

View File

@ -18,3 +18,9 @@ pub const DEFAULT_INCLUDES: [&str;1] = [
*/ */
pub const MEM_SZ: usize = 640 * 1000; // 4kb pub const MEM_SZ: usize = 640 * 1000; // 4kb
pub const STRING_SZ: usize = 640 * 1000; // 4kb pub const STRING_SZ: usize = 640 * 1000; // 4kb
/**
* Experimental options
*/
pub const ENABLE_EXPORTED_FUNCTIONS: bool = false;

View File

@ -58,7 +58,7 @@ pub enum InstructionType {
TypePtr, TypePtr,
TypeInt, TypeInt,
TypeVoid, TypeVoid,
TypeStr, // TypeStr,
TypeAny, TypeAny,
Returns, Returns,
With, With,
@ -84,10 +84,11 @@ pub enum KeywordType {
ConstantDef, ConstantDef,
Function, Function,
FunctionDef, FunctionDef,
FunctionDefInline, FunctionDefExported,
FunctionThen, FunctionThen,
FunctionDone, FunctionDone,
Inline Inline,
Export
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -104,7 +105,8 @@ pub struct Operator{
pub text: String, //? only used for OpType::PushStr pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize, pub jmp: usize,
pub loc: Loc pub loc: Loc,
pub types: (usize, usize)
} }
impl Operator { impl Operator {
@ -117,13 +119,19 @@ impl Operator {
text, text,
loc: (file, row, col), loc: (file, row, col),
tok_typ, 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.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 { impl OpType {
@ -180,7 +188,6 @@ impl OpType {
InstructionType::TypePtr => "ptr", InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int", InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void", InstructionType::TypeVoid => "void",
InstructionType::TypeStr => "str",
InstructionType::Returns => "returns", InstructionType::Returns => "returns",
InstructionType::With => "with", InstructionType::With => "with",
InstructionType::TypeAny => "any", InstructionType::TypeAny => "any",
@ -201,8 +208,9 @@ impl OpType {
KeywordType::FunctionDone => "done", KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)", KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)", KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefInline => "inline function definition (internal)", KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline" KeywordType::Inline => "inline",
KeywordType::Export => "export",
} }
} }
@ -259,7 +267,6 @@ pub enum Types {
Ptr, Ptr,
Int, Int,
Void, Void,
Str,
Any Any
// U8, // U8,
// U16, // U16,

View File

@ -328,7 +328,6 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
InstructionType::TypePtr | InstructionType::TypePtr |
InstructionType::TypeInt | InstructionType::TypeInt |
InstructionType::TypeVoid | InstructionType::TypeVoid |
InstructionType::TypeStr |
InstructionType::TypeAny | InstructionType::TypeAny |
InstructionType::Returns | InstructionType::Returns |
InstructionType::With => ip += 1, InstructionType::With => ip += 1,
@ -362,7 +361,7 @@ pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
KeywordType::While | //* exept this one, this one should just skip over KeywordType::While | //* exept this one, this one should just skip over
KeywordType::Memory | KeywordType::Memory |
KeywordType::FunctionDef | KeywordType::FunctionDef |
KeywordType::FunctionDefInline | KeywordType::FunctionDefExported |
KeywordType::ConstantDef => { KeywordType::ConstantDef => {
//? Disabled since we now pre run the whole program //? 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 }); // 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<i32>{
KeywordType::Constant | KeywordType::Constant |
KeywordType::Function | KeywordType::Function |
KeywordType::Inline | KeywordType::Inline |
KeywordType::Export |
KeywordType::Include => unreachable!(), KeywordType::Include => unreachable!(),
} }
} }
@ -409,7 +409,9 @@ pub fn pre_run(ops: &[Operator]) -> Defineds {
OpType::Keyword(KeywordType::Memory) => { OpType::Keyword(KeywordType::Memory) => {
defineds.memories.insert(op.addr.unwrap(), Memory { size: op.value, loc: op.loc.clone(), id: op.addr.unwrap() }); 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::FunctionDefExported) => {
}
OpType::Keyword(KeywordType::FunctionDef) => { OpType::Keyword(KeywordType::FunctionDef) => {
defineds.functions.insert(op.text.clone(), Function { loc: op.loc.clone(), name: op.text.clone(), id: ip }); defineds.functions.insert(op.text.clone(), Function { loc: op.loc.clone(), name: op.text.clone(), id: ip });
}, },

View File

@ -57,6 +57,9 @@ pub struct Args {
#[arg(long, short='O', default_value_t=String::from("0"))] #[arg(long, short='O', default_value_t=String::from("0"))]
optimisation: String, optimisation: String,
// disables the main function
#[arg(long="lib")]
lib_mode: bool
//#[arg(long, short='F')] //#[arg(long, short='F')]
//features: Vec<String>, //features: Vec<String>,

View File

@ -207,6 +207,7 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
"then" => OpType::Keyword(KeywordType::FunctionThen), "then" => OpType::Keyword(KeywordType::FunctionThen),
"done" => OpType::Keyword(KeywordType::FunctionDone), "done" => OpType::Keyword(KeywordType::FunctionDone),
"inline" => OpType::Keyword(KeywordType::Inline), "inline" => OpType::Keyword(KeywordType::Inline),
"export" => OpType::Keyword(KeywordType::Export),
"return" => OpType::Instruction(InstructionType::Return), "return" => OpType::Instruction(InstructionType::Return),
"returns" => OpType::Instruction(InstructionType::Returns), "returns" => OpType::Instruction(InstructionType::Returns),
"bool" => OpType::Instruction(InstructionType::TypeBool), "bool" => OpType::Instruction(InstructionType::TypeBool),
@ -214,7 +215,6 @@ pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
"ptr" => OpType::Instruction(InstructionType::TypePtr), "ptr" => OpType::Instruction(InstructionType::TypePtr),
"void" => OpType::Instruction(InstructionType::TypeVoid), "void" => OpType::Instruction(InstructionType::TypeVoid),
"any" => OpType::Instruction(InstructionType::TypeAny), "any" => OpType::Instruction(InstructionType::TypeAny),
"str" => OpType::Instruction(InstructionType::TypeStr),
"with" => OpType::Instruction(InstructionType::With), "with" => OpType::Instruction(InstructionType::With),
_ => OpType::Instruction(InstructionType::None) _ => OpType::Instruction(InstructionType::None)
} }

View File

@ -212,6 +212,7 @@ impl<'a> Preprocessor<'a> {
if f_inline { if f_inline {
f_inline = false;
let mut prog: Vec<Operator> = Vec::new(); let mut prog: Vec<Operator> = Vec::new();
let mut depth = -1; let mut depth = -1;
while !rtokens.is_empty() { while !rtokens.is_empty() {
@ -224,7 +225,6 @@ impl<'a> Preprocessor<'a> {
InstructionType::TypeBool | InstructionType::TypeBool |
InstructionType::TypeInt | InstructionType::TypeInt |
InstructionType::TypePtr | InstructionType::TypePtr |
InstructionType::TypeStr |
InstructionType::With | InstructionType::With |
InstructionType::Returns | InstructionType::Returns |
InstructionType::TypeVoid => { InstructionType::TypeVoid => {
@ -271,7 +271,52 @@ impl<'a> Preprocessor<'a> {
tokens: Some(prog) 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<Operator> = 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 { } 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{ self.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(), loc: name.loc.clone(),
name: name.text.clone(), name: name.text.clone(),
@ -357,7 +402,10 @@ impl<'a> Preprocessor<'a> {
} }
OpType::Keyword(KeywordType::Inline) => { 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"); lerror!(&op.loc, "Function is already marked as inline, remove this inline Keyword");
return Err(eyre!("")); return Err(eyre!(""));
} else { } 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); program.push(op);
} }
@ -381,6 +441,7 @@ impl<'a> Preprocessor<'a> {
f.typ != OpType::Instruction(InstructionType::FnCall) && f.typ != OpType::Instruction(InstructionType::FnCall) &&
f.typ != OpType::Instruction(InstructionType::MemUse) && f.typ != OpType::Instruction(InstructionType::MemUse) &&
f.typ != OpType::Keyword(KeywordType::FunctionDef) && f.typ != OpType::Keyword(KeywordType::FunctionDef) &&
f.typ != OpType::Keyword(KeywordType::FunctionDefExported) &&
f.typ != OpType::Keyword(KeywordType::ConstantDef) && f.typ != OpType::Keyword(KeywordType::ConstantDef) &&
f.typ != OpType::Instruction(InstructionType::ConstUse) { f.typ != OpType::Instruction(InstructionType::ConstUse) {
lookup_word(&f.text, &f.loc) lookup_word(&f.text, &f.loc)

View File

@ -62,10 +62,10 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
stack_pop(&mut stack, &op, &[Types::Bool])?; stack_pop(&mut stack, &op, &[Types::Bool])?;
}, },
KeywordType::FunctionDefInline | KeywordType::FunctionDefExported |
KeywordType::FunctionDef => { KeywordType::FunctionDef => {
let name = op.text.clone(); let name = op.text.clone();
// println!("{:?}", name);
if let Some(p) = rtokens.pop() { if let Some(p) = rtokens.pop() {
if p.typ != OpType::Instruction(InstructionType::With){ if p.typ != OpType::Instruction(InstructionType::With){
lerror!(&op.loc, "Expected {:?}, got {:?}", OpType::Instruction(InstructionType::With), p.typ); lerror!(&op.loc, "Expected {:?}, got {:?}", OpType::Instruction(InstructionType::With), p.typ);
@ -106,8 +106,6 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
continue; continue;
} }
Types::Void Types::Void
} else if op.typ == OpType::Instruction(InstructionType::TypeStr) {
Types::Str
} else if op.typ == OpType::Instruction(InstructionType::TypeAny) { } else if op.typ == OpType::Instruction(InstructionType::TypeAny) {
Types::Any Types::Any
} else { } else {
@ -177,8 +175,12 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
}, },
KeywordType::FunctionThen | KeywordType::FunctionThen |
KeywordType::FunctionDone | KeywordType::FunctionDone |
KeywordType::Function => unreachable!(), KeywordType::Inline |
KeywordType::Inline => (), KeywordType::Export |
KeywordType::Function => {
println!("{:?}", op);
unreachable!()
},
} }
}, },
OpType::Instruction(instruction) => { OpType::Instruction(instruction) => {
@ -332,7 +334,10 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
InstructionType::FnCall => { InstructionType::FnCall => {
stack_snapshots.push(stack.clone()); 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()); // in_function = (op.text.clone(), f.clone(), op.loc.clone());
@ -362,7 +367,6 @@ pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>
InstructionType::TypeInt | InstructionType::TypeInt |
InstructionType::TypeVoid | InstructionType::TypeVoid |
InstructionType::TypeAny | InstructionType::TypeAny |
InstructionType::TypeStr |
InstructionType::Returns | InstructionType::Returns |
InstructionType::With => (), InstructionType::With => (),
InstructionType::ConstUse => { InstructionType::ConstUse => {

View File

@ -1,7 +1,12 @@
// include "std.mcl" // include "std.mcl"
fn mcl_print with int ptr returns void then
fn main with void returns void then 1 1 syscall3 drop
69 test
done done
inline fn test with void returns void then _dbg_print done fn mcl_dump with int returns void then
_dbg_print
done
fn main with void returns void then
"hi\n" mcl_print
done