From 8b7f29cd48acc23cecc3b118383798d33cdd813c Mon Sep 17 00:00:00 2001 From: zxq5 Date: Sun, 9 Aug 2026 09:15:09 +0100 Subject: [PATCH] started on a compiler for a simple functional programming language for the DSA. --- doc/notes/functional language codegen.md | 19 ++ tooling/dsfl_compiler/src/codegen.rs | 0 tooling/dsfl_compiler/src/lib.rs | 273 +++++------------------ tooling/dsfl_compiler/src/main.rs | 26 +-- tooling/dsfl_compiler/src/parser.rs | 253 +++++++++++++++++++++ 5 files changed, 346 insertions(+), 225 deletions(-) create mode 100644 doc/notes/functional language codegen.md create mode 100644 tooling/dsfl_compiler/src/codegen.rs create mode 100644 tooling/dsfl_compiler/src/parser.rs diff --git a/doc/notes/functional language codegen.md b/doc/notes/functional language codegen.md new file mode 100644 index 0000000..58110c6 --- /dev/null +++ b/doc/notes/functional language codegen.md @@ -0,0 +1,19 @@ +# Codegen. + +## How? + +(add (sub 5 4) +(add (sub 4 3) +(add (sub 3 2) +(add (sub 2 1) +(add (sub 1 0) +0 +) +) +) +) +) + +becomes + +v1 = 5 - 4 v2 = 4 - 3 v3 = 3 - 2 v4 = 2 - 1 v5 = 1 - 0 v6 = v5 + 0 v7 = v4 + v6 v8 = v3 + v7 v9 = v2 + v8 v10 = v1 + v9 \ No newline at end of file diff --git a/tooling/dsfl_compiler/src/codegen.rs b/tooling/dsfl_compiler/src/codegen.rs new file mode 100644 index 0000000..e69de29 diff --git a/tooling/dsfl_compiler/src/lib.rs b/tooling/dsfl_compiler/src/lib.rs index 88ca7c0..85e7b9a 100644 --- a/tooling/dsfl_compiler/src/lib.rs +++ b/tooling/dsfl_compiler/src/lib.rs @@ -1,12 +1,14 @@ +mod parser; + +use crate::parser::{AST, Lexer}; use assembler::prelude::Assembler; use dsa_formats::binary_dse::DseExecutable; use dsa_tasks::{ProcessAgent, TraceableProcess}; use std::path::PathBuf; -use std::str::FromStr; #[derive(Debug)] -struct DsflCompiler { - src: PathBuf, +pub struct DsflCompiler { + filename: String, } #[derive(Debug)] @@ -20,15 +22,20 @@ impl std::fmt::Display for DsflError { } impl TraceableProcess for DsflCompiler { + type Input = PathBuf; type Output = DseExecutable; type Error = DsflError; fn label(&self) -> String { - format!("Compiling {}", self.src.file_name().unwrap().display()) + format!("Compiling {}", self.filename) } - fn run(mut self: Box, ctx: ProcessAgent) -> Result { - let src_file = self.src.clone(); + fn run( + mut self: Box, + input: impl Into, + ctx: ProcessAgent, + ) -> Result { + let src_file = input.into(); ctx.log(format!("Building {}", src_file.display())); @@ -51,227 +58,69 @@ impl TraceableProcess for DsflCompiler { ctx.log(String::from("Assembling Output...")); - let res = Box::new(Assembler::new(&asm_output_file)) - .run(ctx) - .map_err(|e| DsflError("failed to run Assembler")); + let res = Box::new(Assembler::new( + &asm_output_file + .file_name() + .unwrap() + .to_string_lossy() + .to_string(), + )) + .run(asm_output_file, ctx) + .map_err(|e| DsflError("failed to run Assembler")); res } } -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Token { - Lparen, - Rparen, - - If, - Then, - Else, - Match, - - Operator(Operator), - Int(u32), - Bool(bool), -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Operator { - Add, - Sub, - And, - Or, - Not, - Equals, -} - impl DsflCompiler { - fn compile(&mut self, src: String) -> Result, DsflError> { - let lexed = Lexer::new(src).lex()?; - - Ok(vec![]) - } -} - -pub struct Lexer { - src: Vec, - buffer: String, - tokens: Vec, -} - -impl Lexer { - pub fn new(src: String) -> Self { + pub fn new(filename: &str) -> Self { Self { - src: src.chars().rev().collect(), - buffer: String::new(), - tokens: Vec::new(), + filename: filename.into(), } } - pub fn lex(&mut self) -> Result, DsflError> { - while let Some(c) = self.src.last() { - match c { - ' ' | '\t' | '\n' => { - self.src.pop(); - } - '0'..='9' => { - let result = self.parse_numeric()?; - self.tokens.push(result); - } - 'a'..='z' | 'A'..='Z' | '_' => { - let result = self.parse_ident()?; - self.tokens.push(result); - } - '(' => { - self.tokens.push(Token::Lparen); - self.src.pop(); - } - ')' => { - self.tokens.push(Token::Rparen); - self.src.pop(); - } - _ => return Err(DsflError("invalid char for language 1")), - } - } + fn compile(&mut self, src: String) -> Result { + let tokens = Lexer::new(src).lex()?; + let ast = AST::parse(tokens)?; - Ok(self.tokens.clone()) - } + let mut asm = String::new(); - fn parse_numeric(&mut self) -> Result { - while let Some(c) = self.src.last() { - match c { - ' ' | '\n' | '\t' | '(' | ')' | 'a'..='z' | 'A'..='Z' | '_' => break, - '0'..='9' => { - self.buffer.push(*c); - self.src.pop(); - } - _ => return Err(DsflError("invalid numeric character")), - } - } + asm.push_str( + r#" +include print: "./print.dsa" +dw stack: 0x10000 - let result = Token::Int(u32::from_str(&self.buffer).unwrap()); - self.buffer.clear(); - Ok(result) - } +_init: + ldw stack, bpr + mov bpr, spr +main: + push zero + call program + call print::print_hex_word + pop zero + hlt - fn parse_ident(&mut self) -> Result { - while let Some(c) = self.src.last() { - match c { - ' ' | '\n' | '\t' | '(' | ')' | '0'..='9' => break, - 'a'..='z' | 'A'..='Z' | '_' => { - self.buffer.push(*c); - self.src.pop(); - } - _ => return Err(DsflError("invalid char for language 2")), - } - } +program: func + push rg0 + push rg1 +"#, + ); - let result = match self.buffer.as_str() { - "true" => Ok(Token::Bool(true)), - "false" => Ok(Token::Bool(false)), - "if" => Ok(Token::If), - "then" => Ok(Token::Then), - "else" => Ok(Token::Else), - "match" => Ok(Token::Match), - "add" => Ok(Token::Operator(Operator::Add)), - "sub" => Ok(Token::Operator(Operator::Sub)), - "and" => Ok(Token::Operator(Operator::And)), - "or" => Ok(Token::Operator(Operator::Or)), - "not" => Ok(Token::Operator(Operator::Not)), - "equals" => Ok(Token::Operator(Operator::Equals)), - _ => Err(DsflError("invalid identifier")), - }; + ast.emit_asm(&mut asm)?; - self.buffer.clear(); - result - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum AST { - BinaryOperation(Operator, Box, Box), - UnaryOperation(Operator, Box), - Conditional { - if_: Box, - then: Box, - else_: Box, - }, - Match(Vec), - Int(u32), - Bool(bool), -} - -impl AST { - pub fn parse(tokens: Vec) -> Result { - Self::parse_expr(&mut tokens.into_iter().rev().collect()) - } - - fn parse_expr(tokens: &mut Vec) -> Result { - println!("opening"); - - let next = tokens.pop().exp_tok()?; - - if let Token::Bool(x) = next { - return Ok(Self::Bool(x)); - }; - - if let Token::Int(x) = next { - return Ok(Self::Int(x)); - }; - - Self::expect_true(|| next == Token::Lparen)?; - - let next = tokens.pop().exp_tok()?; - - if let Token::Operator(op) = next { - let left = Self::parse_expr(tokens)?; - let right = Self::parse_expr(tokens)?; - - let t = tokens.pop().exp_tok()?; - Self::expect_true(|| t == Token::Rparen)?; - - return Ok(Self::BinaryOperation(op, Box::new(left), Box::new(right))); - }; - - if let Token::If = next { - let condition = Self::parse_expr(tokens)?; - - let t = tokens.pop().exp_tok()?; - Self::expect_true(|| t == Token::Then)?; - - let then = Self::parse_expr(tokens)?; - - let t = tokens.pop().exp_tok()?; - Self::expect_true(|| t == Token::Else)?; - - let else_ = Self::parse_expr(tokens)?; - - let t = tokens.pop().exp_tok()?; - Self::expect_true(|| t == Token::Rparen)?; - - return Ok(Self::Conditional { - if_: Box::new(condition), - then: Box::new(then), - else_: Box::new(else_), - }); - } - - Err(DsflError("no expression given")) - } - - fn expect_true(expr: impl FnOnce() -> bool) -> Result<(), DsflError> { - expr().ok_or_else(|| DsflError("Expected true expression")) - } -} - -trait ToResult { - fn exp_tok(self) -> Result; -} - -impl ToResult for Option { - fn exp_tok(self) -> Result { - match self { - Some(t) => Ok(t), - None => Err(DsflError("Unexpected EOF")), - } + asm.push_str( + r#" + // return value in bpr+8 + pop rg0 + stw rg0, bpr, 8 + + // epilogue + pop rg1 + pop rg0 + return +"#, + ); + + Ok(asm) } } diff --git a/tooling/dsfl_compiler/src/main.rs b/tooling/dsfl_compiler/src/main.rs index a30d3ce..d7553ec 100644 --- a/tooling/dsfl_compiler/src/main.rs +++ b/tooling/dsfl_compiler/src/main.rs @@ -1,5 +1,8 @@ use clap::Parser; -use dsfl_compiler::{AST, Lexer}; +use dsa_tasks::TraceableProcess; +use dsfl_compiler::DsflCompiler; +use std::fs; +use std::io::Cursor; use std::path::PathBuf; #[derive(Parser, Debug, Clone)] @@ -15,19 +18,16 @@ fn main() { // Parse command line arguments let args = Args::parse(); - let src_file = args.input_path; + let filename = args.input_path.file_name().unwrap().to_str().unwrap(); - println!("Building {}", src_file.display()); + let compiler = DsflCompiler::new(filename); + let result = compiler.run_sync(args.input_path).unwrap(); - let Ok(dsfl_src_code) = std::fs::read_to_string(&src_file) else { - return; - }; + let mut buffer = Cursor::new(Vec::new()); + result.write(&mut buffer).unwrap(); - let tokens = Lexer::new(dsfl_src_code).lex().unwrap(); - println!("{tokens:?}"); - - let ast = AST::parse(tokens.clone()).unwrap(); - println!("{ast:#?}"); - - println!("Successfully compiled {}", src_file.display()); + if let Err(e) = fs::write(args.output_path, buffer.into_inner()) { + eprintln!("Failed to write to output file: {e}"); + std::process::exit(1); + } } diff --git a/tooling/dsfl_compiler/src/parser.rs b/tooling/dsfl_compiler/src/parser.rs new file mode 100644 index 0000000..e8ece43 --- /dev/null +++ b/tooling/dsfl_compiler/src/parser.rs @@ -0,0 +1,253 @@ +use super::DsflError; +use std::str::FromStr; +use std::sync::atomic::AtomicUsize; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Token { + Lparen, + Rparen, + + If, + Then, + Else, + Match, + + Operator(Operator), + Int(u32), + Bool(bool), +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Operator { + Add, + Sub, + And, + Or, + Not, + Equals, +} + +pub struct Lexer { + src: Vec, + buffer: String, + tokens: Vec, +} + +impl Lexer { + pub fn new(src: String) -> Self { + Self { + src: src.chars().rev().collect(), + buffer: String::new(), + tokens: Vec::new(), + } + } + + pub fn lex(&mut self) -> Result, DsflError> { + while let Some(c) = self.src.last() { + match c { + ' ' | '\t' | '\n' => { + self.src.pop(); + } + '0'..='9' => { + let result = self.parse_numeric()?; + self.tokens.push(result); + } + 'a'..='z' | 'A'..='Z' | '_' => { + let result = self.parse_ident()?; + self.tokens.push(result); + } + '(' => { + self.tokens.push(Token::Lparen); + self.src.pop(); + } + ')' => { + self.tokens.push(Token::Rparen); + self.src.pop(); + } + _ => return Err(DsflError("invalid char for language 1")), + } + } + + Ok(self.tokens.clone()) + } + + fn parse_numeric(&mut self) -> Result { + while let Some(c) = self.src.last() { + match c { + ' ' | '\n' | '\t' | '(' | ')' | 'a'..='z' | 'A'..='Z' | '_' => break, + '0'..='9' => { + self.buffer.push(*c); + self.src.pop(); + } + _ => return Err(DsflError("invalid numeric character")), + } + } + + let result = Token::Int(u32::from_str(&self.buffer).unwrap()); + self.buffer.clear(); + Ok(result) + } + + fn parse_ident(&mut self) -> Result { + while let Some(c) = self.src.last() { + match c { + ' ' | '\n' | '\t' | '(' | ')' | '0'..='9' => break, + 'a'..='z' | 'A'..='Z' | '_' => { + self.buffer.push(*c); + self.src.pop(); + } + _ => return Err(DsflError("invalid char for language 2")), + } + } + + let result = match self.buffer.as_str() { + "true" => Ok(Token::Bool(true)), + "false" => Ok(Token::Bool(false)), + "if" => Ok(Token::If), + "then" => Ok(Token::Then), + "else" => Ok(Token::Else), + "match" => Ok(Token::Match), + "add" => Ok(Token::Operator(Operator::Add)), + "sub" => Ok(Token::Operator(Operator::Sub)), + "and" => Ok(Token::Operator(Operator::And)), + "or" => Ok(Token::Operator(Operator::Or)), + "not" => Ok(Token::Operator(Operator::Not)), + "equals" => Ok(Token::Operator(Operator::Equals)), + _ => Err(DsflError("invalid identifier")), + }; + + self.buffer.clear(); + result + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum AST { + BinaryOperation(Operator, Box, Box), + UnaryOperation(Operator, Box), + Conditional { + if_: Box, + then: Box, + else_: Box, + }, + Match(Vec), + Int(u32), + Bool(bool), +} + +static COUNTER: AtomicUsize = AtomicUsize::new(0); +fn get_unique_int() -> usize { + COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) +} + +impl AST { + pub fn emit_asm(&self, result: &mut String) -> Result<(), DsflError> { + match self { + AST::BinaryOperation(op, lhs, rhs) => { + lhs.emit_asm(result)?; + rhs.emit_asm(result)?; + + result.push_str("\tpop rg1\n\tpop rg0\n"); + let op_string = match op { + Operator::Add => "add", + Operator::Sub => "sub", + Operator::And => "and", + Operator::Or => "or", + Operator::Equals => "ieq", + _ => Err(DsflError("invalid operator for binary operation"))?, + }; + result.push_str(&format!("\t{op_string} rg0, rg1, rg0\n\tpush rg0\n")); + } + AST::UnaryOperation(op, rhs) => {} + AST::Conditional { if_, then, else_ } => {} + AST::Match(cases) => {} + AST::Int(val) => { + result.push_str(&format!("\tlli {}, rg0\n", *val as u16)); + if *val >> 16 != 0 { + result.push_str(&format!("\tlui {}, rg0\n", val >> 16)); + } + result.push_str("\tpush rg0\n"); + } + AST::Bool(val) => result.push_str(&format!( + "\tlli {}, rg0\n\tpush rg0\n", + if *val { 1 } else { 0 } + )), + }; + + Ok(()) + } + + pub fn parse(tokens: Vec) -> Result { + Self::parse_expr(&mut tokens.into_iter().rev().collect()) + } + + fn parse_expr(tokens: &mut Vec) -> Result { + println!("opening"); + + let next = tokens.pop().exp_tok()?; + + if let Token::Bool(x) = next { + return Ok(Self::Bool(x)); + }; + + if let Token::Int(x) = next { + return Ok(Self::Int(x)); + }; + + Self::expect_true(|| next == Token::Lparen)?; + + let next = tokens.pop().exp_tok()?; + + if let Token::Operator(op) = next { + let left = Self::parse_expr(tokens)?; + let right = Self::parse_expr(tokens)?; + + let t = tokens.pop().exp_tok()?; + Self::expect_true(|| t == Token::Rparen)?; + + return Ok(Self::BinaryOperation(op, Box::new(left), Box::new(right))); + }; + + if let Token::If = next { + let condition = Self::parse_expr(tokens)?; + + let t = tokens.pop().exp_tok()?; + Self::expect_true(|| t == Token::Then)?; + + let then = Self::parse_expr(tokens)?; + + let t = tokens.pop().exp_tok()?; + Self::expect_true(|| t == Token::Else)?; + + let else_ = Self::parse_expr(tokens)?; + + let t = tokens.pop().exp_tok()?; + Self::expect_true(|| t == Token::Rparen)?; + + return Ok(Self::Conditional { + if_: Box::new(condition), + then: Box::new(then), + else_: Box::new(else_), + }); + } + + Err(DsflError("no expression given")) + } + + fn expect_true(expr: impl FnOnce() -> bool) -> Result<(), DsflError> { + expr().ok_or_else(|| DsflError("Expected true expression")) + } +} + +trait ToResult { + fn exp_tok(self) -> Result; +} + +impl ToResult for Option { + fn exp_tok(self) -> Result { + match self { + Some(t) => Ok(t), + None => Err(DsflError("Unexpected EOF")), + } + } +}