DSCS IR generation progress

This commit is contained in:
2026-08-22 09:40:46 +01:00
parent 75c127d4ff
commit e35ba0e511
20 changed files with 1973 additions and 1669 deletions
+5
View File
@@ -0,0 +1,5 @@
fn main() {
__main_0:
_ = print::print(v_0)
return
}
+8 -8
View File
@@ -1,5 +1,5 @@
// GENERATED BY DSC COMPILER // GENERATED BY DSC COMPILER
// Generated at 2026-08-20 11:13:23 // Generated at 2026-08-21 19:34:30
// Imports // Imports
include alloc: "./lib/memory/block_alloc.dsa" include alloc: "./lib/memory/block_alloc.dsa"
@@ -30,7 +30,7 @@ _ret:
pop bpr pop bpr
ret ret
db str_4: "successful free of ptr" db str_8: "successful free of ptr"
// fn main() -> u32 // fn main() -> u32
main: main:
push bpr push bpr
@@ -121,18 +121,18 @@ main:
ldw spr, rg2, 8 ldw spr, rg2, 8
lli 0, rg4 lli 0, rg4
ieq rg2, rg4, rg5 ieq rg2, rg4, rg5
jez rg5, _else_2 jez rg5, _else_6
_then_1: _then_5:
lwi str_4, rg4 lwi str_8, rg4
stw rg2, spr, 8 stw rg2, spr, 8
// push arg 0 // push arg 0
push rg4 push rg4
call print::print call print::print
pop zero pop zero
jmp _end_3 jmp _end_7
_else_2: _else_6:
nop nop
_end_3: _end_7:
lli 0, rg4 lli 0, rg4
stw rg4, bpr, 8 stw rg4, bpr, 8
jmp _ret jmp _ret
+44
View File
@@ -0,0 +1,44 @@
fn main() {
__main_0:
v_1 = literal 64
v_2 = literal 32
v_0 = alloc::init(v_1, v_2)
v_3 = copy v_0
_ = print::print_hex_word(v_3)
_ = print::print_newline()
v_5 = copy v_0
v_4 = alloc::alloc(v_5)
v_6 = copy v_4
_ = print::print_hex_word(v_6)
v_7 = copy v_4
v_8 = literal 200
store v_7, v_8
_ = print::print_newline()
v_10 = copy v_0
v_9 = alloc::alloc(v_10)
v_11 = copy v_9
_ = print::print_hex_word(v_11)
_ = print::print_newline()
_ = print::print_num(v_12)
v_13 = copy v_0
_ = alloc::free(v_13, v_14)
v_16 = copy v_0
v_15 = alloc::alloc(v_16)
_ = print::print_newline()
v_17 = copy v_15
_ = print::print_hex_word(v_17)
_ = print::print_newline()
v_18 = copy v_4
_ = print::print_hex_word(v_18)
if v_19 then __then_2 else __else_2
__then_2:
_ = print::print(v_20)
jump __endif_2
__else_2:
jump __endif_2
__endif_2:
v_21 = literal 0
return v_21
__unreachable_3:
return
}
+10 -7
View File
@@ -1,12 +1,15 @@
use super::instruction::{InsBlock as IB, Instruction as I, Label}; use super::instruction::InsBlock as IB;
use super::registers::{Register, RegisterAllocator}; use super::registers::RegisterAllocator;
use crate::backend::instruction::Comparison::{ use crate::model::CompilerError;
use crate::model::ast::{
AssignmentOperator, BinaryOperator, Call, ConstExpr, Declaration, Dependency, Expression,
Number, Program, Statement, TypeId, UnaryOperator, Variable,
};
use crate::model::instruction::Comparison::{
Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual, Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
}; };
use crate::model::{ use crate::model::instruction::{Instruction as I, Label};
AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency, use crate::model::register::Register;
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
};
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use dsa_tasks::ProcessAgent; use dsa_tasks::ProcessAgent;
use std::collections::HashMap; use std::collections::HashMap;
@@ -1,6 +1,4 @@
use std::fmt; use crate::model::instruction::Instruction;
use crate::backend::registers::Register;
pub struct InsBlock { pub struct InsBlock {
instructions: Vec<Instruction>, instructions: Vec<Instruction>,
@@ -55,804 +53,3 @@ impl From<Instruction> for InsBlock {
} }
} }
} }
pub enum Comparison {
Greater,
GreaterEqual,
Equal,
NotEqual,
Less,
LessEqual,
}
#[derive(Debug, Clone)]
pub enum Instruction {
// Labels and comments
Label(Label),
Comment {
text: String,
top_level: bool,
},
Newline,
// Data Directives
Db {
label: String,
data: Vec<u8>,
},
Dh {
label: String,
data: Vec<u16>,
},
Dw {
label: String,
data: Vec<u32>,
},
DString {
// alias for db.
label: String,
data: String,
},
Resx {
label: String,
size: u32,
},
// Include
Include {
name: String,
path: String,
},
// Data movement
Mov {
src: Register,
dest: Register,
},
Movs {
src: Register,
dest: Register,
},
// Memory operations
Ldb {
src: MemOperand,
dest: Register,
},
Ldh {
src: MemOperand,
dest: Register,
},
Ldw {
src: MemOperand,
dest: Register,
},
Stb {
src: Register,
dest: MemOperand,
},
Sth {
src: Register,
dest: MemOperand,
},
Stw {
src: Register,
dest: MemOperand,
},
// Immediate loads
Lli {
imm: Imm,
dest: Register,
},
Lui {
imm: Imm,
dest: Register,
},
Lwi {
imm: Imm,
dest: Register,
},
LwiLabel {
label: String,
dest: Register,
},
// Arithmetic
Add {
src1: Register,
src2: Register,
dest: Register,
},
Sub {
src1: Register,
src2: Register,
dest: Register,
},
AddI {
src: Register,
imm: Imm,
dest: Option<Register>,
},
SubI {
src: Register,
imm: Imm,
dest: Option<Register>,
},
// Bitwise
And {
src1: Register,
src2: Register,
dest: Register,
},
Or {
src1: Register,
src2: Register,
dest: Register,
},
Xor {
src1: Register,
src2: Register,
dest: Register,
},
Not {
src: Register,
dest: Register,
},
Nand {
src1: Register,
src2: Register,
dest: Register,
},
Nor {
src1: Register,
src2: Register,
dest: Register,
},
Xnor {
src1: Register,
src2: Register,
dest: Register,
},
// Shifts
Shl {
src1: Register,
r_shamt: Register,
i_shamt: u16,
dest: Register,
},
Shr {
src1: Register,
r_shamt: Register,
i_shamt: u16,
dest: Register,
},
Ieq {
src1: Register,
src2: Register,
dest: Register,
},
Ine {
src1: Register,
src2: Register,
dest: Register,
},
Ilt {
src1: Register,
src2: Register,
dest: Register,
},
Ile {
src1: Register,
src2: Register,
dest: Register,
},
Igt {
src1: Register,
src2: Register,
dest: Register,
},
Ige {
src1: Register,
src2: Register,
dest: Register,
},
// Jumps
Jmp {
target: Label,
},
Jnz {
src: Register,
target: Label,
},
Jez {
src: Register,
target: Label,
},
Jic {
target: Label,
},
Jnc {
target: Label,
},
// Stack
Push {
reg: Register,
},
Pop {
reg: Register,
},
// Function calls
Call {
target: String,
}, // namespace::function
Return,
// System
Hlt,
Nop,
Int {
code: u8,
},
}
pub enum DataDirective {
U8(Vec<u8>),
U16(Vec<u16>),
U32(Vec<u32>),
String(String),
Char(char),
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Label(l) => write!(f, "{}:", l),
Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
// automatically.
Self::Comment { text, top_level } => write!(
f,
"{}",
text.lines()
.map(|line| format!(
"{}// {}",
if *top_level { "" } else { " " },
line.trim(),
))
.collect::<Vec<String>>()
.join("\n")
),
Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
Self::Db { label, data } => write!(
f,
"db {}: {}",
label,
data.iter()
.map(|&b| format!("{:#04X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::Dh { label, data } => write!(
f,
"dh {}: {}",
label,
data.iter()
.map(|&b| format!("{:#06X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::Dw { label, data } => write!(
f,
"dw {}: {}",
label,
data.iter()
.map(|&b| format!("{:#08X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
Self::Ldb { src: addr, dest } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " ldb {}, {}, {}", reg, dest, offset)
}
Self::Ldh { src: addr, dest } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " ldh {}, {}, {}", reg, dest, offset)
}
Self::Ldw { src, dest } => {
let (reg, offset) = reg_and_offset(src);
write!(f, " ldw {}, {}, {}", reg, dest, offset)
}
// Self::Ldbs { addr, dest } => {
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldhs { addr, dest } => {
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldws { addr, dest } => {
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
// }
Self::Stb { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " stb {}, {}, {}", src, reg, offset)
}
Self::Sth { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " sth {}, {}, {}", src, reg, offset)
}
Self::Stw { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " stw {}, {}, {}", src, reg, offset)
}
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
// arithmetic
Self::Add { src1, src2, dest } => {
write!(f, " add {}, {}, {}", src1, src2, dest)
}
Self::Sub { src1, src2, dest } => {
write!(f, " sub {}, {}, {}", src1, src2, dest)
}
Self::And { src1, src2, dest } => {
write!(f, " and {}, {}, {}", src1, src2, dest)
}
Self::Or { src1, src2, dest } => {
write!(f, " or {}, {}, {}", src1, src2, dest)
}
Self::Nand { src1, src2, dest } => {
write!(f, " nand {}, {}, {}", src1, src2, dest)
}
Self::Xor { src1, src2, dest } => {
write!(f, " xor {}, {}, {}", src1, src2, dest)
}
Self::Nor { src1, src2, dest } => {
write!(f, " nor {}, {}, {}", src1, src2, dest)
}
Self::Not { src, dest } => {
write!(f, " not {} {}", src, dest)
}
Self::Xnor { src1, src2, dest } => {
write!(f, " xnor {}, {}, {}", src1, src2, dest)
}
Self::AddI { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " addi {}, {}, {}", src, imm, d)
} else {
write!(f, " addi {}, {}", src, imm)
}
}
Self::SubI { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " subi {}, {}, {}", src, imm, d)
} else {
write!(f, " subi {}, {}", src, imm)
}
}
// shift instructions
Self::Shl {
src1,
r_shamt,
i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
Self::Shr {
src1,
r_shamt,
i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
// jump instructions
Self::Jmp { target } => write!(f, " jmp {}", target),
Self::Jic { target } => write!(f, " jic {}", target),
Self::Jnc { target } => write!(f, " jnc {}", target),
Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
// stack instructions
Self::Push { reg } => write!(f, " push {}", reg),
Self::Pop { reg } => write!(f, " pop {}", reg),
// call & return instructions
Self::Call { target } => write!(f, " call {}", target),
Self::Return => write!(f, " ret"),
// misc instructions
Self::Int { code } => write!(f, " int {}", code),
Self::Hlt => write!(f, " hlt"),
Self::Nop => write!(f, " nop"),
}
}
}
impl Instruction {
// data directives
pub fn db_string(label: impl Into<String>, data: impl Into<String>) -> Self {
Self::DString {
label: label.into(),
data: data.into(),
}
}
pub fn db_word(label: impl Into<String>, data: u32) -> Self {
Self::Dw {
label: label.into(),
data: vec![data],
}
}
pub fn db_bytes(label: impl Into<String>, data: &[u8]) -> Self {
Self::Db {
label: label.into(),
data: data.to_vec(),
}
}
// Movement
pub fn mov<R1, R2>(src: R1, dest: R2) -> Self
where
R1: Into<Register>,
R2: Into<Register>,
{
Self::Mov {
src: src.into(),
dest: dest.into(),
}
}
// Memory loads
pub fn ldw_reg<R>(base: R, dest: Register) -> Self
where
R: Into<Register>,
{
Self::Ldw {
src: MemOperand::RegIndirect(base.into()),
dest,
}
}
pub fn ldw_reg_offset<R>(base: R, dest: Register, offset: i32) -> Self
where
R: Into<Register>,
{
Self::Ldw {
src: MemOperand::RegOffset(base.into(), offset),
dest,
}
}
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
Self::Ldw {
src: MemOperand::Label(label.into()),
dest,
}
}
// Memory stores
pub fn stw_reg<R>(src: Register, base: R) -> Self
where
R: Into<Register>,
{
Self::Stw {
src,
dest: MemOperand::RegIndirect(base.into()),
}
}
pub fn stw_reg_offset<R>(src: Register, base: R, offset: i32) -> Self
where
R: Into<Register>,
{
Self::Stw {
src,
dest: MemOperand::RegOffset(base.into(), offset),
}
}
pub fn stw_label(src: Register, label: impl Into<Label>) -> Self {
Self::Stw {
src,
dest: MemOperand::Label(label.into()),
}
}
// Arithmetic
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
Self::Add { src1, src2, dest }
}
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
Self::Sub { src1, src2, dest }
}
pub fn and(src1: Register, src2: Register, dest: Register) -> Self {
Self::And { src1, src2, dest }
}
pub fn or(src1: Register, src2: Register, dest: Register) -> Self {
Self::Or { src1, src2, dest }
}
pub fn xor(src1: Register, src2: Register, dest: Register) -> Self {
Self::Xor { src1, src2, dest }
}
pub fn not(src: Register, dest: Register) -> Self {
Self::Not { src, dest }
}
pub fn shl(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
Self::Shl {
src1,
r_shamt,
i_shamt,
dest,
}
}
pub fn shr(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
Self::Shr {
src1,
r_shamt,
i_shamt,
dest,
}
}
pub fn iadd(src: Register, value: i64) -> Self {
let imm = Imm(value.unsigned_abs() as u32);
if value < 0 {
Self::SubI {
src,
imm,
dest: None,
}
} else {
Self::AddI {
src,
imm,
dest: None,
}
}
}
pub fn iadd_dest(src: Register, value: i32, dest: Register) -> Self {
let imm = Imm(value.unsigned_abs());
if value < 0 {
Self::SubI {
src,
imm,
dest: Some(dest),
}
} else {
Self::AddI {
src,
imm,
dest: Some(dest),
}
}
}
pub fn inc(reg: Register) -> Self {
Self::AddI {
src: reg,
imm: Imm(1),
dest: None,
}
}
pub fn dec(reg: Register) -> Self {
Self::SubI {
src: reg,
imm: Imm(1),
dest: None,
}
}
// Immediate loads
pub fn lwi(value: u32, dest: Register) -> Self {
if value > 0xFFFF {
Self::Lwi {
imm: Imm(value),
dest,
}
} else {
Self::Lli {
imm: Imm(value),
dest,
}
}
}
pub fn lwi_label(label: impl Into<String>, dest: Register) -> Self {
Self::LwiLabel {
label: label.into(),
dest,
}
}
// Control flow
pub fn label(name: impl Into<String>) -> Self {
Self::Label(Label(name.into()))
}
pub fn cmp(operand: Comparison, src1: Register, src2: Register, dest: Register) -> Self {
match operand {
Comparison::Equal => Self::Ieq { src1, src2, dest },
Comparison::NotEqual => Self::Ine { src1, src2, dest },
Comparison::Greater => Self::Igt { src1, src2, dest },
Comparison::GreaterEqual => Self::Ige { src1, src2, dest },
Comparison::Less => Self::Ilt { src1, src2, dest },
Comparison::LessEqual => Self::Ile { src1, src2, dest },
}
}
pub fn jmp(target: impl Into<Label>) -> Self {
Self::Jmp {
target: target.into(),
}
}
pub fn jnz(src: Register, target: impl Into<Label>) -> Self {
Self::Jnz {
src,
target: target.into(),
}
}
pub fn jez(src: Register, target: impl Into<Label>) -> Self {
Self::Jez {
src,
target: target.into(),
}
}
pub fn jic(target: impl Into<Label>) -> Self {
Self::Jic {
target: target.into(),
}
}
pub fn jnc(target: impl Into<Label>) -> Self {
Self::Jnc {
target: target.into(),
}
}
// Stack
pub fn push(reg: Register) -> Self {
Self::Push { reg }
}
pub fn pop(reg: Register) -> Self {
Self::Pop { reg }
}
// Functions
pub fn call(target: impl Into<String>) -> Self {
Self::Call {
target: target.into(),
}
}
pub fn int(code: u8) -> Self {
Self::Int { code }
}
pub fn ret() -> Self {
Self::Return
}
// Utilities
pub fn comment(text: impl Into<String>) -> Self {
Self::Comment {
text: text.into(),
top_level: false,
}
}
pub fn global_comment(text: impl Into<String>) -> Self {
Self::Comment {
text: text.into(),
top_level: true,
}
}
pub fn include(name: impl Into<String>, path: impl Into<String>) -> Self {
Self::Include {
name: name.into(),
path: path.into(),
}
}
}
// Convenience trait for Label conversion
impl From<String> for Label {
fn from(s: String) -> Self {
Label(s)
}
}
impl From<&str> for Label {
fn from(s: &str) -> Self {
Label(s.to_string())
}
}
fn reg_and_offset(op: &MemOperand) -> (String, i32) {
match op {
MemOperand::RegIndirect(reg) => (reg.to_string(), 0),
MemOperand::RegOffset(reg, offset) => (reg.to_string(), *offset),
MemOperand::Label(label) => (label.to_string(), 0),
MemOperand::LabelOffset(label, offset) => (label.to_string(), *offset),
}
}
/// Memory operand for loads/stores
#[derive(Debug, Clone)]
pub enum MemOperand {
/// Register indirect: [reg]
RegIndirect(Register),
/// Register with offset: [reg + offset]
RegOffset(Register, i32),
/// Label: [label]
Label(Label),
/// Label with offset: [label + offset]
LabelOffset(Label, i32),
}
/// Immediate value (16-bit or 32-bit)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Imm(pub u32);
impl fmt::Display for Imm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Label reference
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Label(pub String);
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
@@ -1,102 +0,0 @@
use std::fmt;
use std::fmt::Formatter;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
pub enum Statement {
Const(VirtualReg, Literal),
BinaryOp { left: VirtualReg, op: PrimitiveOperation,
right: VirtualReg, result: VirtualReg },
AddressOf {
value: VirtualReg, result: VirtualReg },
Dereference {
address: VirtualReg, result: VirtualReg },
GetStructField {
struct_addr: VirtualReg, result: VirtualReg, offset: usize },
SetStructField {
struct_addr: VirtualReg, value: VirtualReg, offset: usize },
Call {
return_value: VirtualReg, func: FunctionName, args: Vec<VirtualReg> },
}
pub struct IRProgram {
pub functions: Vec<Function>,
}
pub struct Function {
pub name: FunctionName,
pub params: Vec<VirtualReg>,
pub blocks: Vec<BasicBlock>,
}
pub struct BasicBlock {
pub label: Label,
pub statements: Vec<Statement>,
pub terminator: Terminator,
}
pub enum Terminator {
Jump(Label),
ConditionalJump { if_flag: VirtualReg, then_goto: Label, else_goto: Label },
Return(VirtualReg),
ReturnVoid,
}
pub type FunctionName = String;
#[derive(Clone)]
pub struct Label {
id: usize,
name: Rc<String>
}
impl Label {
pub fn new_with_id(name: impl Into<String>, id: usize) -> Self {
Self { id, name: Rc::new(name.into()) }
}
pub fn new(name: impl Into<String>) -> Self {
Self { id: Self::get_new_id(), name: Rc::new(name.into()) }
}
pub fn get_new_id() -> usize {
LABEL_ID.fetch_add(1, Ordering::Relaxed)
}
}
impl fmt::Display for Label {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "__{}_{}", self.name, self.id)
}
}
static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VirtualReg(pub isize);
impl VirtualReg {
pub fn void() -> Self {
Self(-1)
}
}
pub enum Literal {
UInt(usize),
Int(isize),
Char(char),
}
pub enum PrimitiveOperation {
Add,
Sub,
And,
Or,
Xor,
Nor,
Nand
}
@@ -1,445 +1,540 @@
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use clap::builder::Resettable::Value;
use dsa_tasks::ProcessAgent; use dsa_tasks::ProcessAgent;
use crate::backend::scope::Allocator;
use crate::model::{Block, Call, CompilerError, Declaration, Expression, Program, Statement, TypeId, Variable};
use crate::backend::intermediate_rep as IR; use crate::model::ast::{
use crate::backend::intermediate_rep::{BasicBlock, Label, Terminator, VirtualReg}; Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, Variable,
};
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, Terminator, VirtualReg};
use crate::model::{CompilerError, dsa_ir as IR};
pub struct IrLoweringCodeGenerator { pub struct IrLoweringCodeGenerator {
ast: Program, ast: Program,
functions: Vec<IR::Function> functions: Vec<IR::Function>,
} }
impl IrLoweringCodeGenerator { impl IrLoweringCodeGenerator {
pub fn new(ast: Program) -> Self { pub fn new(ast: Program) -> Self {
Self { ast, functions: vec![] } Self {
} ast,
functions: vec![],
}
}
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> { pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
for block in self.ast.clone().declarations { for block in self.ast.clone().declarations {
match block { match block {
Declaration::Variable { ..} => todo!(), Declaration::Variable { .. } => {}
Declaration::Dependency(_) => todo!(), Declaration::Dependency(_) => {}
Declaration::Function { name, ..} => todo!(), Declaration::Function { name, .. } => {}
Declaration::Struct { .. } => todo!(), Declaration::Struct { .. } => {}
} }
} }
for block in self.ast.clone().declarations { for block in self.ast.clone().declarations {
self.generate_block(&block)?; self.generate_block(&block)?;
} }
self.output(ctx) self.output(ctx)
} }
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> { pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
todo!() Ok(IRProgram {
} functions: self.functions.clone(),
})
}
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> { pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
match decl { match decl {
Declaration::Function { name, params, body, return_type } => { Declaration::Function {
let function = self.generate_function(name, params, body, return_type)?; name,
self.functions.push(function); params,
}, body,
Declaration::Struct { .. } => todo!("no codegen required for structs yet"), return_type,
Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"), } => {
Declaration::Variable { .. } => todo!("codegen for statics and constants"), let function = self.generate_function(name, params, body, return_type)?;
} self.functions.push(function);
}
// Declaration::Struct { .. } => todo!("no codegen required for structs yet"),
// Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"),
// Declaration::Variable { .. } => todo!("codegen for statics and constants"),
_ => {}
}
Ok(()) Ok(())
} }
pub fn generate_function( pub fn generate_function(
&mut self, function_name: &str, &mut self,
params: &[Variable], function_name: &str,
body: &Block, params: &[Variable],
return_type: &TypeId body: &Block,
) -> Result<IR::Function, CompilerError> { return_type: &TypeId,
let mut context = FunctionContext::new(Label::new(function_name.to_string())); ) -> Result<IR::Function, CompilerError> {
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
let params = (0..params.len()) let params = (0..params.len()).map(|_| context.new_vreg()).collect();
.map(|_| context.new_vreg()).collect();
self.collapse_block(body, &mut context)?; self.collapse_block(body, &mut context)?;
let blocks = context.terminate(IR::Terminator::ReturnVoid); let blocks = context.terminate(IR::Terminator::ReturnVoid);
Ok(IR::Function { Ok(IR::Function {
name: function_name.to_string(), params, blocks, name: function_name.to_string(),
}) params,
} blocks,
})
}
fn collapse_block( fn collapse_block(
&self, &self,
block: &Block, block: &Block,
ctx: &mut FunctionContext, ctx: &mut FunctionContext,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerError> {
for statement in block { for statement in block {
self.collapse_statement(statement, ctx)?; self.collapse_statement(statement, ctx)?;
} }
Ok(()) Ok(())
} }
fn collapse_statement( fn collapse_statement(
&self, &self,
stmt: &Statement, stmt: &Statement,
ctx: &mut FunctionContext, ctx: &mut FunctionContext,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerError> {
match stmt { match stmt {
Statement::Block(block) => { Statement::Block(block) => self.collapse_block(block, ctx),
self.collapse_block(block, ctx)
}
Statement::If { condition, then_stmt, else_stmt } => { Statement::If {
/* if statement expansion. produces the following layout condition,
reg_cond = ... expansion of {condition} ... then_stmt,
if reg_cond goto then_{id} else goto else_{id} else_stmt,
then_{id}: } => {
... expansion of {then_stmt} ... /* if statement expansion. produces the following layout
goto end_{id} reg_cond = ... expansion of {condition} ...
else_{id}: if reg_cond goto then_{id} else goto else_{id}
... expansion of {else_stmt} ... then_{id}:
goto end_{id} ... expansion of {then_stmt} ...
end_{id}: goto end_{id}
... not our responsibility ... else_{id}:
*/ ... expansion of {else_stmt} ...
let id = Label::get_new_id(); goto end_{id}
let then_label = Label::new_with_id("then", id); end_{id}:
let else_label = Label::new_with_id("else", id); ... not our responsibility ...
let end_label = Label::new_with_id("endif", id); */
let id = Label::get_new_id();
let then_label = Label::new_with_id("then", id);
let else_label = Label::new_with_id("else", id);
let end_label = Label::new_with_id("endif", id);
let eval_result = self.collapse_expression(condition, ctx)?; let eval_result = ctx.new_vreg();
self.collapse_expression(condition, ctx, eval_result)?;
ctx.next_block( ctx.next_block(
Terminator::ConditionalJump { Terminator::ConditionalJump {
if_flag: eval_result, if_flag: eval_result,
then_goto: then_label.clone(), then_goto: then_label.clone(),
else_goto: else_label.clone(), else_goto: else_label.clone(),
}, },
then_label.clone() then_label.clone(),
); );
self.collapse_block(then_stmt, ctx)?; self.collapse_block(then_stmt, ctx)?;
ctx.next_block( ctx.next_block(Terminator::Jump(end_label.clone()), else_label.clone());
Terminator::Jump(end_label.clone()),
else_label.clone()
);
self.collapse_block(else_stmt, ctx)?; self.collapse_block(else_stmt, ctx)?;
ctx.next_block( ctx.next_block(Terminator::Jump(end_label.clone()), end_label.clone());
Terminator::Jump(end_label.clone()),
end_label.clone()
);
Ok(()) Ok(())
} }
Statement::Defer(call) => { Statement::Defer(call) => {
// defers stmt. all currently in scope defers are called in FILO order on return // defers stmt. all currently in scope defers are called in FILO order on return
ctx.defer(call.clone()); ctx.defer(call.clone());
Ok(()) Ok(())
}, }
Statement::Return(expr) => { Statement::Return(expr) => {
// return stmt. calls relevant defer methods and expands the body of the return statement. // return stmt. calls relevant defer methods and expands the body of the return statement.
let result = if let Some(value) = expr {
let result_reg = ctx.new_vreg();
self.collapse_expression(value, ctx, result_reg)?;
result_reg
} else {
VirtualReg::void()
};
let result = if let Some(value) = expr { // run cleanup logic (defers)
self.collapse_expression(value, ctx)? for call in ctx.get_deferred_calls() {
} else { VirtualReg::void() }; self.collapse_call(&call, ctx, VirtualReg::void())?;
}
// run cleanup logic (defers) ctx.next_block(Terminator::Return(result), Label::new("unreachable"));
for call in ctx.get_deferred_calls() {
self.collapse_call(&call, ctx)?;
}
ctx.next_block(Terminator::Return(result), Label::new("unreachable")); Ok(())
}
Ok(()) Statement::Expression { expr } => {
} // generate the code for the expression but discard the result
// let the optimiser remove it if it provably has no side effects.
self.collapse_expression(expr, ctx, VirtualReg::void())?;
Ok(())
}
Statement::Expression { expr } => { Statement::Continue => {
// generate the code for the expression but discard the result // continue stmt. goes to the 'start' block of the loop
// let the optimiser remove it if it provably has no side effects.
let _ = self.collapse_expression(expr, ctx)?; let Some(scope) = ctx.current_scope() else {
Ok(()) return Err(CompilerError::generic(
} "Invalid 'Continue' statement (not inside any loop)",
));
};
Statement::Continue => { ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
// continue stmt. goes to the 'start' block of the loop Ok(())
}
let Some(scope) = ctx.current_scope() else { Statement::Break => {
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)")); // break stmt. jumps to the 'end' block of the loop
};
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable")); let Some(scope) = ctx.current_scope() else {
Ok(()) return Err(CompilerError::generic(
}, "Invalid 'Continue' statement (not inside any loop)",
));
};
Statement::Break => { ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable"));
// break stmt. jumps to the 'end' block of the loop Ok(())
}
let Some(scope) = ctx.current_scope() else { Statement::While { condition, body } => {
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)")); /* while loop. produces the following layout
}; goto while_check_{id}
while_check_{id}:
if {condition} goto while_body_{id} else goto while_end_{id}
while_body_{id}:
... expansion of {body} ...
goto while_check_{id}
while_end_{id}:
... not our responsibility ...
*/
ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable")); let id = Label::get_new_id();
Ok(()) let check_label = Label::new_with_id("while_check", id);
}, let body_label = Label::new_with_id("while_body", id);
let end_label = Label::new_with_id("while_end", id);
Statement::While { condition, body } => { ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
/* while loop. produces the following layout
goto while_check_{id}
while_check_{id}:
if {condition} goto while_body_{id} else goto while_end_{id}
while_body_{id}:
... expansion of {body} ...
goto while_check_{id}
while_end_{id}:
... not our responsibility ...
*/
let id = Label::get_new_id(); let if_flag = ctx.new_vreg();
let check_label = Label::new_with_id("while_check", id); self.collapse_expression(condition, ctx, if_flag)?;
let body_label = Label::new_with_id("while_body", id);
let end_label = Label::new_with_id("while_end", id);
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone()); ctx.next_block(
Terminator::ConditionalJump {
if_flag,
then_goto: body_label.clone(),
else_goto: end_label.clone(),
},
body_label.clone(),
);
let result = self.collapse_expression(condition, ctx)?; self.collapse_block(body, ctx)?;
ctx.next_block( ctx.next_block(Terminator::Jump(check_label.clone()), end_label.clone());
Terminator::ConditionalJump {
if_flag: result,
then_goto: body_label.clone(),
else_goto: end_label.clone()
},
body_label.clone(),
);
self.collapse_block(body, ctx)?; Ok(())
}
ctx.next_block( Statement::Loop(block) => {
Terminator::Jump(check_label.clone()), /* indefinite loop. produces the following layout
end_label.clone() goto start_loop_{id}
); start_loop_{id}:
goto start_loop_{id}
while_end_{id}:
... not our responsibility ...
*/
Ok(()) let id = Label::get_new_id();
} let start_label = Label::new_with_id("start_loop", id);
let end_label = Label::new_with_id("end_loop", id);
Statement::Loop(block) => { ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone());
/* indefinite loop. produces the following layout
goto start_loop_{id}
start_loop_{id}:
goto start_loop_{id}
while_end_{id}:
... not our responsibility ...
*/
let id = Label::get_new_id(); ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone()));
let start_label = Label::new_with_id("start_loop", id);
let end_label = Label::new_with_id("end_loop", id);
ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone()); self.collapse_block(block, ctx)?;
ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone())); ctx.exit_scope();
self.collapse_block(block, ctx)?; ctx.next_block(Terminator::Jump(start_label.clone()), end_label);
ctx.exit_scope(); Ok(())
}
ctx.next_block(Terminator::Jump(start_label.clone()), end_label); Statement::Declaration { var, value } => {
let Some(expr) = value else {
ctx.new_local(VirtualReg::void(), var.clone());
return Ok(());
};
Ok(()) let v_reg = ctx.new_vreg();
}, self.collapse_expression(expr, ctx, v_reg)?;
_ => todo!() ctx.new_local(v_reg, var.clone());
} Ok(())
} }
fn collapse_call( Statement::Assign {
&self, varname,
call: &Call, value,
ctx: &mut FunctionContext operator,
) -> Result<VirtualReg, CompilerError> { } => {
let reg = ctx.new_vreg(); let Some(v_reg) = ctx.find_local(varname) else {
return Err(CompilerError::generic(format!(
"Variable not defined: {varname}"
)));
};
let args = call.args.iter() self.collapse_expression(value, ctx, v_reg)?;
.map(|arg| self.collapse_expression(&arg, ctx)) Ok(())
.collect::<Result<Vec<VirtualReg>, _>>()?; } // todo!!!!
ctx.push_stmt(IR::Statement::Call { Statement::PtrWrite { ptr, value } => {
return_value: reg, let ptr_reg = ctx.new_vreg();
func: call.name.to_string(), self.collapse_expression(&ptr, ctx, ptr_reg)?;
args
});
Ok(reg) let value_reg = ctx.new_vreg();
} self.collapse_expression(&value, ctx, value_reg)?;
fn collapse_expression( ctx.push_stmt(IR::Statement::Store {
&self, value: value_reg,
expr: &Expression, address: ptr_reg,
ctx: &mut FunctionContext, });
) -> Result<VirtualReg, CompilerError> {
todo!() Ok(())
} }
}
}
fn collapse_call(
&self,
call: &Call,
ctx: &mut FunctionContext,
v_reg: VirtualReg,
) -> Result<(), CompilerError> {
let args = call
.args
.iter()
.map(|arg| {
let v_reg = ctx.new_vreg();
self.collapse_expression(&arg, ctx, v_reg).map(|_| v_reg)
})
.collect::<Result<Vec<VirtualReg>, _>>()?;
ctx.push_stmt(IR::Statement::Call {
return_value: v_reg,
func: call.name.to_string(),
args,
});
Ok(())
}
fn collapse_expression(
&self,
expr: &Expression,
ctx: &mut FunctionContext,
v_reg: VirtualReg,
) -> Result<(), CompilerError> {
match expr {
Expression::Empty => Ok(()),
Expression::Call { func, .. } => {
self.collapse_call(func, ctx, v_reg)?;
Ok(())
}
Expression::CharLiteral(c) => {
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Char(*c)));
Ok(())
}
Expression::Number(num) => {
match num {
Number::Signed(val, ..) => {
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Int(*val as isize)));
}
Number::Unsigned(val, ..) => {
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::UInt(*val as usize)));
}
}
Ok(())
}
Expression::Variable { name, expr_type } => {
let Some(var) = ctx.find_local(&name.name) else {
return Err(CompilerError::generic(format!(
"Variable not defined: {name}"
)));
};
ctx.push_stmt(IR::Statement::Copy {
src: var,
dst: v_reg,
});
Ok(())
}
// TODO! all of the below:
Expression::Binary { .. } => Ok(()),
Expression::Unary { .. } => Ok(()),
Expression::UnaryPostfix { .. } => Ok(()),
Expression::TypeCast { .. } => Ok(()),
Expression::IndexAccess { .. } => Ok(()),
Expression::MemberAccess { .. } => Ok(()),
Expression::StringLiteral(_) => Ok(()),
Expression::ArrayLiteral { .. } => Ok(()),
Expression::StructLiteral { .. } => Ok(()),
}
}
} }
struct BasicBlockBuilder { struct BasicBlockBuilder {
label: Label, label: Label,
statements: Vec<IR::Statement> statements: Vec<IR::Statement>,
} }
impl BasicBlockBuilder { impl BasicBlockBuilder {
fn new(label: Label) -> Self { fn new(label: Label) -> Self {
Self { Self {
label, label,
statements: vec![] statements: vec![],
} }
} }
pub fn push(&mut self, statement: IR::Statement) { pub fn push(&mut self, statement: IR::Statement) {
self.statements.push(statement); self.statements.push(statement);
} }
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock { pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
BasicBlock { BasicBlock {
label: std::mem::replace(&mut self.label, label), label: std::mem::replace(&mut self.label, label),
statements: std::mem::take(&mut self.statements), statements: std::mem::take(&mut self.statements),
terminator, terminator,
} }
} }
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock { pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
BasicBlock { BasicBlock {
label: self.label, label: self.label,
statements: self.statements, statements: self.statements,
terminator, terminator,
} }
} }
} }
pub struct Scope { pub struct Scope {
start: Label, start: Label,
end: Label, end: Label,
defers: Vec<Call>, defers: Vec<Call>,
locals: Vec<(VirtualReg, Variable)>, locals: Vec<(VirtualReg, Variable)>,
} }
impl Scope { impl Scope {
pub fn new(start: Label, end: Label) -> Self { pub fn new(start: Label, end: Label) -> Self {
Self { start, end, defers: vec![], locals: vec![] } Self {
} start,
end,
defers: vec![],
locals: vec![],
}
}
} }
struct FunctionContext { struct FunctionContext {
entrypoint: Label, entrypoint: Label,
scopes: Vec<Scope>, scopes: Vec<Scope>,
blocks: Vec<BasicBlock>, blocks: Vec<BasicBlock>,
builder: BasicBlockBuilder, builder: BasicBlockBuilder,
v_register_idx: usize, v_register_idx: usize,
} }
impl FunctionContext { impl FunctionContext {
pub fn new(entrypoint: Label) -> Self { pub fn new(entrypoint: Label) -> Self {
Self { Self {
entrypoint: entrypoint.clone(), entrypoint: entrypoint.clone(),
blocks: Vec::new(), blocks: Vec::new(),
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))], scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
builder: BasicBlockBuilder::new(entrypoint), builder: BasicBlockBuilder::new(entrypoint),
v_register_idx: 0, v_register_idx: 0,
} }
} }
pub fn new_vreg(&mut self) -> VirtualReg { pub fn new_vreg(&mut self) -> VirtualReg {
self.v_register_idx += 1; self.v_register_idx += 1;
VirtualReg(self.v_register_idx as isize - 1) VirtualReg(self.v_register_idx as isize - 1)
} }
pub fn defer(&mut self, call: Call) { pub fn defer(&mut self, call: Call) {
self.scopes.last_mut().unwrap().defers.push(call); self.scopes.last_mut().unwrap().defers.push(call);
} }
pub fn new_local(&mut self, variable: Variable) -> VirtualReg { pub fn new_local(&mut self, v_reg: VirtualReg, variable: Variable) {
let vreg = self.new_vreg(); self.scopes
self.scopes.last_mut().unwrap().locals.push((vreg, variable)); .last_mut()
vreg .unwrap()
} .locals
.push((v_reg, variable));
}
pub fn enter_scope(&mut self, scope: Scope) { pub fn enter_scope(&mut self, scope: Scope) {
self.scopes.push(scope); self.scopes.push(scope);
} }
pub fn current_scope(&mut self) -> Option<Scope> { pub fn current_scope(&mut self) -> Option<Scope> {
self.scopes.pop() self.scopes.pop()
} }
pub fn get_deferred_calls(&self) -> Vec<Call> { pub fn get_deferred_calls(&self) -> Vec<Call> {
self.scopes self.scopes
.iter() .iter()
.rev() .rev()
.flat_map(|s| .flat_map(|s| s.defers.iter().cloned().rev())
s.defers .collect()
.iter() }
.cloned()
.rev()
).collect()
}
pub fn find_local(&self, var: &Variable) -> Option<VirtualReg> { pub fn find_local(&self, var: &String) -> Option<VirtualReg> {
for scope in self.scopes.iter().rev() { for scope in self.scopes.iter().rev() {
for local in &scope.locals { for local in &scope.locals {
if local.1 == *var { if local.1.name == *var {
return Some(local.0) return Some(local.0);
} }
} }
} }
None None
} }
pub fn exit_scope(&mut self) { pub fn exit_scope(&mut self) {
self.scopes.pop().expect("(BUG) Cannot exit outermost scope"); self.scopes
} .pop()
.expect("(BUG) Cannot exit outermost scope");
}
pub fn next_block(&mut self, terminator: Terminator, label: Label) { pub fn next_block(&mut self, terminator: Terminator, label: Label) {
self.blocks.push(self.builder.next_block(terminator, label)); self.blocks.push(self.builder.next_block(terminator, label));
} }
pub fn push_stmt(&mut self, stmt: IR::Statement) { pub fn push_stmt(&mut self, stmt: IR::Statement) {
self.builder.push(stmt); self.builder.push(stmt);
} }
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> { pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
self.blocks.push(self.builder.terminate_block(terminator)); self.blocks.push(self.builder.terminate_block(terminator));
self.blocks self.blocks
} }
pub fn take(&mut self) -> Vec<IR::BasicBlock> { pub fn take(&mut self) -> Vec<IR::BasicBlock> {
std::mem::take(&mut self.blocks) std::mem::take(&mut self.blocks)
} }
} }
+11 -5
View File
@@ -1,14 +1,20 @@
use crate::model::{CompilerError, Program}; use crate::backend::ir_lowering_codegen::IrLoweringCodeGenerator;
use crate::model::CompilerError;
use crate::model::ast::Program;
use dsa_tasks::ProcessAgent; use dsa_tasks::ProcessAgent;
mod codegen; mod codegen;
mod instruction; mod instruction;
pub mod ir_lowering_codegen;
mod registers; mod registers;
mod scope; mod scope;
pub mod intermediate_rep;
pub mod ir_lowering_codegen;
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> { pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
let mut codegen = codegen::CodeGenerator::new(ast.clone()); let mut codegen_test = IrLoweringCodeGenerator::new(ast.clone());
codegen.generate(ctx) let result = codegen_test.run(ctx)?;
Ok(result.to_string())
// let mut codegen = codegen::CodeGenerator::new(ast.clone());
// codegen.generate(ctx)
} }
+4 -140
View File
@@ -1,7 +1,9 @@
use std::{collections::HashMap, fmt}; use std::collections::HashMap;
use crate::backend::instruction::{InsBlock, Instruction}; use crate::backend::instruction::InsBlock;
use crate::model::CompilerError; use crate::model::CompilerError;
use crate::model::instruction::Instruction;
use crate::model::register::{Location, Register};
/// Register allocator for DSA assembly generation /// Register allocator for DSA assembly generation
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling /// Manages general-purpose registers (rg0-rgf) and handles stack spilling
@@ -21,28 +23,6 @@ pub struct RegisterAllocator {
in_use: Vec<(Register, bool)>, in_use: Vec<(Register, bool)>,
} }
#[derive(Debug, Clone)]
pub struct Location {
register: Option<Register>,
stack: Option<i32>,
}
impl Location {
pub fn stack(offset: i32) -> Self {
Location {
register: None,
stack: Some(offset),
}
}
pub fn register(register: Register) -> Self {
Location {
register: Some(register),
stack: None,
}
}
}
impl RegisterAllocator { impl RegisterAllocator {
pub fn new() -> Self { pub fn new() -> Self {
// Initialize with available GP registers (rg0-rgf = 16 registers) // Initialize with available GP registers (rg0-rgf = 16 registers)
@@ -427,119 +407,3 @@ impl RegisterAllocator {
.collect() .collect()
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Register {
// general purpose
Rg0 = 0,
Rg1 = 1,
Rg2 = 2,
Rg3 = 3,
Rg4 = 4,
Rg5 = 5,
Rg6 = 6,
Rg7 = 7,
Rg8 = 8,
Rg9 = 9,
Rga = 10,
Rgb = 11,
Rgc = 12,
Rgd = 13,
Rge = 14,
Rgf = 15,
// special
Bpr,
Spr,
Ret,
Acc,
// read only
Pcx,
Zero,
// null
Null,
}
impl Register {
pub fn get_gp() -> [Register; 16] {
[
Register::Rg0,
Register::Rg1,
Register::Rg2,
Register::Rg3,
Register::Rg4,
Register::Rg5,
Register::Rg6,
Register::Rg7,
Register::Rg8,
Register::Rg9,
Register::Rga,
Register::Rgb,
Register::Rgc,
Register::Rgd,
Register::Rge,
Register::Rgf,
]
}
pub fn is_gp(&self) -> bool {
(*self as u8) < 16
}
pub fn from_index(idx: usize) -> Register {
match idx {
0 => Register::Rg0,
1 => Register::Rg1,
2 => Register::Rg2,
3 => Register::Rg3,
4 => Register::Rg4,
5 => Register::Rg5,
6 => Register::Rg6,
7 => Register::Rg7,
8 => Register::Rg8,
9 => Register::Rg9,
10 => Register::Rga,
11 => Register::Rgb,
12 => Register::Rgc,
13 => Register::Rgd,
14 => Register::Rge,
15 => Register::Rgf,
_ => unreachable!("this function shouldn't ever be called with idx>15"),
}
}
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rg0 => write!(f, "rg0"),
Self::Rg1 => write!(f, "rg1"),
Self::Rg2 => write!(f, "rg2"),
Self::Rg3 => write!(f, "rg3"),
Self::Rg4 => write!(f, "rg4"),
Self::Rg5 => write!(f, "rg5"),
Self::Rg6 => write!(f, "rg6"),
Self::Rg7 => write!(f, "rg7"),
Self::Rg8 => write!(f, "rg8"),
Self::Rg9 => write!(f, "rg9"),
Self::Rga => write!(f, "rga"),
Self::Rgb => write!(f, "rgb"),
Self::Rgc => write!(f, "rgc"),
Self::Rgd => write!(f, "rgd"),
Self::Rge => write!(f, "rge"),
Self::Rgf => write!(f, "rgf"),
Self::Acc => write!(f, "acc"),
Self::Ret => write!(f, "ret"),
Self::Bpr => write!(f, "bpr"),
Self::Spr => write!(f, "spr"),
Self::Zero => write!(f, "zero"),
Self::Pcx => write!(f, "pcx"),
Self::Null => write!(f, "null"),
}
}
}
+5 -6
View File
@@ -1,11 +1,10 @@
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc}; use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
use crate::backend::instruction::Instruction; use crate::error;
use crate::backend::registers::Register; use crate::model::CompilerError;
use crate::{ use crate::model::ast::{Name, TypeId};
error, use crate::model::instruction::Instruction;
model::{CompilerError, Name, TypeId}, use crate::model::register::Register;
};
/// scope object /// scope object
pub struct Scope<'a> { pub struct Scope<'a> {
+18 -219
View File
@@ -1,177 +1,8 @@
use crate::model::ast::{Name, TypeId};
use crate::model::tokens::Token;
use std::iter::Peekable; use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
// Keywords
Fn,
Let,
If,
Else,
Loop,
While,
Break,
Return,
Continue,
Include,
Static,
Const,
As,
SizeOf,
Struct,
// Identifiers and literals
Identifier(Name),
String(String),
Char(char),
SignedInt(i32, Option<TypeId>),
UnsignedInt(u32, Option<TypeId>),
// Delimiters
LeftParen, // (
RightParen, // )
LeftBrace, // {
RightBrace, // }
LeftBracket, // [
RightBracket, // ]
Semicolon, // ;
Colon, // :
Comma, // ,
Dot, // .
RightArrow, // ->
// Arithmetic operators
Plus, // +
Minus, // -
Star, // *
Slash, // /
Percent, // %
PlusPlus, // ++
MinusMinus, // --
// Bitwise operators
Ampersand, // &
Pipe, // |
Caret, // ^
Tilde, // ~
LeftShift, // <<
RightShift, // >>
// Logical operators
Bang, // !
LogicalAnd, // &&
LogicalOr, // ||
// Comparison operators
EqualEqual, // ==
BangEqual, // !=
Less, // <
LessEqual, // <=
Greater, // >
GreaterEqual, // >=
// Assignment operators
Assign, // =
PlusEqual, // +=
MinusEqual, // -=
StarEqual, // *=
SlashEqual, // /=
PercentEqual, // %=
AndEqual, // &=
OrEqual, // |=
XorEqual, // ^=
ShlEqual, // <<=
ShrEqual, // >>=
// Special
Eof,
}
use crate::model::{Name, TypeId};
use std::fmt;
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref ns) = self.namespace {
write!(f, "{}::{}", ns, self.name)
} else {
write!(f, "{}", self.name)
}
}
}
impl Token {
pub fn tt(&self) -> &str {
match self {
Token::Const => "Const",
Token::Static => "Static",
Token::Include => "Include",
Token::Struct => "Struct",
Token::Fn => "Fn",
Token::If => "If",
Token::Let => "Let",
Token::Else => "Else",
Token::Loop => "Loop",
Token::While => "While",
Token::Break => "Break",
Token::Return => "Return",
Token::Continue => "Continue",
Token::As => "As",
Token::Identifier(_) => "Identifier",
Token::String(_) => "String",
Token::UnsignedInt(_, _) => "UnsignedInt",
Token::SignedInt(_, _) => "SignedInt",
Token::Char(_) => "Char",
Token::LeftParen => "LeftParen",
Token::RightParen => "RightParen",
Token::LeftBrace => "LeftBrace",
Token::RightBrace => "RightBrace",
Token::LeftBracket => "LeftBracket",
Token::RightBracket => "RightBracket",
Token::Semicolon => "Semicolon",
Token::Colon => "Colon",
Token::Comma => "Comma",
Token::Dot => "Dot",
Token::RightArrow => "RightArrow",
Token::Plus => "Plus",
Token::Minus => "Minus",
Token::Star => "Star",
Token::Slash => "Slash",
Token::Percent => "Percent",
Token::PlusPlus => "PlusPlus",
Token::MinusMinus => "MinusMinus",
Token::Ampersand => "Ampersand",
Token::Pipe => "Pipe",
Token::Caret => "Caret",
Token::Tilde => "Tilde",
Token::LeftShift => "LeftShift",
Token::RightShift => "RightShift",
Token::Bang => "Bang",
Token::LogicalAnd => "LogicalAnd",
Token::LogicalOr => "LogicalOr",
Token::EqualEqual => "EqualEqual",
Token::BangEqual => "BangEqual",
Token::Less => "Less",
Token::LessEqual => "LessEqual",
Token::Greater => "Greater",
Token::GreaterEqual => "GreaterEqual",
Token::Assign => "Assign",
Token::PlusEqual => "PlusEqual",
Token::MinusEqual => "MinusEqual",
Token::StarEqual => "StarEqual",
Token::SlashEqual => "SlashEqual",
Token::PercentEqual => "PercentEqual",
Token::AndEqual => "AndEqual",
Token::OrEqual => "OrEqual",
Token::XorEqual => "XorEqual",
Token::ShlEqual => "ShlEqual",
Token::ShrEqual => "ShrEqual",
Token::SizeOf => "SizeOf",
Token::Eof => "Eof",
}
}
}
pub struct Lexer<'a> { pub struct Lexer<'a> {
chars: Peekable<Chars<'a>>, chars: Peekable<Chars<'a>>,
current: Option<char>, current: Option<char>,
@@ -407,12 +238,8 @@ impl<'a> Lexer<'a> {
} else { } else {
value as i32 value as i32
}; };
if signed_val < i8::MIN as i32 || signed_val > i8::MAX as i32 if signed_val < i8::MIN as i32 || signed_val > i8::MAX as i32 {
{ self.error(&format!("Value {} out of range for i8", signed_val));
self.error(&format!(
"Value {} out of range for i8",
signed_val
));
return Token::SignedInt(0, Some(TypeId::I8)); return Token::SignedInt(0, Some(TypeId::I8));
} }
Token::SignedInt(signed_val, Some(TypeId::I8)) Token::SignedInt(signed_val, Some(TypeId::I8))
@@ -423,13 +250,8 @@ impl<'a> Lexer<'a> {
} else { } else {
value as i32 value as i32
}; };
if signed_val < i16::MIN as i32 if signed_val < i16::MIN as i32 || signed_val > i16::MAX as i32 {
|| signed_val > i16::MAX as i32 self.error(&format!("Value {} out of range for i16", signed_val));
{
self.error(&format!(
"Value {} out of range for i16",
signed_val
));
return Token::SignedInt(0, Some(TypeId::I16)); return Token::SignedInt(0, Some(TypeId::I16));
} }
Token::SignedInt(signed_val, Some(TypeId::I16)) Token::SignedInt(signed_val, Some(TypeId::I16))
@@ -437,19 +259,13 @@ impl<'a> Lexer<'a> {
TypeId::I32 => { TypeId::I32 => {
let signed_val = if is_negative { let signed_val = if is_negative {
if value > i32::MAX as u64 + 1 { if value > i32::MAX as u64 + 1 {
self.error(&format!( self.error(&format!("Value -{} out of range for i32", value));
"Value -{} out of range for i32",
value
));
return Token::SignedInt(0, Some(TypeId::I32)); return Token::SignedInt(0, Some(TypeId::I32));
} }
-(value as i32) -(value as i32)
} else { } else {
if value > i32::MAX as u64 { if value > i32::MAX as u64 {
self.error(&format!( self.error(&format!("Value {} out of range for i32", value));
"Value {} out of range for i32",
value
));
return Token::SignedInt(0, Some(TypeId::I32)); return Token::SignedInt(0, Some(TypeId::I32));
} }
value as i32 value as i32
@@ -462,10 +278,7 @@ impl<'a> Lexer<'a> {
return Token::UnsignedInt(0, Some(TypeId::U8)); return Token::UnsignedInt(0, Some(TypeId::U8));
} }
if value > u8::MAX as u64 { if value > u8::MAX as u64 {
self.error(&format!( self.error(&format!("Value {} out of range for u8", value));
"Value {} out of range for u8",
value
));
return Token::UnsignedInt(0, Some(TypeId::U8)); return Token::UnsignedInt(0, Some(TypeId::U8));
} }
Token::UnsignedInt(value as u32, Some(TypeId::U8)) Token::UnsignedInt(value as u32, Some(TypeId::U8))
@@ -476,10 +289,7 @@ impl<'a> Lexer<'a> {
return Token::UnsignedInt(0, Some(TypeId::U16)); return Token::UnsignedInt(0, Some(TypeId::U16));
} }
if value > u16::MAX as u64 { if value > u16::MAX as u64 {
self.error(&format!( self.error(&format!("Value {} out of range for u16", value));
"Value {} out of range for u16",
value
));
return Token::UnsignedInt(0, Some(TypeId::U16)); return Token::UnsignedInt(0, Some(TypeId::U16));
} }
Token::UnsignedInt(value as u32, Some(TypeId::U16)) Token::UnsignedInt(value as u32, Some(TypeId::U16))
@@ -490,10 +300,7 @@ impl<'a> Lexer<'a> {
return Token::UnsignedInt(0, Some(TypeId::U32)); return Token::UnsignedInt(0, Some(TypeId::U32));
} }
if value > u32::MAX as u64 { if value > u32::MAX as u64 {
self.error(&format!( self.error(&format!("Value {} out of range for u32", value));
"Value {} out of range for u32",
value
));
return Token::UnsignedInt(0, Some(TypeId::U32)); return Token::UnsignedInt(0, Some(TypeId::U32));
} }
Token::UnsignedInt(value as u32, Some(TypeId::U32)) Token::UnsignedInt(value as u32, Some(TypeId::U32))
@@ -592,8 +399,7 @@ impl<'a> Lexer<'a> {
if c.is_ascii_digit() { if c.is_ascii_digit() {
self.advance(); self.advance();
num_str.push(c); num_str.push(c);
} else if c == '_' && self.peek_second().is_some_and(|ch| ch.is_ascii_digit()) } else if c == '_' && self.peek_second().is_some_and(|ch| ch.is_ascii_digit()) {
{
// Allow underscores as separators only between digits // Allow underscores as separators only between digits
self.advance(); self.advance();
} else { } else {
@@ -662,8 +468,7 @@ impl<'a> Lexer<'a> {
return Err("Invalid binary number: no digits after 0b".to_string()); return Err("Invalid binary number: no digits after 0b".to_string());
} }
u64::from_str_radix(&num_str, 2) u64::from_str_radix(&num_str, 2).map_err(|_| format!("Invalid binary number: {}", num_str))
.map_err(|_| format!("Invalid binary number: {}", num_str))
} }
// ======================================================================== // ========================================================================
@@ -713,10 +518,7 @@ impl<'a> Lexer<'a> {
'\'' => '\'', '\'' => '\'',
'0' => '\0', '0' => '\0',
_ => { _ => {
return Err(format!( return Err(format!("Invalid escape sequence: \\{}", escaped));
"Invalid escape sequence: \\{}",
escaped
));
} }
}; };
s.push(escaped_char); s.push(escaped_char);
@@ -767,9 +569,7 @@ impl<'a> Lexer<'a> {
Some('0') => '\0', Some('0') => '\0',
Some(c) => return Err(format!("Invalid escape sequence: \\{}", c)), Some(c) => return Err(format!("Invalid escape sequence: \\{}", c)),
None => { None => {
return Err( return Err("Unexpected end after escape in char literal".to_string());
"Unexpected end after escape in char literal".to_string()
);
} }
} }
} }
@@ -782,9 +582,7 @@ impl<'a> Lexer<'a> {
self.advance(); // Move to closing quote self.advance(); // Move to closing quote
if self.current != Some('\'') { if self.current != Some('\'') {
return Err( return Err("Character literal must contain exactly one character".to_string());
"Character literal must contain exactly one character".to_string()
);
} }
Ok(ch) Ok(ch)
@@ -998,7 +796,8 @@ mod tests {
#[test] #[test]
fn test_operators() { fn test_operators() {
let input = "+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>="; let input =
"+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>=";
let mut lexer = Lexer::new(input); let mut lexer = Lexer::new(input);
let expected = vec![ let expected = vec![
+2 -1
View File
@@ -1,5 +1,6 @@
use crate::frontend::parser::{ParseResult, Parser}; use crate::frontend::parser::{ParseResult, Parser};
use crate::model::{CompilerError, Program}; use crate::model::CompilerError;
use crate::model::ast::Program;
use dsa_tasks::ProcessAgent; use dsa_tasks::ProcessAgent;
// mod c; // mod c;
+5 -4
View File
@@ -1,8 +1,9 @@
use super::lexer::Token; use crate::model::CompilerError;
use crate::model::{ use crate::model::ast::{
AssignmentOperator, BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration, AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency,
Dependency, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
}; };
use crate::model::tokens::Token;
use crate::{expect_tt, expect_value}; use crate::{expect_tt, expect_value};
use std::ops::{ControlFlow, FromResidual, Residual, Try}; use std::ops::{ControlFlow, FromResidual, Residual, Try};
+1 -1
View File
@@ -1,8 +1,8 @@
#![feature(try_trait_v2)] #![feature(try_trait_v2)]
#![feature(try_trait_v2_residual)] #![feature(try_trait_v2_residual)]
use crate::model::CompilerError;
use dsa_tasks::{ProcessAgent, TraceableProcess}; use dsa_tasks::{ProcessAgent, TraceableProcess};
use model::CompilerError;
use std::path::PathBuf; use std::path::PathBuf;
mod backend; mod backend;
@@ -1,38 +1,12 @@
use core::fmt; use crate::model::CompilerError;
use std::fmt;
#[allow(unused)]
#[derive(Debug, Clone)]
pub enum CompilerError {
UnexpectedToken(String),
UnexpectedEndOfInput,
UnexpectedCharacter(char),
Undefined(Name),
InvalidSyntax(String),
Generic(String),
UnknownType,
TypeMismatch(TypeId, TypeId),
Unimplemented(String),
}
impl std::error::Error for CompilerError {}
impl fmt::Display for CompilerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl CompilerError {
pub fn generic(msg: impl Into<String>) -> Self {
Self::Generic(msg.into())
}
}
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Name { pub struct Name {
pub name: String, pub name: String,
pub namespace: Option<String>, pub namespace: Option<String>,
} }
impl Name { impl Name {
pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self { pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self {
Self { Self {
@@ -499,3 +473,13 @@ impl fmt::Display for UnaryOperator {
} }
} }
} }
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref ns) = self.namespace {
write!(f, "{}::{}", ns, self.name)
} else {
write!(f, "{}", self.name)
}
}
}
+271
View File
@@ -0,0 +1,271 @@
use std::fmt;
use std::fmt::Formatter;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone, Debug)]
pub enum Statement {
Const(VirtualReg, Literal),
BinaryOp {
left: VirtualReg,
op: PrimitiveOperation,
right: VirtualReg,
result: VirtualReg,
},
Copy {
src: VirtualReg,
dst: VirtualReg,
},
Store {
value: VirtualReg,
address: VirtualReg,
},
Load {
address: VirtualReg,
result: VirtualReg,
},
GetStructField {
struct_addr: VirtualReg,
result: VirtualReg,
offset: usize,
},
SetStructField {
struct_addr: VirtualReg,
value: VirtualReg,
offset: usize,
},
Call {
return_value: VirtualReg,
func: FunctionName,
args: Vec<VirtualReg>,
},
}
#[derive(Clone, Debug)]
pub struct IRProgram {
pub functions: Vec<Function>,
}
#[derive(Clone, Debug)]
pub struct Function {
pub name: FunctionName,
pub params: Vec<VirtualReg>,
pub blocks: Vec<BasicBlock>,
}
#[derive(Clone, Debug)]
pub struct BasicBlock {
pub label: Label,
pub statements: Vec<Statement>,
pub terminator: Terminator,
}
#[derive(Clone, Debug)]
pub enum Terminator {
Jump(Label),
ConditionalJump {
if_flag: VirtualReg,
then_goto: Label,
else_goto: Label,
},
Return(VirtualReg),
ReturnVoid,
}
pub type FunctionName = String;
#[derive(Clone, Debug)]
pub struct Label {
id: usize,
name: Rc<String>,
}
impl Label {
pub fn new_with_id(name: impl Into<String>, id: usize) -> Self {
Self {
id,
name: Rc::new(name.into()),
}
}
pub fn new(name: impl Into<String>) -> Self {
Self {
id: Self::get_new_id(),
name: Rc::new(name.into()),
}
}
pub fn get_new_id() -> usize {
LABEL_ID.fetch_add(1, Ordering::Relaxed)
}
}
impl fmt::Display for Label {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "__{}_{}", self.name, self.id)
}
}
static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VirtualReg(pub isize);
impl VirtualReg {
pub fn void() -> Self {
Self(-1)
}
}
#[derive(Clone, Debug)]
pub enum Literal {
UInt(usize),
Int(isize),
Char(char),
}
#[derive(Clone, Debug)]
pub enum PrimitiveOperation {
Add,
Sub,
And,
Or,
Xor,
Nor,
Nand,
}
impl fmt::Display for VirtualReg {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.0 == -1 {
write!(f, "_")
} else {
write!(f, "v_{}", self.0)
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Literal::UInt(v) => write!(f, "{}", v),
Literal::Int(v) => write!(f, "{}", v),
Literal::Char(c) => write!(f, "'{}'", c),
}
}
}
impl fmt::Display for PrimitiveOperation {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = match self {
PrimitiveOperation::Add => "+",
PrimitiveOperation::Sub => "-",
PrimitiveOperation::And => "&",
PrimitiveOperation::Or => "|",
PrimitiveOperation::Xor => "^",
PrimitiveOperation::Nor => "nor",
PrimitiveOperation::Nand => "nand",
};
write!(f, "{}", s)
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
Statement::BinaryOp {
left,
op,
right,
result,
} => {
write!(f, "{} = {} {} {}", result, left, op, right)
}
Statement::Copy { src, dst } => write!(f, "{} = copy {}", dst, src),
Statement::Store { value, address } => write!(f, "store {}, {}", address, value),
Statement::Load { address, result } => write!(f, "{} = load {}", result, address),
Statement::GetStructField {
struct_addr,
result,
offset,
} => {
write!(f, "{} = {}.{}", result, struct_addr, offset)
}
Statement::SetStructField {
struct_addr,
value,
offset,
} => {
write!(f, "{}.{} = {}", struct_addr, offset, value)
}
Statement::Call {
return_value,
func,
args,
} => {
let args_str = args
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", ");
write!(f, "{} = {}({})", return_value, func, args_str)
}
}
}
}
impl fmt::Display for Terminator {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Terminator::Jump(label) => write!(f, "jump {}", label),
Terminator::ConditionalJump {
if_flag,
then_goto,
else_goto,
} => {
write!(f, "if {} then {} else {}", if_flag, then_goto, else_goto)
}
Terminator::Return(reg) => write!(f, "return {}", reg),
Terminator::ReturnVoid => write!(f, "return"),
}
}
}
impl fmt::Display for BasicBlock {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "{}:", self.label)?;
for stmt in &self.statements {
writeln!(f, " {}", stmt)?;
}
writeln!(f, " {}", self.terminator)
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let params_str = self
.params
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(", ");
writeln!(f, "fn {}({}) {{", self.name, params_str)?;
for block in &self.blocks {
write!(f, "{}", block)?;
}
writeln!(f, "}}")
}
}
impl fmt::Display for IRProgram {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (i, func) in self.functions.iter().enumerate() {
if i > 0 {
writeln!(f)?;
}
write!(f, "{}", func)?;
}
Ok(())
}
}
@@ -0,0 +1,803 @@
use crate::model::register::Register;
use std::fmt;
pub enum Comparison {
Greater,
GreaterEqual,
Equal,
NotEqual,
Less,
LessEqual,
}
#[derive(Debug, Clone)]
pub enum Instruction {
// Labels and comments
Label(Label),
Comment {
text: String,
top_level: bool,
},
Newline,
// Data Directives
Db {
label: String,
data: Vec<u8>,
},
Dh {
label: String,
data: Vec<u16>,
},
Dw {
label: String,
data: Vec<u32>,
},
DString {
// alias for db.
label: String,
data: String,
},
Resx {
label: String,
size: u32,
},
// Include
Include {
name: String,
path: String,
},
// Data movement
Mov {
src: Register,
dest: Register,
},
Movs {
src: Register,
dest: Register,
},
// Memory operations
Ldb {
src: MemOperand,
dest: Register,
},
Ldh {
src: MemOperand,
dest: Register,
},
Ldw {
src: MemOperand,
dest: Register,
},
Stb {
src: Register,
dest: MemOperand,
},
Sth {
src: Register,
dest: MemOperand,
},
Stw {
src: Register,
dest: MemOperand,
},
// Immediate loads
Lli {
imm: Imm,
dest: Register,
},
Lui {
imm: Imm,
dest: Register,
},
Lwi {
imm: Imm,
dest: Register,
},
LwiLabel {
label: String,
dest: Register,
},
// Arithmetic
Add {
src1: Register,
src2: Register,
dest: Register,
},
Sub {
src1: Register,
src2: Register,
dest: Register,
},
AddI {
src: Register,
imm: Imm,
dest: Option<Register>,
},
SubI {
src: Register,
imm: Imm,
dest: Option<Register>,
},
// Bitwise
And {
src1: Register,
src2: Register,
dest: Register,
},
Or {
src1: Register,
src2: Register,
dest: Register,
},
Xor {
src1: Register,
src2: Register,
dest: Register,
},
Not {
src: Register,
dest: Register,
},
Nand {
src1: Register,
src2: Register,
dest: Register,
},
Nor {
src1: Register,
src2: Register,
dest: Register,
},
Xnor {
src1: Register,
src2: Register,
dest: Register,
},
// Shifts
Shl {
src1: Register,
r_shamt: Register,
i_shamt: u16,
dest: Register,
},
Shr {
src1: Register,
r_shamt: Register,
i_shamt: u16,
dest: Register,
},
Ieq {
src1: Register,
src2: Register,
dest: Register,
},
Ine {
src1: Register,
src2: Register,
dest: Register,
},
Ilt {
src1: Register,
src2: Register,
dest: Register,
},
Ile {
src1: Register,
src2: Register,
dest: Register,
},
Igt {
src1: Register,
src2: Register,
dest: Register,
},
Ige {
src1: Register,
src2: Register,
dest: Register,
},
// Jumps
Jmp {
target: Label,
},
Jnz {
src: Register,
target: Label,
},
Jez {
src: Register,
target: Label,
},
Jic {
target: Label,
},
Jnc {
target: Label,
},
// Stack
Push {
reg: Register,
},
Pop {
reg: Register,
},
// Function calls
Call {
target: String,
}, // namespace::function
Return,
// System
Hlt,
Nop,
Int {
code: u8,
},
}
pub enum DataDirective {
U8(Vec<u8>),
U16(Vec<u16>),
U32(Vec<u32>),
String(String),
Char(char),
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Label(l) => write!(f, "{}:", l),
Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
// automatically.
Self::Comment { text, top_level } => write!(
f,
"{}",
text.lines()
.map(|line| format!(
"{}// {}",
if *top_level { "" } else { " " },
line.trim(),
))
.collect::<Vec<String>>()
.join("\n")
),
Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
Self::Db { label, data } => write!(
f,
"db {}: {}",
label,
data.iter()
.map(|&b| format!("{:#04X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::Dh { label, data } => write!(
f,
"dh {}: {}",
label,
data.iter()
.map(|&b| format!("{:#06X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::Dw { label, data } => write!(
f,
"dw {}: {}",
label,
data.iter()
.map(|&b| format!("{:#08X}", b))
.collect::<Vec<String>>()
.join(", ")
),
Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
Self::Ldb { src: addr, dest } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " ldb {}, {}, {}", reg, dest, offset)
}
Self::Ldh { src: addr, dest } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " ldh {}, {}, {}", reg, dest, offset)
}
Self::Ldw { src, dest } => {
let (reg, offset) = reg_and_offset(src);
write!(f, " ldw {}, {}, {}", reg, dest, offset)
}
// Self::Ldbs { addr, dest } => {
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldhs { addr, dest } => {
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
// }
// Self::Ldws { addr, dest } => {
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
// }
Self::Stb { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " stb {}, {}, {}", src, reg, offset)
}
Self::Sth { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " sth {}, {}, {}", src, reg, offset)
}
Self::Stw { src, dest: addr } => {
let (reg, offset) = reg_and_offset(addr);
write!(f, " stw {}, {}, {}", src, reg, offset)
}
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
// arithmetic
Self::Add { src1, src2, dest } => {
write!(f, " add {}, {}, {}", src1, src2, dest)
}
Self::Sub { src1, src2, dest } => {
write!(f, " sub {}, {}, {}", src1, src2, dest)
}
Self::And { src1, src2, dest } => {
write!(f, " and {}, {}, {}", src1, src2, dest)
}
Self::Or { src1, src2, dest } => {
write!(f, " or {}, {}, {}", src1, src2, dest)
}
Self::Nand { src1, src2, dest } => {
write!(f, " nand {}, {}, {}", src1, src2, dest)
}
Self::Xor { src1, src2, dest } => {
write!(f, " xor {}, {}, {}", src1, src2, dest)
}
Self::Nor { src1, src2, dest } => {
write!(f, " nor {}, {}, {}", src1, src2, dest)
}
Self::Not { src, dest } => {
write!(f, " not {} {}", src, dest)
}
Self::Xnor { src1, src2, dest } => {
write!(f, " xnor {}, {}, {}", src1, src2, dest)
}
Self::AddI { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " addi {}, {}, {}", src, imm, d)
} else {
write!(f, " addi {}, {}", src, imm)
}
}
Self::SubI { src, imm, dest } => {
if let Some(d) = dest {
write!(f, " subi {}, {}, {}", src, imm, d)
} else {
write!(f, " subi {}, {}", src, imm)
}
}
// shift instructions
Self::Shl {
src1,
r_shamt,
i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
Self::Shr {
src1,
r_shamt,
i_shamt,
dest,
} => {
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
}
Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
// jump instructions
Self::Jmp { target } => write!(f, " jmp {}", target),
Self::Jic { target } => write!(f, " jic {}", target),
Self::Jnc { target } => write!(f, " jnc {}", target),
Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
// stack instructions
Self::Push { reg } => write!(f, " push {}", reg),
Self::Pop { reg } => write!(f, " pop {}", reg),
// call & return instructions
Self::Call { target } => write!(f, " call {}", target),
Self::Return => write!(f, " ret"),
// misc instructions
Self::Int { code } => write!(f, " int {}", code),
Self::Hlt => write!(f, " hlt"),
Self::Nop => write!(f, " nop"),
}
}
}
impl Instruction {
// data directives
pub fn db_string(label: impl Into<String>, data: impl Into<String>) -> Self {
Self::DString {
label: label.into(),
data: data.into(),
}
}
pub fn db_word(label: impl Into<String>, data: u32) -> Self {
Self::Dw {
label: label.into(),
data: vec![data],
}
}
pub fn db_bytes(label: impl Into<String>, data: &[u8]) -> Self {
Self::Db {
label: label.into(),
data: data.to_vec(),
}
}
// Movement
pub fn mov<R1, R2>(src: R1, dest: R2) -> Self
where
R1: Into<Register>,
R2: Into<Register>,
{
Self::Mov {
src: src.into(),
dest: dest.into(),
}
}
// Memory loads
pub fn ldw_reg<R>(base: R, dest: Register) -> Self
where
R: Into<Register>,
{
Self::Ldw {
src: MemOperand::RegIndirect(base.into()),
dest,
}
}
pub fn ldw_reg_offset<R>(base: R, dest: Register, offset: i32) -> Self
where
R: Into<Register>,
{
Self::Ldw {
src: MemOperand::RegOffset(base.into(), offset),
dest,
}
}
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
Self::Ldw {
src: MemOperand::Label(label.into()),
dest,
}
}
// Memory stores
pub fn stw_reg<R>(src: Register, base: R) -> Self
where
R: Into<Register>,
{
Self::Stw {
src,
dest: MemOperand::RegIndirect(base.into()),
}
}
pub fn stw_reg_offset<R>(src: Register, base: R, offset: i32) -> Self
where
R: Into<Register>,
{
Self::Stw {
src,
dest: MemOperand::RegOffset(base.into(), offset),
}
}
pub fn stw_label(src: Register, label: impl Into<Label>) -> Self {
Self::Stw {
src,
dest: MemOperand::Label(label.into()),
}
}
// Arithmetic
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
Self::Add { src1, src2, dest }
}
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
Self::Sub { src1, src2, dest }
}
pub fn and(src1: Register, src2: Register, dest: Register) -> Self {
Self::And { src1, src2, dest }
}
pub fn or(src1: Register, src2: Register, dest: Register) -> Self {
Self::Or { src1, src2, dest }
}
pub fn xor(src1: Register, src2: Register, dest: Register) -> Self {
Self::Xor { src1, src2, dest }
}
pub fn not(src: Register, dest: Register) -> Self {
Self::Not { src, dest }
}
pub fn shl(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
Self::Shl {
src1,
r_shamt,
i_shamt,
dest,
}
}
pub fn shr(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
Self::Shr {
src1,
r_shamt,
i_shamt,
dest,
}
}
pub fn iadd(src: Register, value: i64) -> Self {
let imm = Imm(value.unsigned_abs() as u32);
if value < 0 {
Self::SubI {
src,
imm,
dest: None,
}
} else {
Self::AddI {
src,
imm,
dest: None,
}
}
}
pub fn iadd_dest(src: Register, value: i32, dest: Register) -> Self {
let imm = Imm(value.unsigned_abs());
if value < 0 {
Self::SubI {
src,
imm,
dest: Some(dest),
}
} else {
Self::AddI {
src,
imm,
dest: Some(dest),
}
}
}
pub fn inc(reg: Register) -> Self {
Self::AddI {
src: reg,
imm: Imm(1),
dest: None,
}
}
pub fn dec(reg: Register) -> Self {
Self::SubI {
src: reg,
imm: Imm(1),
dest: None,
}
}
// Immediate loads
pub fn lwi(value: u32, dest: Register) -> Self {
if value > 0xFFFF {
Self::Lwi {
imm: Imm(value),
dest,
}
} else {
Self::Lli {
imm: Imm(value),
dest,
}
}
}
pub fn lwi_label(label: impl Into<String>, dest: Register) -> Self {
Self::LwiLabel {
label: label.into(),
dest,
}
}
// Control flow
pub fn label(name: impl Into<String>) -> Self {
Self::Label(Label(name.into()))
}
pub fn cmp(operand: Comparison, src1: Register, src2: Register, dest: Register) -> Self {
match operand {
Comparison::Equal => Self::Ieq { src1, src2, dest },
Comparison::NotEqual => Self::Ine { src1, src2, dest },
Comparison::Greater => Self::Igt { src1, src2, dest },
Comparison::GreaterEqual => Self::Ige { src1, src2, dest },
Comparison::Less => Self::Ilt { src1, src2, dest },
Comparison::LessEqual => Self::Ile { src1, src2, dest },
}
}
pub fn jmp(target: impl Into<Label>) -> Self {
Self::Jmp {
target: target.into(),
}
}
pub fn jnz(src: Register, target: impl Into<Label>) -> Self {
Self::Jnz {
src,
target: target.into(),
}
}
pub fn jez(src: Register, target: impl Into<Label>) -> Self {
Self::Jez {
src,
target: target.into(),
}
}
pub fn jic(target: impl Into<Label>) -> Self {
Self::Jic {
target: target.into(),
}
}
pub fn jnc(target: impl Into<Label>) -> Self {
Self::Jnc {
target: target.into(),
}
}
// Stack
pub fn push(reg: Register) -> Self {
Self::Push { reg }
}
pub fn pop(reg: Register) -> Self {
Self::Pop { reg }
}
// Functions
pub fn call(target: impl Into<String>) -> Self {
Self::Call {
target: target.into(),
}
}
pub fn int(code: u8) -> Self {
Self::Int { code }
}
pub fn ret() -> Self {
Self::Return
}
// Utilities
pub fn comment(text: impl Into<String>) -> Self {
Self::Comment {
text: text.into(),
top_level: false,
}
}
pub fn global_comment(text: impl Into<String>) -> Self {
Self::Comment {
text: text.into(),
top_level: true,
}
}
pub fn include(name: impl Into<String>, path: impl Into<String>) -> Self {
Self::Include {
name: name.into(),
path: path.into(),
}
}
}
// Convenience trait for Label conversion
impl From<String> for Label {
fn from(s: String) -> Self {
Label(s)
}
}
impl From<&str> for Label {
fn from(s: &str) -> Self {
Label(s.to_string())
}
}
fn reg_and_offset(op: &MemOperand) -> (String, i32) {
match op {
MemOperand::RegIndirect(reg) => (reg.to_string(), 0),
MemOperand::RegOffset(reg, offset) => (reg.to_string(), *offset),
MemOperand::Label(label) => (label.to_string(), 0),
MemOperand::LabelOffset(label, offset) => (label.to_string(), *offset),
}
}
/// Memory operand for loads/stores
#[derive(Debug, Clone)]
pub enum MemOperand {
/// Register indirect: [reg]
RegIndirect(Register),
/// Register with offset: [reg + offset]
RegOffset(Register, i32),
/// Label: [label]
Label(Label),
/// Label with offset: [label + offset]
LabelOffset(Label, i32),
}
/// Immediate value (16-bit or 32-bit)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Imm(pub u32);
impl fmt::Display for Imm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Label reference
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Label(pub String);
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
+36
View File
@@ -0,0 +1,36 @@
use ast::{Name, TypeId};
use std::fmt;
pub mod ast;
pub mod dsa_ir;
pub mod instruction;
pub mod register;
pub mod tokens;
#[allow(unused)]
#[derive(Debug, Clone)]
pub enum CompilerError {
UnexpectedToken(String),
UnexpectedEndOfInput,
UnexpectedCharacter(char),
Undefined(Name),
InvalidSyntax(String),
Generic(String),
UnknownType,
TypeMismatch(TypeId, TypeId),
Unimplemented(String),
}
impl std::error::Error for CompilerError {}
impl fmt::Display for CompilerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl CompilerError {
pub fn generic(msg: impl Into<String>) -> Self {
Self::Generic(msg.into())
}
}
+139
View File
@@ -0,0 +1,139 @@
use std::fmt;
#[derive(Debug, Clone)]
pub struct Location {
pub register: Option<Register>,
pub stack: Option<i32>,
}
impl Location {
pub fn stack(offset: i32) -> Self {
Location {
register: None,
stack: Some(offset),
}
}
pub fn register(register: Register) -> Self {
Location {
register: Some(register),
stack: None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Register {
// general purpose
Rg0 = 0,
Rg1 = 1,
Rg2 = 2,
Rg3 = 3,
Rg4 = 4,
Rg5 = 5,
Rg6 = 6,
Rg7 = 7,
Rg8 = 8,
Rg9 = 9,
Rga = 10,
Rgb = 11,
Rgc = 12,
Rgd = 13,
Rge = 14,
Rgf = 15,
// special
Bpr,
Spr,
Ret,
Acc,
// read only
Pcx,
Zero,
// null
Null,
}
impl Register {
pub fn get_gp() -> [Register; 16] {
[
Register::Rg0,
Register::Rg1,
Register::Rg2,
Register::Rg3,
Register::Rg4,
Register::Rg5,
Register::Rg6,
Register::Rg7,
Register::Rg8,
Register::Rg9,
Register::Rga,
Register::Rgb,
Register::Rgc,
Register::Rgd,
Register::Rge,
Register::Rgf,
]
}
pub fn is_gp(&self) -> bool {
(*self as u8) < 16
}
pub fn from_index(idx: usize) -> Register {
match idx {
0 => Register::Rg0,
1 => Register::Rg1,
2 => Register::Rg2,
3 => Register::Rg3,
4 => Register::Rg4,
5 => Register::Rg5,
6 => Register::Rg6,
7 => Register::Rg7,
8 => Register::Rg8,
9 => Register::Rg9,
10 => Register::Rga,
11 => Register::Rgb,
12 => Register::Rgc,
13 => Register::Rgd,
14 => Register::Rge,
15 => Register::Rgf,
_ => unreachable!("this function shouldn't ever be called with idx>15"),
}
}
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rg0 => write!(f, "rg0"),
Self::Rg1 => write!(f, "rg1"),
Self::Rg2 => write!(f, "rg2"),
Self::Rg3 => write!(f, "rg3"),
Self::Rg4 => write!(f, "rg4"),
Self::Rg5 => write!(f, "rg5"),
Self::Rg6 => write!(f, "rg6"),
Self::Rg7 => write!(f, "rg7"),
Self::Rg8 => write!(f, "rg8"),
Self::Rg9 => write!(f, "rg9"),
Self::Rga => write!(f, "rga"),
Self::Rgb => write!(f, "rgb"),
Self::Rgc => write!(f, "rgc"),
Self::Rgd => write!(f, "rgd"),
Self::Rge => write!(f, "rge"),
Self::Rgf => write!(f, "rgf"),
Self::Acc => write!(f, "acc"),
Self::Ret => write!(f, "ret"),
Self::Bpr => write!(f, "bpr"),
Self::Spr => write!(f, "spr"),
Self::Zero => write!(f, "zero"),
Self::Pcx => write!(f, "pcx"),
Self::Null => write!(f, "null"),
}
}
}
+159
View File
@@ -0,0 +1,159 @@
use crate::model::ast::{Name, TypeId};
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
// Keywords
Fn,
Let,
If,
Else,
Loop,
While,
Break,
Return,
Continue,
Include,
Static,
Const,
As,
SizeOf,
Struct,
// Identifiers and literals
Identifier(Name),
String(String),
Char(char),
SignedInt(i32, Option<TypeId>),
UnsignedInt(u32, Option<TypeId>),
// Delimiters
LeftParen, // (
RightParen, // )
LeftBrace, // {
RightBrace, // }
LeftBracket, // [
RightBracket, // ]
Semicolon, // ;
Colon, // :
Comma, // ,
Dot, // .
RightArrow, // ->
// Arithmetic operators
Plus, // +
Minus, // -
Star, // *
Slash, // /
Percent, // %
PlusPlus, // ++
MinusMinus, // --
// Bitwise operators
Ampersand, // &
Pipe, // |
Caret, // ^
Tilde, // ~
LeftShift, // <<
RightShift, // >>
// Logical operators
Bang, // !
LogicalAnd, // &&
LogicalOr, // ||
// Comparison operators
EqualEqual, // ==
BangEqual, // !=
Less, // <
LessEqual, // <=
Greater, // >
GreaterEqual, // >=
// Assignment operators
Assign, // =
PlusEqual, // +=
MinusEqual, // -=
StarEqual, // *=
SlashEqual, // /=
PercentEqual, // %=
AndEqual, // &=
OrEqual, // |=
XorEqual, // ^=
ShlEqual, // <<=
ShrEqual, // >>=
// Special
Eof,
}
impl Token {
pub fn tt(&self) -> &str {
match self {
Token::Const => "Const",
Token::Static => "Static",
Token::Include => "Include",
Token::Struct => "Struct",
Token::Fn => "Fn",
Token::If => "If",
Token::Let => "Let",
Token::Else => "Else",
Token::Loop => "Loop",
Token::While => "While",
Token::Break => "Break",
Token::Return => "Return",
Token::Continue => "Continue",
Token::As => "As",
Token::Identifier(_) => "Identifier",
Token::String(_) => "String",
Token::UnsignedInt(_, _) => "UnsignedInt",
Token::SignedInt(_, _) => "SignedInt",
Token::Char(_) => "Char",
Token::LeftParen => "LeftParen",
Token::RightParen => "RightParen",
Token::LeftBrace => "LeftBrace",
Token::RightBrace => "RightBrace",
Token::LeftBracket => "LeftBracket",
Token::RightBracket => "RightBracket",
Token::Semicolon => "Semicolon",
Token::Colon => "Colon",
Token::Comma => "Comma",
Token::Dot => "Dot",
Token::RightArrow => "RightArrow",
Token::Plus => "Plus",
Token::Minus => "Minus",
Token::Star => "Star",
Token::Slash => "Slash",
Token::Percent => "Percent",
Token::PlusPlus => "PlusPlus",
Token::MinusMinus => "MinusMinus",
Token::Ampersand => "Ampersand",
Token::Pipe => "Pipe",
Token::Caret => "Caret",
Token::Tilde => "Tilde",
Token::LeftShift => "LeftShift",
Token::RightShift => "RightShift",
Token::Bang => "Bang",
Token::LogicalAnd => "LogicalAnd",
Token::LogicalOr => "LogicalOr",
Token::EqualEqual => "EqualEqual",
Token::BangEqual => "BangEqual",
Token::Less => "Less",
Token::LessEqual => "LessEqual",
Token::Greater => "Greater",
Token::GreaterEqual => "GreaterEqual",
Token::Assign => "Assign",
Token::PlusEqual => "PlusEqual",
Token::MinusEqual => "MinusEqual",
Token::StarEqual => "StarEqual",
Token::SlashEqual => "SlashEqual",
Token::PercentEqual => "PercentEqual",
Token::AndEqual => "AndEqual",
Token::OrEqual => "OrEqual",
Token::XorEqual => "XorEqual",
Token::ShlEqual => "ShlEqual",
Token::ShrEqual => "ShrEqual",
Token::SizeOf => "SizeOf",
Token::Eof => "Eof",
}
}
}