started on a compiler for a simple functional programming language for the DSA.
This commit is contained in:
@@ -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
|
||||
@@ -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<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
||||
let src_file = self.src.clone();
|
||||
fn run(
|
||||
mut self: Box<Self>,
|
||||
input: impl Into<Self::Input>,
|
||||
ctx: ProcessAgent,
|
||||
) -> Result<Self::Output, Self::Error> {
|
||||
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<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 {
|
||||
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<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")),
|
||||
}
|
||||
}
|
||||
fn compile(&mut self, src: String) -> Result<String, DsflError> {
|
||||
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<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")),
|
||||
}
|
||||
}
|
||||
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<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")),
|
||||
}
|
||||
}
|
||||
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<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")),
|
||||
}
|
||||
asm.push_str(
|
||||
r#"
|
||||
// return value in bpr+8
|
||||
pop rg0
|
||||
stw rg0, bpr, 8
|
||||
|
||||
// epilogue
|
||||
pop rg1
|
||||
pop rg0
|
||||
return
|
||||
"#,
|
||||
);
|
||||
|
||||
Ok(asm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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),
|
||||
}
|
||||
|
||||
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<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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user