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 assembler::prelude::Assembler;
|
||||||
use dsa_formats::binary_dse::DseExecutable;
|
use dsa_formats::binary_dse::DseExecutable;
|
||||||
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DsflCompiler {
|
pub struct DsflCompiler {
|
||||||
src: PathBuf,
|
filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -20,15 +22,20 @@ impl std::fmt::Display for DsflError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TraceableProcess for DsflCompiler {
|
impl TraceableProcess for DsflCompiler {
|
||||||
|
type Input = PathBuf;
|
||||||
type Output = DseExecutable;
|
type Output = DseExecutable;
|
||||||
type Error = DsflError;
|
type Error = DsflError;
|
||||||
|
|
||||||
fn label(&self) -> String {
|
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> {
|
fn run(
|
||||||
let src_file = self.src.clone();
|
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()));
|
ctx.log(format!("Building {}", src_file.display()));
|
||||||
|
|
||||||
@@ -51,227 +58,69 @@ impl TraceableProcess for DsflCompiler {
|
|||||||
|
|
||||||
ctx.log(String::from("Assembling Output..."));
|
ctx.log(String::from("Assembling Output..."));
|
||||||
|
|
||||||
let res = Box::new(Assembler::new(&asm_output_file))
|
let res = Box::new(Assembler::new(
|
||||||
.run(ctx)
|
&asm_output_file
|
||||||
.map_err(|e| DsflError("failed to run Assembler"));
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
.run(asm_output_file, ctx)
|
||||||
|
.map_err(|e| DsflError("failed to run Assembler"));
|
||||||
|
|
||||||
res
|
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 {
|
impl DsflCompiler {
|
||||||
fn compile(&mut self, src: String) -> Result<Vec<u8>, DsflError> {
|
pub fn new(filename: &str) -> Self {
|
||||||
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 {
|
Self {
|
||||||
src: src.chars().rev().collect(),
|
filename: filename.into(),
|
||||||
buffer: String::new(),
|
|
||||||
tokens: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lex(&mut self) -> Result<Vec<Token>, DsflError> {
|
fn compile(&mut self, src: String) -> Result<String, DsflError> {
|
||||||
while let Some(c) = self.src.last() {
|
let tokens = Lexer::new(src).lex()?;
|
||||||
match c {
|
let ast = AST::parse(tokens)?;
|
||||||
' ' | '\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())
|
let mut asm = String::new();
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_numeric(&mut self) -> Result<Token, DsflError> {
|
asm.push_str(
|
||||||
while let Some(c) = self.src.last() {
|
r#"
|
||||||
match c {
|
include print: "./print.dsa"
|
||||||
' ' | '\n' | '\t' | '(' | ')' | 'a'..='z' | 'A'..='Z' | '_' => break,
|
dw stack: 0x10000
|
||||||
'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());
|
_init:
|
||||||
self.buffer.clear();
|
ldw stack, bpr
|
||||||
Ok(result)
|
mov bpr, spr
|
||||||
}
|
main:
|
||||||
|
push zero
|
||||||
|
call program
|
||||||
|
call print::print_hex_word
|
||||||
|
pop zero
|
||||||
|
hlt
|
||||||
|
|
||||||
fn parse_ident(&mut self) -> Result<Token, DsflError> {
|
program: func
|
||||||
while let Some(c) = self.src.last() {
|
push rg0
|
||||||
match c {
|
push rg1
|
||||||
' ' | '\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() {
|
ast.emit_asm(&mut asm)?;
|
||||||
"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();
|
asm.push_str(
|
||||||
result
|
r#"
|
||||||
}
|
// return value in bpr+8
|
||||||
}
|
pop rg0
|
||||||
|
stw rg0, bpr, 8
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum AST {
|
// epilogue
|
||||||
BinaryOperation(Operator, Box<AST>, Box<AST>),
|
pop rg1
|
||||||
UnaryOperation(Operator, Box<AST>),
|
pop rg0
|
||||||
Conditional {
|
return
|
||||||
if_: Box<AST>,
|
"#,
|
||||||
then: Box<AST>,
|
);
|
||||||
else_: Box<AST>,
|
|
||||||
},
|
Ok(asm)
|
||||||
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")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use clap::Parser;
|
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;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
@@ -15,19 +18,16 @@ fn main() {
|
|||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
let args = Args::parse();
|
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 {
|
let mut buffer = Cursor::new(Vec::new());
|
||||||
return;
|
result.write(&mut buffer).unwrap();
|
||||||
};
|
|
||||||
|
|
||||||
let tokens = Lexer::new(dsfl_src_code).lex().unwrap();
|
if let Err(e) = fs::write(args.output_path, buffer.into_inner()) {
|
||||||
println!("{tokens:?}");
|
eprintln!("Failed to write to output file: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
let ast = AST::parse(tokens.clone()).unwrap();
|
}
|
||||||
println!("{ast:#?}");
|
|
||||||
|
|
||||||
println!("Successfully compiled {}", src_file.display());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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