started on a compiler for a simple functional programming language for the DSA.

This commit is contained in:
2026-08-05 01:09:43 +01:00
parent 43e9d6942c
commit b542fc3c21
6 changed files with 332 additions and 2 deletions
@@ -35,7 +35,6 @@ fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc => {
ins_jump_unconditional(opcode, &args)
}
Opcode::Jez | Opcode::Jnz => ins_jump_conditional(opcode, &args),
Opcode::Shl | Opcode::Shr => ins_bitshift(opcode, &args),
Opcode::Add
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "dsfl_compiler"
edition.workspace = true
version.workspace = true
authors.workspace = true
[[bin]]
name = "dsfl"
path = "src/main.rs"
[lib]
name = "dsfl_compiler"
path = "src/lib.rs"
[dependencies]
clap = { version = "4.6.0", features = ["derive"] }
common = { path = "../../libraries/common" }
dsa_formats = { path = "../../libraries/dsa_formats" }
dsa_tasks = { path = "../../libraries/dsa_tasks" }
assembler = { path = "../assembler" }
+277
View File
@@ -0,0 +1,277 @@
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,
}
#[derive(Debug)]
pub struct DsflError(&'static str);
impl std::error::Error for DsflError {}
impl std::fmt::Display for DsflError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl TraceableProcess for DsflCompiler {
type Output = DseExecutable;
type Error = DsflError;
fn label(&self) -> String {
format!("Compiling {}", self.src.file_name().unwrap().display())
}
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
let src_file = self.src.clone();
ctx.log(format!("Building {}", src_file.display()));
let Ok(dsfl_src_code) = std::fs::read_to_string(&src_file) else {
return Err(DsflError("Failed to read file"));
};
// logic
let asm_output = self.compile(dsfl_src_code)?;
ctx.log(format!("Successfully compiled {}", src_file.display()));
let asm_output_file = src_file.with_extension("dsa");
ctx.log(format!("Writing result to {}", asm_output_file.display()));
let Ok(_) = std::fs::write(&asm_output_file, asm_output) else {
return Err(DsflError("failed to write dsa assembly to file"));
};
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"));
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<Vec<u8>, DsflError> {
let lexed = Lexer::new(src).lex()?;
Ok(vec![])
}
}
pub struct Lexer {
src: Vec<char>,
buffer: String,
tokens: Vec<Token>,
}
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<Vec<Token>, 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<Token, DsflError> {
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<Token, DsflError> {
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<AST>, Box<AST>),
UnaryOperation(Operator, Box<AST>),
Conditional {
if_: Box<AST>,
then: Box<AST>,
else_: Box<AST>,
},
Match(Vec<AST>),
Int(u32),
Bool(bool),
}
impl AST {
pub fn parse(tokens: Vec<Token>) -> Result<AST, DsflError> {
Self::parse_expr(&mut tokens.into_iter().rev().collect())
}
fn parse_expr(tokens: &mut Vec<Token>) -> Result<AST, DsflError> {
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<T> {
fn exp_tok(self) -> Result<T, DsflError>;
}
impl<T> ToResult<T> for Option<T> {
fn exp_tok(self) -> Result<T, DsflError> {
match self {
Some(t) => Ok(t),
None => Err(DsflError("Unexpected EOF")),
}
}
}
+33
View File
@@ -0,0 +1,33 @@
use clap::Parser;
use dsfl_compiler::{AST, Lexer};
use std::path::PathBuf;
#[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(short = 'i')]
pub input_path: PathBuf,
#[arg(short = 'o')]
pub output_path: PathBuf,
}
fn main() {
// Parse command line arguments
let args = Args::parse();
let src_file = args.input_path;
println!("Building {}", src_file.display());
let Ok(dsfl_src_code) = std::fs::read_to_string(&src_file) else {
return;
};
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());
}