started on a compiler for a simple functional programming language for the DSA.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
@@ -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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
Reference in New Issue
Block a user