From dd1bc0b478e427fcf91387084ecb5783e0940743 Mon Sep 17 00:00:00 2001 From: Goren Barak Date: Fri, 1 Dec 2023 16:58:35 -0500 Subject: [PATCH] Hopefully made math recursively work and added `FasmCodegen` struct. --- Cargo.toml | 1 + src/#main.rs# | 66 ------- src/codegen/fasm.rs | 421 +++++++++++++++++++++++--------------------- src/main.rs | 5 +- src/parse/parse.rs | 76 ++------ 5 files changed, 239 insertions(+), 330 deletions(-) delete mode 100644 src/#main.rs# diff --git a/Cargo.toml b/Cargo.toml index 3bca400..473469f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "skylang" version = "0.1.0" edition = "2021" +channel = "nightly" [lib] proc-macro = true diff --git a/src/#main.rs# b/src/#main.rs# deleted file mode 100644 index 5605800..0000000 --- a/src/#main.rs# +++ /dev/null @@ -1,66 +0,0 @@ -#![allow(warnings)] - -pub mod lex; -pub mod codegen; -use crate::codegen::fasm::*; -use crate::lex::tok::*; -use crate::parse::ast::*; -use crate::parse::parse::*; -use logos::Logos; - -pub mod parse; - -macro_rules! arrow { - ($spaces:expr) => { - println!("{}↓", $spaces); - } -} - -fn main() { - // let fc = fasm_codegen!( - // vec![ - // Expr::VarDefinition(VarDefinition {name: "goren", value: Value::Number(10)}), - // Expr::MathExpr(Math { - // left: &Value::Var(VarReference { name: "goren"}), - // right: &Value::Number(17), - // operator: MathOperator::OP_MULT - // } - // ), - // Expr::FunDefinition(FunDefinition { - // name: "adder", contents: vec![ - // Expr::MathExpr( - // Math { - // left: &Value::Param(ParamReference {param_number: 0}), - // right: &Value::Param(ParamReference {param_number: 1}), - // operator: MathOperator::OP_ADD - // } - // ) - // ] - // }), - - // Expr::FunCall( - // FunCall { - // name: "adder", - // params: vec![Value::Var(VarReference {name: "goren"}), Value::Number(6)] - // } - // ), - - // Expr::Breakpoint - // ] - // ); - - - // println!("{}", fc); - let parsed = "30 * 60"; - - let mut lexer = Token::lexer(parsed); - - println!("\"{}\"", parsed); - arrow!(" "); - println!("{:?}", lex_str(parsed)); - arrow!(" "); - let parsed = parse_math(lexer); - println!("{:?}", parsed); - arrow!(" "); - println!("{}", fasm_codegen!(&vec![parsed.unwrap()])); -} diff --git a/src/codegen/fasm.rs b/src/codegen/fasm.rs index 31854e8..1cb96f1 100644 --- a/src/codegen/fasm.rs +++ b/src/codegen/fasm.rs @@ -1,234 +1,249 @@ use crate::parse::ast::*; -use std::rc::Rc; use skylang::temp; -#[macro_export] -macro_rules! fasm_codegen { - // Macro to make calling fasm_codegen function easier. - ($exprs:expr) => { - fasm_codegen($exprs, true) - }; +const REGISTERS: [&str; 9] = ["r10", "r11", "r12", "r13", "r14", "r15", "rax", "rdi", "rsi"]; - (fun: $exprs:expr) => { - fasm_codegen($exprs, false) - } +pub struct FasmCodegen { + register_counter: usize } -pub fn fasm_codegen(exprs: &Vec, not_a_function: bool) -> String { - // Define asm_func, used for functions. - let mut asm_func = String::new(); - // Define asm_data, used for variables. - let mut asm_data = String::new(); - // Define asm_start, used for the entry point. - let mut asm_start = String::new(); - macro_rules! unwrap { - ($item:expr) => { - asm_start.push_str(fasm_codegen!(fun: &vec![$item.as_ref().clone()]).as_str()); +impl FasmCodegen { + pub fn new() -> Self { + FasmCodegen { + register_counter: 0, } } + pub fn fasm_codegen(&mut self, exprs: &Vec, not_a_function: bool) -> String { + macro_rules! fasm_codegen { + // Macro to make calling fasm_codegen function easier. + ($exprs:expr) => {{ + self.fasm_codegen($exprs, true) + }}; - // If not_a_function, push necessary headers to the asm_start variable. - if not_a_function { - asm_start.push_str("format ELF64 executable 3\n"); - asm_start.push_str("segment readable executable\n"); - asm_start.push_str("entry _start\n"); - asm_start.push_str("_start:\n"); - asm_data.push_str("\nsegment readable writable\n"); - } - - // Iterate over expressions. - for expr in exprs.iter() { - // Use patern matching on `expr`. - match expr { - // If the expression is a math expression. - Expr::MathExpr(e) => { - unwrap!(e.left); - asm_start.push_str(format!("\tmov r10, rax\n").as_str()); - unwrap!(e.right); - asm_start.push_str(format!("\tmov r11, rax\n").as_str()); - match e.operator { - // If the operator is addition. - MathOperator::OP_ADD => { - asm_start.push_str("\tadd r10, r11\n"); - asm_start.push_str("\tmov rax, r10\n"); - // r10 ← r10 + r11; rax ← r10; - // The sum will now be stored in the %rax register. - }, - // If the operator is multiplication. - MathOperator::OP_MULT => { - asm_start.push_str("\timul r10, r11\n"); - asm_start.push_str("\tmov rax, r10\n"); - // r10 ← r10 * r11; rax ← r10; - // The product will now be stored in the %rax register. - }, - // If the operator is division. - MathOperator::OP_DIV => { - asm_start.push_str("\tmov rax, r10\n"); - asm_start.push_str("\txor rdx, rdx\n"); - asm_start.push_str("\tidiv r11\n"); - // rax ← r10; rdx ← 0; rax ← concat(rax, rdx) / r11; - // The quotient will now be stored in the %rax register. - }, - // If the operators is subtraction. - MathOperator::OP_SUB => { - asm_start.push_str("\tsub r10, r11\n"); - asm_start.push_str("\tmov rax, r10\n"); - // r10 ← r10 - r11; rax ← r10; - // The difference will now be stored in the %rax register. - }, - // If the operator is modulo. - MathOperator::OP_MOD => { - asm_start.push_str("\tmov rax, r10\n"); - asm_start.push_str("\txor rdx, rdx\n"); - asm_start.push_str("\tidiv r11\n"); - asm_start.push_str("\tmov rax, rdx\n"); - // rax ← r10; rdx ← 0; rdx ← concat(rax, rdx) % r11; rax ← rdx; - // The remainder will now be stored in the %rax register. - } - } - }, + (fun: $exprs:expr) => {{ + self.fasm_codegen($exprs, false) + }}; + } - // If the expression is a function call. - Expr::FunCall(e) => { - for (i, p) in e.params.iter().enumerate() { - match i { - 0 => { - // First parameter. Put in %rdi. - unwrap!(p); - asm_start.push_str(format!("\tmov rdi, rax\n").as_str()); - // rdi ← e.params[0]; + // Define asm_func, used for functions. + let mut asm_func = String::new(); + // Define asm_data, used for variables. + let mut asm_data = String::new(); + // Define asm_start, used for the entry point. + let mut asm_start = String::new(); + macro_rules! unwrap { + ($item:expr) => { + asm_start.push_str(fasm_codegen!(fun: &vec![$item.as_ref().clone()]).as_str()); + } + } + + + // If not_a_function, push necessary headers to the asm_start variable. + if not_a_function { + asm_start.push_str("format ELF64 executable 3\n"); + asm_start.push_str("segment readable executable\n"); + asm_start.push_str("entry _start\n"); + asm_start.push_str("_start:\n"); + asm_data.push_str("\nsegment readable writable\n"); + } + + // Iterate over expressions. + for expr in exprs.iter() { + // Use patern matching on `expr`. + match expr { + // If the expression is a math expression. + Expr::MathExpr(e) => { + unwrap!(e.left); + self.register_counter += 1; + asm_start.push_str(format!("\tmov {}, rax\n", REGISTERS[self.register_counter]).as_str()); + unwrap!(e.right); + self.register_counter += 1; + asm_start.push_str(format!("\tmov {}, rax\n", REGISTERS[self.register_counter]).as_str()); + match e.operator { + // If the operator is addition. + MathOperator::OP_ADD => { + asm_start.push_str(format!("\tadd {}, {}\n", REGISTERS[self.register_counter - 1], REGISTERS[self.register_counter]).as_str()); + asm_start.push_str(format!("\tmov rax, {}\n", REGISTERS[self.register_counter - 1]).as_str()); + // r10 ← r10 + r11; rax ← r10; + // The sum will now be stored in the %rax register. }, - - 1 => { - // Second parameter. Put in %rsi. - unwrap!(p); - asm_start.push_str(format!("\tmov rsi, rax\n").as_str()); - // rsi ← e.params[1]; + // If the operator is multiplication. + MathOperator::OP_MULT => { + asm_start.push_str(format!("\timul {}, {}\n", REGISTERS[self.register_counter - 1], REGISTERS[self.register_counter]).as_str()); + asm_start.push_str(format!("\tmov rax, {}\n", REGISTERS[self.register_counter - 1]).as_str()); + // r10 ← r10 * r11; rax ← r10; + // The product will now be stored in the %rax register. }, - - 2 => { - // Third parameter. Put in %rdx. - unwrap!(p); - asm_start.push_str(format!("\tmov rdx, rax\n").as_str()); - // rdx ← e.params[2]; + // If the operator is division. + MathOperator::OP_DIV => { + asm_start.push_str("\tmov rax, r10\n"); + asm_start.push_str("\txor rdx, rdx\n"); + asm_start.push_str("\tidiv r11\n"); + // rax ← r10; rdx ← 0; rax ← concat(rax, rdx) / r11; + // The quotient will now be stored in the %rax register. }, - - 3 => { - // Fourth parameter. Put in %rcx. - unwrap!(p); - asm_start.push_str(format!("\tmov rcx, rax\n").as_str()); - // rcx ← e.params[3]; + // If the operators is subtraction. + MathOperator::OP_SUB => { + asm_start.push_str("\tsub r10, r11\n"); + asm_start.push_str("\tmov rax, r10\n"); + // r10 ← r10 - r11; rax ← r10; + // The difference will now be stored in the %rax register. }, - - 4 => { - // Fifth parameter. Put in %r8. - unwrap!(p); - asm_start.push_str(format!("\tmov r8, rax").as_str()); - // r8 ← e.params[4]; - }, - - 5 => { - // Sixth parameter. Put in %r9. - unwrap!(p); - asm_start.push_str(format!("\tmov r9, rax\n").as_str()); - // r9 ← e.params[5]; - }, - - _ => { - // Parameters after the sixth parameter are pushed to the stack. - unwrap!(p); - asm_start.push_str(format!("\tpush rax\n").as_str()); - // STACK_TOP ← e.params[(6+)]; + // If the operator is modulo. + MathOperator::OP_MOD => { + asm_start.push_str("\tmov rax, r10\n"); + asm_start.push_str("\txor rdx, rdx\n"); + asm_start.push_str("\tidiv r11\n"); + asm_start.push_str("\tmov rax, rdx\n"); + // rax ← r10; rdx ← 0; rdx ← concat(rax, rdx) % r11; rax ← rdx; + // The remainder will now be stored in the %rax register. } } - } + }, - // Call the function. - asm_start.push_str(format!("\tcall {}\n", e.name).as_str()); - }, + // If the expression is a function call. + Expr::FunCall(e) => { + for (i, p) in e.params.iter().enumerate() { + match i { + 0 => { + // First parameter. Put in %rdi. + unwrap!(p); + asm_start.push_str(format!("\tmov rdi, rax\n").as_str()); + // rdi ← e.params[0]; + }, - // Define a global variable. - Expr::GlobalDefinition(e) => { - // Define a 64-bit global variable. - - asm_data.push_str(format!("\t{} dq {}", e.name, e.value).as_str()); - }, + 1 => { + // Second parameter. Put in %rsi. + unwrap!(p); + asm_start.push_str(format!("\tmov rsi, rax\n").as_str()); + // rsi ← e.params[1]; + }, - // Breakpoint. - Expr::Breakpoint => { - // Write the interrupt for a debugger breakpoint. - asm_start.push_str("\tint3\n"); - }, + 2 => { + // Third parameter. Put in %rdx. + unwrap!(p); + asm_start.push_str(format!("\tmov rdx, rax\n").as_str()); + // rdx ← e.params[2]; + }, - // Return something from a function. - Expr::Return(e) => { - // Do the operation that should later be returned. - asm_start.push_str(fasm_codegen!(fun: &e).as_str()); - // Move the return value to rbp + 8. - // [rbp + 8] ← return_value - }, + 3 => { + // Fourth parameter. Put in %rcx. + unwrap!(p); + asm_start.push_str(format!("\tmov rcx, rax\n").as_str()); + // rcx ← e.params[3]; + }, - // A function defenition. - Expr::FunDefinition(e) => { - // In x86-64 assembly, a function is defined as :. Push this to the `asm_func`. - asm_func.push_str(format!("{}:\n", e.name).as_str()); - // Call the function itself specifying that you are defining a function, and push the returned value to `asm_func`. - asm_func.push_str(fasm_codegen!(fun: &e.contents).as_str()); - // Use the ret instruction to return from the procedure. - asm_func.push_str("\tret\n"); - }, + 4 => { + // Fifth parameter. Put in %r8. + unwrap!(p); + asm_start.push_str(format!("\tmov r8, rax").as_str()); + // r8 ← e.params[4]; + }, - Expr::If(e) => { - // Increment the temporary variable/function counter. - // Compare the left and right value. - unwrap!(e.left); - asm_start.push_str("mov rdi, rax"); - unwrap!(e.right); - asm_start.push_str("mov rsi, rax"); - asm_start.push_str(format!("\tcmp rdi, rsi\n").as_str()); - // Check what the condition is. - match e.cond { - COND_OP::EQ => { - // If the compared values are equal to each other jump to the temporary function. - asm_start.push_str(format!("je .{}", temp!()).as_str()); - }, + 5 => { + // Sixth parameter. Put in %r9. + unwrap!(p); + asm_start.push_str(format!("\tmov r9, rax\n").as_str()); + // r9 ← e.params[5]; + }, - COND_OP::NE => { - // If the compared values are not equal to eachother jump to the temporary function. - asm_start.push_str(format!("jne .{}", temp!()).as_str()); + _ => { + // Parameters after the sixth parameter are pushed to the stack. + unwrap!(p); + asm_start.push_str(format!("\tpush rax\n").as_str()); + // STACK_TOP ← e.params[(6+)]; + } + } } - } - // Create the temporary function. - asm_func.push_str(format!(".{}:\n", temp!()).as_str()); - asm_func.push_str(fasm_codegen!(fun: &e.action).as_str()); - asm_func.push_str("\tret\n"); + // Call the function. + asm_start.push_str(format!("\tcall {}\n", e.name).as_str()); + }, - }, - Expr::Number(n) => { - asm_func.push_str(format!("\tmov rax, {}\n", n).as_str()) - }, - no => unsafe { - // Write some data I randomly typed to your memory because don't going around playing with something that I haven't implemented yet. - println!("{:?} is not. implemented.", no); - let mut ptr = 0x00 as *mut f64; - ::std::ptr::write(ptr, 124010240120401240.12410240124120401240); - }, + // Define a global variable. + Expr::GlobalDefinition(e) => { + // Define a 64-bit global variable. + + asm_data.push_str(format!("\t{} dq {}", e.name, e.value).as_str()); + }, + + // Breakpoint. + Expr::Breakpoint => { + // Write the interrupt for a debugger breakpoint. + asm_start.push_str("\tint3\n"); + }, + + // Return something from a function. + Expr::Return(e) => { + // Do the operation that should later be returned. + asm_start.push_str(fasm_codegen!(fun: &e).as_str()); + // Move the return value to rbp + 8. + // [rbp + 8] ← return_value + }, + + // A function defenition. + Expr::FunDefinition(e) => { + // In x86-64 assembly, a function is defined as :. Push this to the `asm_func`. + asm_func.push_str(format!("{}:\n", e.name).as_str()); + // Call the function itself specifying that you are defining a function, and push the returned value to `asm_func`. + asm_func.push_str(fasm_codegen!(fun: &e.contents).as_str()); + // Use the ret instruction to return from the procedure. + asm_func.push_str("\tret\n"); + }, + + Expr::If(e) => { + // Increment the temporary variable/function counter. + // Compare the left and right value. + unwrap!(e.left); + asm_start.push_str("mov rdi, rax"); + unwrap!(e.right); + asm_start.push_str("mov rsi, rax"); + asm_start.push_str(format!("\tcmp rdi, rsi\n").as_str()); + // Check what the condition is. + match e.cond { + COND_OP::EQ => { + // If the compared values are equal to each other jump to the temporary function. + asm_start.push_str(format!("je .{}", temp!()).as_str()); + }, + + COND_OP::NE => { + // If the compared values are not equal to eachother jump to the temporary function. + asm_start.push_str(format!("jne .{}", temp!()).as_str()); + } + } + + // Create the temporary function. + asm_func.push_str(format!(".{}:\n", temp!()).as_str()); + asm_func.push_str(fasm_codegen!(fun: &e.action).as_str()); + asm_func.push_str("\tret\n"); + + }, + Expr::Number(n) => { + asm_func.push_str(format!("\tmov rax, {}\n", n).as_str()) + }, + no => unsafe { + // Write some data I randomly typed to your memory because don't going around playing with something that I haven't implemented yet. + println!("{:?} is not. implemented.", no); + let mut ptr = 0x00 as *mut f64; + ::std::ptr::write(ptr, 124010240120401240.12410240124120401240); + }, + } + } - - } - + - if not_a_function { - // Use the exit syscall to leave the program. If you don't do this, you will get a segmentation fault. - asm_start.push_str("\tmov rax, 60 ; 60 is the system call number for exit.\n"); - asm_start.push_str("\txor rdi, rdi ; 0 is the exit code we want.\n"); - asm_start.push_str("\tsyscall ; this is the instruction to actually perform the system call.\n"); + if not_a_function { + // Use the exit syscall to leave the program. If you don't do this, you will get a segmentation fault. + asm_start.push_str("\tmov rax, 60 ; 60 is the system call number for exit.\n"); + asm_start.push_str("\txor rdi, rdi ; 0 is the exit code we want.\n"); + asm_start.push_str("\tsyscall ; this is the instruction to actually perform the system call.\n"); + } + // Get the final `asm` string derived from all of the other strings that we have manipulated (finally!). + let asm = format!("{}{}{}", asm_start, asm_func, asm_data); + // Return the final `asm` string. + + asm } - // Get the final `asm` string derived from all of the other strings that we have manipulated (finally!). - let asm = format!("{}{}{}", asm_start, asm_func, asm_data); - // Return the final `asm` string. - - asm + } diff --git a/src/main.rs b/src/main.rs index f7c2ff1..b579b4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(associated_type_bounds)] #![allow(warnings)] pub mod lex; @@ -51,7 +52,7 @@ fn main() { // println!("{}", fc); - let parsed = "30 * 60"; + let parsed = "3*10+5"; let mut lexer = Token::lexer(parsed); @@ -62,6 +63,6 @@ fn main() { let parsed = parse_math(lexer); println!("{:?}", parsed); arrow!(" "); - println!("{}", fasm_codegen!(&vec![parsed.unwrap()])); + println!("{}", FasmCodegen::new().fasm_codegen(&vec![parsed.unwrap()], true)); } diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 5869da9..53354e6 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -10,27 +10,18 @@ macro_rules! unwrap { } } -#[macro_export] -macro_rules! parse_value { - ($parse:expr) => { - parse_value(&($parse.next(), $parse.slice())) - } -} - pub fn parse_math(mut tokens: Lexer) -> Option { // Is it a Value? → Is it an operator? → Is it a value? - if let Some(left) = parse_value!(tokens) { - if let Some(operator) = match_operator(&mut tokens) { - if let Some(right) = parse_value!(tokens) { - let left = Rc::new(left); - let right = Rc::new(right); - - return Some(Expr::MathExpr(Math {left: left, right: right, operator})) + if let Some(Ok(Number(left))) = tokens.next() { + if let Some(op) = match_operator(&mut tokens) { + if let Some(right) = parse_math(tokens) { + return Some(Expr::MathExpr(Math {left: Rc::new(Expr::Number(left)), right: Rc::new(right), operator: op})); } + } else { + return Some(Expr::Number(left)); } } - None } @@ -52,50 +43,17 @@ pub fn parse_global_declaration(mut tokens: Lexer) -> Option { tok } -pub fn parse_value<'a>(token: &(Option>, &'a str)) -> Option> { - if let Some(Ok(tt)) = &token.0 { - let mut value = None; - - if let Number(n) = tt { - value = Some(Expr::Number(*n)); - } else if *tt == Identifier { - value = Some(Expr::Var(VarReference { name: token.1 })); - } - - value - - } else { - return None; - } -} - -pub fn parse_fun_call(mut tokens: Lexer) -> Option { - // Is it an Ident? → Is it a LeftParen? → Is it a value (I should really make a function to parse that) or is it a RightParen? - // ↓ ↓ - // If it's a value, push that to `params`. Otherwise, params will just be a `Vec::new()`. - let mut tok = None; - - if unwrap!(tokens) == Identifier { - let name = tokens.slice(); - if unwrap!(tokens) == LeftParen { - let mut params = Vec::new(); - while let Some(value) = parse_value!(tokens) { - params.push(Rc::new(value)); - } - tok = Some(Expr::FunCall(FunCall {name, params: params.clone()})); - } - } - - tok -} - pub fn match_operator(tokens: &mut Lexer) -> Option { - match unwrap!(tokens) { - Plus => Some(MathOperator::OP_ADD), - Minus => Some(MathOperator::OP_SUB), - Slash => Some(MathOperator::OP_DIV), - Star => Some(MathOperator::OP_MULT), - Percent => Some(MathOperator::OP_MOD), - _ => None + if let Some(Ok(token)) = tokens.next() { + return match token { + Plus => Some(MathOperator::OP_ADD), + Minus => Some(MathOperator::OP_SUB), + Slash => Some(MathOperator::OP_DIV), + Star => Some(MathOperator::OP_MULT), + Percent => Some(MathOperator::OP_MOD), + _ => None + }; } + + None }