parser3
This commit is contained in:
@@ -1,117 +0,0 @@
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
pub type FileID = usize;
|
||||
pub type FileMap = HashMap<FileID, SrcFile>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SrcFile {
|
||||
pub path: PathBuf,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FilePos {
|
||||
pub file: FileID,
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct FileSpan {
|
||||
pub file: FileID,
|
||||
pub start: FilePos,
|
||||
pub end: FilePos,
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn start(file: FileID) -> Self {
|
||||
Self {
|
||||
line: 0,
|
||||
col: 0,
|
||||
file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn to(self, end: FilePos) -> FileSpan {
|
||||
FileSpan {
|
||||
start: self,
|
||||
end,
|
||||
file: self.file,
|
||||
}
|
||||
}
|
||||
pub fn char_span(self) -> FileSpan {
|
||||
FileSpan::at(self)
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE: usize = 1;
|
||||
const AFTER: usize = 0;
|
||||
|
||||
impl FileSpan {
|
||||
const BUILTIN_FILE: usize = usize::MAX;
|
||||
pub fn at(pos: FilePos) -> Self {
|
||||
Self {
|
||||
start: pos,
|
||||
end: pos,
|
||||
file: pos.file,
|
||||
}
|
||||
}
|
||||
pub fn builtin() -> Self {
|
||||
let pos = FilePos {
|
||||
file: Self::BUILTIN_FILE,
|
||||
line: 0,
|
||||
col: 0,
|
||||
};
|
||||
Self::at(pos)
|
||||
}
|
||||
pub fn is_builtin(&self) -> bool {
|
||||
self.file == Self::BUILTIN_FILE
|
||||
}
|
||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
||||
if self.is_builtin() {
|
||||
return Ok(());
|
||||
}
|
||||
let start = self.start.line.saturating_sub(BEFORE);
|
||||
let num_before = self.start.line - start;
|
||||
let mut lines = file.lines().skip(start);
|
||||
let width = format!("{}", self.end.line + AFTER).len();
|
||||
let same_line = self.start.line == self.end.line;
|
||||
for i in 0..num_before {
|
||||
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
|
||||
let len = if same_line {
|
||||
self.end.col - self.start.col + 1
|
||||
} else {
|
||||
line.len() - self.start.col
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
" ".repeat(self.start.col) + &"^".repeat(len)
|
||||
)?;
|
||||
if !same_line {
|
||||
for _ in 0..self.end.line - self.start.line - 1 {
|
||||
lines.next();
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
"^".repeat(self.end.col + 1)
|
||||
)?;
|
||||
}
|
||||
// for i in 0..AFTER {
|
||||
// if let Some(next) = lines.next() {
|
||||
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
|
||||
// }
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
mod output;
|
||||
mod file;
|
||||
|
||||
pub use output::*;
|
||||
pub use file::*;
|
||||
@@ -1,73 +0,0 @@
|
||||
use super::{FileMap, FilePos, FileSpan};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompilerMsg {
|
||||
pub msg: String,
|
||||
pub spans: Vec<FileSpan>,
|
||||
}
|
||||
|
||||
pub struct CompilerOutput {
|
||||
pub file_map: FileMap,
|
||||
pub errs: Vec<CompilerMsg>,
|
||||
pub hints: Vec<CompilerMsg>,
|
||||
}
|
||||
|
||||
impl CompilerMsg {
|
||||
pub fn from_msg(msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn new(msg: String, span: FileSpan) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: vec![span],
|
||||
}
|
||||
}
|
||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: vec![FileSpan::at(pos)],
|
||||
}
|
||||
}
|
||||
pub fn write_to(
|
||||
&self,
|
||||
ty: &str,
|
||||
writer: &mut impl std::io::Write,
|
||||
map: &FileMap,
|
||||
) -> std::io::Result<()> {
|
||||
let after = if self.spans.is_empty() { "" } else { ":" };
|
||||
writeln!(writer, "{}: {}{}", ty, self.msg, after)?;
|
||||
for span in &self.spans {
|
||||
let file = map.get(&span.file).expect("unknown file id");
|
||||
writeln!(writer, "{:?}", &file.path)?;
|
||||
span.write_for(writer, &file.text)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerOutput {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
errs: Vec::new(),
|
||||
hints: Vec::new(),
|
||||
file_map: FileMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn err(&mut self, msg: CompilerMsg) {
|
||||
self.errs.push(msg);
|
||||
}
|
||||
pub fn hint(&mut self, msg: CompilerMsg) {
|
||||
self.hints.push(msg);
|
||||
}
|
||||
pub fn write_to(&self, out: &mut impl std::io::Write) {
|
||||
for err in &self.errs {
|
||||
err.write_to("error", out, &self.file_map).unwrap();
|
||||
}
|
||||
for hint in &self.hints {
|
||||
hint.write_to("hint", out, &self.file_map).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod riscv;
|
||||
@@ -1,357 +0,0 @@
|
||||
use crate::{
|
||||
compiler::program::{Addr, Instr, SymTable},
|
||||
ir::Symbol,
|
||||
util::{Bits32, LabeledFmt},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LinkerInstruction<R = Reg, S = Symbol> {
|
||||
Op {
|
||||
op: Funct3,
|
||||
funct: Funct7,
|
||||
dest: R,
|
||||
src1: R,
|
||||
src2: R,
|
||||
},
|
||||
OpImm {
|
||||
op: Funct3,
|
||||
dest: R,
|
||||
src: R,
|
||||
imm: i32,
|
||||
},
|
||||
OpImmF7 {
|
||||
op: Funct3,
|
||||
funct: Funct7,
|
||||
dest: R,
|
||||
src: R,
|
||||
imm: i32,
|
||||
},
|
||||
Store {
|
||||
width: Funct3,
|
||||
src: R,
|
||||
offset: i32,
|
||||
base: R,
|
||||
},
|
||||
Load {
|
||||
width: Funct3,
|
||||
dest: R,
|
||||
offset: i32,
|
||||
base: R,
|
||||
},
|
||||
Mv {
|
||||
dest: R,
|
||||
src: R,
|
||||
},
|
||||
La {
|
||||
dest: R,
|
||||
src: S,
|
||||
},
|
||||
Jal {
|
||||
dest: R,
|
||||
offset: i32,
|
||||
},
|
||||
Call(S),
|
||||
J(S),
|
||||
Branch {
|
||||
to: S,
|
||||
typ: Funct3,
|
||||
left: R,
|
||||
right: R,
|
||||
},
|
||||
Ret,
|
||||
ECall,
|
||||
EBreak,
|
||||
Li {
|
||||
dest: R,
|
||||
imm: i32,
|
||||
},
|
||||
}
|
||||
impl<R, S> LinkerInstruction<R, S> {
|
||||
pub fn map<R2, S2>(&self, r: impl Fn(&R) -> R2) -> LinkerInstruction<R2, S2> {
|
||||
self.try_map(|v| Some(r(v))).unwrap()
|
||||
}
|
||||
pub fn try_map<R2, S2>(&self, r: impl Fn(&R) -> Option<R2>) -> Option<LinkerInstruction<R2, S2>> {
|
||||
use LinkerInstruction as I;
|
||||
Some(match self {
|
||||
Self::ECall => I::ECall,
|
||||
Self::EBreak => I::EBreak,
|
||||
&Self::Li { ref dest, imm } => I::Li { dest: r(dest)?, imm },
|
||||
Self::Mv { ref dest, src } => I::Mv {
|
||||
dest: r(dest)?,
|
||||
src: r(src)?,
|
||||
},
|
||||
Self::La { .. } => todo!(),
|
||||
&Self::Load {
|
||||
width,
|
||||
ref dest,
|
||||
ref base,
|
||||
offset,
|
||||
} => I::Load {
|
||||
width,
|
||||
dest: r(dest)?,
|
||||
offset,
|
||||
base: r(base)?,
|
||||
},
|
||||
&Self::Store {
|
||||
width,
|
||||
ref src,
|
||||
ref base,
|
||||
offset,
|
||||
} => I::Store {
|
||||
width,
|
||||
src: r(src)?,
|
||||
offset,
|
||||
base: r(base)?,
|
||||
},
|
||||
&Self::Op {
|
||||
op,
|
||||
funct,
|
||||
ref dest,
|
||||
ref src1,
|
||||
ref src2,
|
||||
} => I::Op {
|
||||
op,
|
||||
funct,
|
||||
dest: r(dest)?,
|
||||
src1: r(src1)?,
|
||||
src2: r(src2)?,
|
||||
},
|
||||
&Self::OpImm { op, ref dest, ref src, imm } => I::OpImm {
|
||||
op,
|
||||
dest: r(dest)?,
|
||||
src: r(src)?,
|
||||
imm,
|
||||
},
|
||||
&Self::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
ref dest,
|
||||
ref src,
|
||||
imm,
|
||||
} => I::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
dest: r(dest)?,
|
||||
src: r(src)?,
|
||||
imm,
|
||||
},
|
||||
Self::Ret => I::Ret,
|
||||
Self::Call(..) => todo!(),
|
||||
Self::Jal { .. } => todo!(),
|
||||
Self::J(..) => todo!(),
|
||||
Self::Branch { .. } => todo!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
|
||||
opi(op32i::ADD, dest, src, imm.to_u())
|
||||
}
|
||||
|
||||
pub fn ori(dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
|
||||
opi(op32i::OR, dest, src, imm)
|
||||
}
|
||||
|
||||
impl Instr for LinkerInstruction {
|
||||
fn push_to(
|
||||
&self,
|
||||
data: &mut Vec<u8>,
|
||||
sym_map: &mut SymTable,
|
||||
pos: Addr,
|
||||
missing: bool,
|
||||
) -> Option<Symbol> {
|
||||
let last = match self {
|
||||
Self::Op {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src1,
|
||||
src2,
|
||||
} => opr(*op, *funct, *dest, *src1, *src2),
|
||||
Self::OpImm { op, dest, src, imm } => opi(*op, *dest, *src, BitsI32::new(*imm).to_u()),
|
||||
Self::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src,
|
||||
imm,
|
||||
} => opif7(*op, *funct, *dest, *src, BitsI32::new(*imm)),
|
||||
Self::Store {
|
||||
width,
|
||||
src,
|
||||
offset,
|
||||
base,
|
||||
} => store(*width, *src, BitsI32::new(*offset), *base),
|
||||
Self::Load {
|
||||
width,
|
||||
dest,
|
||||
offset,
|
||||
base,
|
||||
} => load(*width, *dest, BitsI32::new(*offset), *base),
|
||||
Self::Mv { dest, src } => addi(*dest, *src, BitsI32::new(0)),
|
||||
Self::La { dest, src } => {
|
||||
if let Some(addr) = sym_map.get(*src) {
|
||||
let offset = addr.val() as i32 - pos.val() as i32;
|
||||
let sign = offset.signum();
|
||||
let mut lower = offset % 0x1000;
|
||||
let mut upper = offset - lower;
|
||||
if (((lower >> 11) & 1) == 1) ^ (sign == -1) {
|
||||
let add = sign << 12;
|
||||
upper += add;
|
||||
lower = offset - upper;
|
||||
}
|
||||
assert!(upper + (lower << 20 >> 20) == offset);
|
||||
data.extend(auipc(*dest, BitsI32::new(upper)).to_le_bytes());
|
||||
addi(*dest, *dest, BitsI32::new(lower))
|
||||
} else {
|
||||
data.extend_from_slice(&[0; 2 * 4]);
|
||||
return Some(*src);
|
||||
}
|
||||
}
|
||||
Self::Jal { dest, offset } => jal(*dest, BitsI32::new(*offset)),
|
||||
Self::J(sym) => {
|
||||
if let Some(addr) = sym_map.get(*sym) {
|
||||
let offset = addr.val() as i32 - pos.val() as i32;
|
||||
j(BitsI32::new(offset))
|
||||
} else {
|
||||
data.extend_from_slice(&[0; 4]);
|
||||
return Some(*sym);
|
||||
}
|
||||
}
|
||||
Self::Call(sym) => {
|
||||
if let Some(addr) = sym_map.get(*sym) {
|
||||
let offset = addr.val() as i32 - pos.val() as i32;
|
||||
jal(ra, BitsI32::new(offset))
|
||||
} else {
|
||||
data.extend_from_slice(&[0; 4]);
|
||||
return Some(*sym);
|
||||
}
|
||||
}
|
||||
Self::Ret => ret(),
|
||||
Self::ECall => ecall(),
|
||||
Self::EBreak => ebreak(),
|
||||
Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm)),
|
||||
Self::Branch {
|
||||
to,
|
||||
typ,
|
||||
left,
|
||||
right,
|
||||
} => {
|
||||
if let Some(addr) = sym_map.get(*to) {
|
||||
let offset = addr.val() as i32 - pos.val() as i32;
|
||||
branch(*typ, *left, *right, BitsI32::new(offset))
|
||||
} else {
|
||||
data.extend_from_slice(&[0; 4]);
|
||||
return Some(*to);
|
||||
}
|
||||
}
|
||||
};
|
||||
data.extend(last.to_le_bytes());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkerInstruction {
|
||||
pub fn addi(dest: Reg, src: Reg, imm: i32) -> Self {
|
||||
Self::OpImm {
|
||||
op: op32i::ADD,
|
||||
dest,
|
||||
src,
|
||||
imm,
|
||||
}
|
||||
}
|
||||
pub fn sd(src: Reg, offset: i32, base: Reg) -> Self {
|
||||
Self::Store {
|
||||
width: width::D,
|
||||
src,
|
||||
offset,
|
||||
base,
|
||||
}
|
||||
}
|
||||
pub fn ld(dest: Reg, offset: i32, base: Reg) -> Self {
|
||||
Self::Load {
|
||||
width: width::D,
|
||||
dest,
|
||||
offset,
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is not even remotely worth it but technically it doesn't use the heap I think xdddddddddd
|
||||
impl<R: std::fmt::Debug, S: std::fmt::Debug> std::fmt::Debug for LinkerInstruction<R, S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_label(f, &|f, s| write!(f, "{s:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugInstr<'a, R, S, L: Fn(&mut std::fmt::Formatter<'_>, &S) -> std::fmt::Result> {
|
||||
instr: &'a LinkerInstruction<R, S>,
|
||||
label: &'a L,
|
||||
}
|
||||
|
||||
impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction<R, S> {
|
||||
fn fmt_label(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
label: &dyn crate::util::Labeler<S>,
|
||||
) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ECall => write!(f, "ecall"),
|
||||
Self::EBreak => write!(f, "ebreak"),
|
||||
Self::Li { dest, imm } => write!(f, "li {dest:?}, {imm:?}"),
|
||||
Self::Mv { dest, src } => write!(f, "mv {dest:?}, {src:?}"),
|
||||
Self::La { dest, src } => {
|
||||
write!(f, "la {dest:?}, @")?;
|
||||
label(f, src)
|
||||
}
|
||||
Self::Load {
|
||||
width,
|
||||
dest,
|
||||
offset,
|
||||
base,
|
||||
} => write!(f, "l{} {dest:?}, {offset}({base:?})", width::str(*width)),
|
||||
Self::Store {
|
||||
width,
|
||||
src,
|
||||
offset,
|
||||
base,
|
||||
} => write!(f, "s{} {src:?}, {offset}({base:?})", width::str(*width)),
|
||||
Self::Op {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src1,
|
||||
src2,
|
||||
} => write!(f, "{} {dest:?}, {src1:?}, {src2:?}", opstr(*op, *funct)),
|
||||
Self::OpImm { op, dest, src, imm } => {
|
||||
write!(f, "{}i {dest:?}, {src:?}, {imm}", opstr(*op, op32i::FUNCT7))
|
||||
}
|
||||
Self::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src,
|
||||
imm,
|
||||
} => write!(f, "{}i {dest:?}, {src:?}, {imm}", opstr(*op, *funct)),
|
||||
Self::Jal { dest, offset } => write!(f, "jal {dest:?}, {offset:?}"),
|
||||
Self::Call(s) => {
|
||||
write!(f, "call ")?;
|
||||
label(f, s)
|
||||
}
|
||||
Self::J(s) => {
|
||||
write!(f, "j ")?;
|
||||
label(f, s)
|
||||
}
|
||||
Self::Branch {
|
||||
to,
|
||||
typ,
|
||||
left,
|
||||
right,
|
||||
} => write!(f, "b{} {left:?} {right:?} {to:?}", branch::str(*typ)),
|
||||
Self::Ret => write!(f, "ret"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram},
|
||||
ir::{arch::riscv64::RegRef, LInstruction as IRI, LProgram, Len, Size, VarID},
|
||||
};
|
||||
|
||||
use super::{LinkerInstruction as LI, *};
|
||||
|
||||
fn align(s: &Size) -> i32 {
|
||||
(*s as i32 - 1).div_euclid(8) + 1
|
||||
}
|
||||
|
||||
fn mov_mem(
|
||||
v: &mut Vec<LI>,
|
||||
src: Reg,
|
||||
src_offset: i32,
|
||||
dest: Reg,
|
||||
dest_offset: i32,
|
||||
temp: Reg,
|
||||
mut len: Len,
|
||||
) {
|
||||
let mut off = 0;
|
||||
for width in width::MAIN.iter().rev().copied() {
|
||||
let wl = width::len(width);
|
||||
while len >= wl {
|
||||
v.extend([
|
||||
LI::Load {
|
||||
width,
|
||||
dest: temp,
|
||||
offset: src_offset + off,
|
||||
base: src,
|
||||
},
|
||||
LI::Store {
|
||||
width,
|
||||
src: temp,
|
||||
offset: dest_offset + off,
|
||||
base: dest,
|
||||
},
|
||||
]);
|
||||
len -= wl;
|
||||
off += wl as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
|
||||
let mut fns = Vec::new();
|
||||
let mut data = Vec::new();
|
||||
let mut dbg = DebugInfo::new(program.labels().to_vec());
|
||||
for (sym, d) in program.ro_data() {
|
||||
data.push((d.clone(), *sym));
|
||||
}
|
||||
for (sym, f) in program.fns() {
|
||||
let mut v = Vec::new();
|
||||
let mut stack = HashMap::new();
|
||||
let mut stack_len = 0;
|
||||
let mut stack_ra = None;
|
||||
let mut stack_rva = None;
|
||||
if f.makes_call {
|
||||
// return addr
|
||||
stack_ra = Some(stack_len);
|
||||
stack_len += 8;
|
||||
}
|
||||
for (id, s) in &f.stack {
|
||||
stack.insert(id, stack_len);
|
||||
stack_len += align(s);
|
||||
}
|
||||
for (id, s) in f.args.iter().rev() {
|
||||
stack.insert(id, stack_len);
|
||||
stack_len += align(s);
|
||||
}
|
||||
if f.ret_size > 0 {
|
||||
stack_rva = Some(stack_len);
|
||||
stack_len += align(&f.ret_size);
|
||||
}
|
||||
v.push(LI::addi(sp, sp, -stack_len));
|
||||
for (id, var) in &f.subvar_map {
|
||||
// TODO: ALIGN DOES NOT MAKE SENSE HERE!!! need to choose to decide in lower or asm
|
||||
stack.insert(id, stack[&var.id] + align(&var.offset));
|
||||
}
|
||||
let has_stack = stack_len > 0;
|
||||
if has_stack {
|
||||
if let Some(stack_ra) = stack_ra {
|
||||
v.push(LI::sd(ra, stack_ra, sp));
|
||||
}
|
||||
}
|
||||
let mut locations = HashMap::new();
|
||||
let mut irli = Vec::new();
|
||||
let mut ret = Vec::new();
|
||||
if has_stack {
|
||||
if let Some(stack_ra) = stack_ra {
|
||||
ret.push(LI::ld(ra, stack_ra, sp));
|
||||
}
|
||||
ret.push(LI::addi(sp, sp, stack_len));
|
||||
}
|
||||
ret.push(LI::Ret);
|
||||
|
||||
for i in &f.instructions {
|
||||
irli.push((v.len(), format!("{i:?}")));
|
||||
match i {
|
||||
IRI::Mv {
|
||||
dst: dest,
|
||||
dst_offset: dest_offset,
|
||||
src,
|
||||
src_offset,
|
||||
} => {
|
||||
let s = align(&f.stack[src]) as u32;
|
||||
mov_mem(
|
||||
&mut v,
|
||||
sp,
|
||||
stack[src] + align(src_offset),
|
||||
sp,
|
||||
stack[dest] + align(dest_offset),
|
||||
t0,
|
||||
s,
|
||||
);
|
||||
}
|
||||
IRI::Ref { dst: dest, src } => {
|
||||
v.push(LI::addi(t0, sp, stack[src]));
|
||||
v.push(LI::sd(t0, stack[dest], sp));
|
||||
}
|
||||
IRI::LoadAddr {
|
||||
dst: dest,
|
||||
offset,
|
||||
src,
|
||||
} => {
|
||||
v.extend([
|
||||
LI::La {
|
||||
dest: t0,
|
||||
src: *src,
|
||||
},
|
||||
LI::sd(t0, stack[dest] + *offset as i32, sp),
|
||||
]);
|
||||
}
|
||||
IRI::LoadData {
|
||||
dst: dest,
|
||||
offset,
|
||||
src,
|
||||
len,
|
||||
} => {
|
||||
v.push(LI::La {
|
||||
dest: t0,
|
||||
src: *src,
|
||||
});
|
||||
mov_mem(&mut v, t0, 0, sp, stack[dest] + *offset as i32, t1, *len);
|
||||
}
|
||||
IRI::Call { dst: dest, f, args } => {
|
||||
let mut offset = 0;
|
||||
if let Some((dest, s)) = dest {
|
||||
offset -= align(s);
|
||||
v.push(LI::addi(t0, sp, stack[&dest]));
|
||||
v.push(LI::sd(t0, offset, sp))
|
||||
}
|
||||
for (arg, s) in args {
|
||||
let bs = align(s);
|
||||
offset -= bs;
|
||||
mov_mem(&mut v, sp, stack[arg], sp, offset, t0, bs as Len);
|
||||
}
|
||||
v.push(LI::Call(*f));
|
||||
}
|
||||
IRI::AsmBlock {
|
||||
inputs,
|
||||
outputs,
|
||||
instructions,
|
||||
} => {
|
||||
for (reg, var) in inputs {
|
||||
v.push(LI::ld(*reg, stack[var], sp));
|
||||
}
|
||||
fn r(rr: &RegRef<VarID>) -> Reg {
|
||||
match rr {
|
||||
RegRef::Var(..) => todo!(),
|
||||
RegRef::Reg(reg) => *reg,
|
||||
}
|
||||
}
|
||||
for i in instructions {
|
||||
v.push(i.map(|v| r(v)));
|
||||
}
|
||||
for (reg, var) in outputs {
|
||||
v.push(LI::sd(*reg, stack[var], sp));
|
||||
}
|
||||
}
|
||||
IRI::Ret { src } => {
|
||||
if let Some(src) = src {
|
||||
let Some(rva) = stack_rva else {
|
||||
panic!("no return value address on stack!")
|
||||
};
|
||||
v.push(LI::ld(t0, rva, sp));
|
||||
mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
|
||||
}
|
||||
v.extend(&ret);
|
||||
}
|
||||
IRI::Jump(location) => {
|
||||
v.push(LI::J(*location));
|
||||
}
|
||||
IRI::Branch { to, cond } => {
|
||||
v.push(LI::ld(t0, stack[cond], sp));
|
||||
v.push(LI::Branch {
|
||||
to: *to,
|
||||
typ: branch::EQ,
|
||||
left: t0,
|
||||
right: zero,
|
||||
})
|
||||
}
|
||||
IRI::Mark(location) => {
|
||||
locations.insert(v.len(), *location);
|
||||
}
|
||||
}
|
||||
}
|
||||
dbg.push_fn(irli);
|
||||
fns.push(UnlinkedFunction {
|
||||
instrs: v,
|
||||
sym: *sym,
|
||||
locations,
|
||||
});
|
||||
}
|
||||
UnlinkedProgram {
|
||||
fns,
|
||||
ro_data: data,
|
||||
start: Some(program.entry()),
|
||||
dbg,
|
||||
sym_count: program.len(),
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
use crate::{
|
||||
compiler::arch::riscv::Reg,
|
||||
util::{Bits32, BitsI32},
|
||||
};
|
||||
|
||||
pub struct RawInstruction(u32);
|
||||
|
||||
impl RawInstruction {
|
||||
pub fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_le_bytes().into_iter()
|
||||
}
|
||||
pub fn to_be_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_be_bytes().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub const SYSTEM: u32 = 0b1110011;
|
||||
pub const LOAD: u32 = 0b0000011;
|
||||
pub const STORE: u32 = 0b0100011;
|
||||
pub const AUIPC: u32 = 0b0010111;
|
||||
pub const IMM_OP: u32 = 0b0010011;
|
||||
pub const OP: u32 = 0b0110011;
|
||||
pub const JAL: u32 = 0b1101111;
|
||||
pub const JALR: u32 = 0b1100111;
|
||||
pub const BRANCH: u32 = 0b1100011;
|
||||
|
||||
pub type Funct3 = Bits32<2, 0>;
|
||||
pub type Funct7 = Bits32<6, 0>;
|
||||
|
||||
use RawInstruction as I;
|
||||
|
||||
pub const fn r_type(
|
||||
funct7: Bits32<6, 0>,
|
||||
rs2: Reg,
|
||||
rs1: Reg,
|
||||
funct3: Bits32<2, 0>,
|
||||
rd: Reg,
|
||||
opcode: u32,
|
||||
) -> I {
|
||||
I((funct7.val() << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn i_type(imm: Bits32<11, 0>, rs1: Reg, funct: Funct3, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.val() << 20) + (rs1.val() << 15) + (funct.val() << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<11, 0>, opcode: u32) -> I {
|
||||
I((imm.bits(11, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 12)
|
||||
+ (imm.bits(4, 0) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<12, 1>, opcode: u32) -> I {
|
||||
I((imm.bit(12) << 31)
|
||||
+ (imm.bits(10, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 12)
|
||||
+ (imm.bits(4, 1) << 8)
|
||||
+ (imm.bit(11) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn u_type(imm: Bits32<31, 12>, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.bits(31, 12) << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.bit(20) << 31)
|
||||
+ (imm.bits(10, 1) << 21)
|
||||
+ (imm.bit(11) << 20)
|
||||
+ (imm.bits(19, 12) << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
|
||||
pub fn opr(op: Funct3, funct: Funct7, dest: Reg, src1: Reg, src2: Reg) -> I {
|
||||
r_type(funct, src2, src1, op, dest, OP)
|
||||
}
|
||||
pub fn opi(op: Funct3, dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
|
||||
i_type(imm, src, op, dest, IMM_OP)
|
||||
}
|
||||
pub fn opif7(op: Funct3, funct: Funct7, dest: Reg, src: Reg, imm: BitsI32<4, 0>) -> I {
|
||||
i_type(
|
||||
Bits32::new(imm.to_u().val() + (funct.val() << 5)),
|
||||
src,
|
||||
op,
|
||||
dest,
|
||||
IMM_OP,
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
mod base;
|
||||
mod rv32i;
|
||||
mod rv32m;
|
||||
mod string;
|
||||
pub use base::*;
|
||||
pub use rv32i::*;
|
||||
pub use rv32m::*;
|
||||
pub use string::*;
|
||||
@@ -1,122 +0,0 @@
|
||||
use crate::{compiler::arch::riscv::Reg, util::Bits32};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub mod op32i {
|
||||
use super::*;
|
||||
|
||||
pub const ADD: Funct3 = Funct3::new(0b000);
|
||||
pub const SL: Funct3 = Funct3::new(0b001);
|
||||
pub const SLT: Funct3 = Funct3::new(0b010);
|
||||
pub const SLTU: Funct3 = Funct3::new(0b011);
|
||||
pub const XOR: Funct3 = Funct3::new(0b100);
|
||||
pub const SR: Funct3 = Funct3::new(0b101);
|
||||
pub const OR: Funct3 = Funct3::new(0b110);
|
||||
pub const AND: Funct3 = Funct3::new(0b111);
|
||||
|
||||
pub const LOGICAL: Funct7 = Funct7::new(0b0000000);
|
||||
pub const ARITHMETIC: Funct7 = Funct7::new(0b0100000);
|
||||
pub const F7ADD: Funct7 = Funct7::new(0b0000000);
|
||||
pub const F7SUB: Funct7 = Funct7::new(0b0100000);
|
||||
|
||||
pub const FUNCT7: Funct7 = Funct7::new(0b0000000);
|
||||
}
|
||||
|
||||
pub mod width {
|
||||
use crate::ir::Len;
|
||||
|
||||
use super::*;
|
||||
pub const MAIN: [Funct3; 4] = [B, H, W, D];
|
||||
|
||||
pub const B: Funct3 = Funct3::new(0b000);
|
||||
pub const H: Funct3 = Funct3::new(0b001);
|
||||
pub const W: Funct3 = Funct3::new(0b010);
|
||||
pub const D: Funct3 = Funct3::new(0b011);
|
||||
pub const BU: Funct3 = Funct3::new(0b100);
|
||||
pub const HU: Funct3 = Funct3::new(0b101);
|
||||
pub const WU: Funct3 = Funct3::new(0b110);
|
||||
|
||||
pub const fn str(w: Funct3) -> &'static str {
|
||||
match w {
|
||||
B => "b",
|
||||
H => "h",
|
||||
W => "w",
|
||||
D => "d",
|
||||
BU => "bu",
|
||||
HU => "hu",
|
||||
WU => "wu",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn len(w: Funct3) -> Len {
|
||||
match w {
|
||||
B => 1,
|
||||
H => 2,
|
||||
W => 4,
|
||||
D => 8,
|
||||
BU => 1,
|
||||
HU => 2,
|
||||
WU => 4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod branch {
|
||||
use super::*;
|
||||
pub const EQ: Funct3 = Funct3::new(0b000);
|
||||
pub const NE: Funct3 = Funct3::new(0b001);
|
||||
pub const LT: Funct3 = Funct3::new(0b100);
|
||||
pub const GE: Funct3 = Funct3::new(0b101);
|
||||
pub const LTU: Funct3 = Funct3::new(0b110);
|
||||
pub const GEU: Funct3 = Funct3::new(0b111);
|
||||
|
||||
pub fn str(f: Funct3) -> &'static str {
|
||||
match f {
|
||||
EQ => "eq",
|
||||
NE => "ne",
|
||||
LT => "lt",
|
||||
GE => "ge",
|
||||
LTU => "ltu",
|
||||
GEU => "geu",
|
||||
_ => "?",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ecall() -> RawInstruction {
|
||||
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
|
||||
}
|
||||
pub const fn ebreak() -> RawInstruction {
|
||||
i_type(Bits32::new(1), zero, Bits32::new(0), zero, SYSTEM)
|
||||
}
|
||||
pub const fn auipc(dest: Reg, imm: BitsI32<31, 12>) -> RawInstruction {
|
||||
u_type(imm.to_u(), dest, AUIPC)
|
||||
}
|
||||
|
||||
pub const fn load(width: Funct3, dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
||||
i_type(offset.to_u(), base, width, dest, LOAD)
|
||||
}
|
||||
|
||||
pub const fn store(width: Funct3, src: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
||||
s_type(src, base, width, offset.to_u(), STORE)
|
||||
}
|
||||
|
||||
pub const fn jal(dest: Reg, offset: BitsI32<20, 1>) -> RawInstruction {
|
||||
j_type(offset.to_u(), dest, JAL)
|
||||
}
|
||||
pub const fn jalr(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> RawInstruction {
|
||||
i_type(offset.to_u(), base, Bits32::new(0), dest, JALR)
|
||||
}
|
||||
|
||||
pub const fn j(offset: BitsI32<20, 1>) -> RawInstruction {
|
||||
jal(zero, offset)
|
||||
}
|
||||
pub const fn ret() -> RawInstruction {
|
||||
jalr(zero, BitsI32::new(0), ra)
|
||||
}
|
||||
|
||||
pub const fn branch(typ: Funct3, left: Reg, right: Reg, offset: BitsI32<12, 1>) -> RawInstruction {
|
||||
b_type(right, left, typ, offset.to_u(), BRANCH)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use super::{Funct3, Funct7};
|
||||
|
||||
pub mod op32m {
|
||||
use super::*;
|
||||
pub const MUL: Funct3 = Funct3::new(0b000);
|
||||
pub const MULH: Funct3 = Funct3::new(0b001);
|
||||
pub const MULHSU: Funct3 = Funct3::new(0b010);
|
||||
pub const MULHU: Funct3 = Funct3::new(0b011);
|
||||
pub const DIV: Funct3 = Funct3::new(0b100);
|
||||
pub const DIVU: Funct3 = Funct3::new(0b101);
|
||||
pub const REM: Funct3 = Funct3::new(0b110);
|
||||
pub const REMU: Funct3 = Funct3::new(0b111);
|
||||
|
||||
pub const FUNCT7: Funct7 = Funct7::new(0b0000001);
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub fn opstr(op: Funct3, funct: Funct7) -> &'static str {
|
||||
match (op, funct) {
|
||||
(op32i::SLT, op32i::FUNCT7) => "slt",
|
||||
(op32i::SLTU, op32i::FUNCT7) => "sltu",
|
||||
(op32i::XOR, op32i::FUNCT7) => "xor",
|
||||
(op32i::OR, op32i::FUNCT7) => "or",
|
||||
(op32i::AND, op32i::FUNCT7) => "and",
|
||||
|
||||
(op32i::ADD, op32i::F7ADD) => "add",
|
||||
(op32i::ADD, op32i::F7SUB) => "sub",
|
||||
(op32i::SL, op32i::LOGICAL) => "sll",
|
||||
(op32i::SR, op32i::LOGICAL) => "srl",
|
||||
(op32i::SR, op32i::ARITHMETIC) => "sra",
|
||||
|
||||
(op32m::MUL, op32m::FUNCT7) => "mul",
|
||||
(op32m::MULH, op32m::FUNCT7) => "mulh",
|
||||
(op32m::MULHSU, op32m::FUNCT7) => "mulhsu",
|
||||
(op32m::MULHU, op32m::FUNCT7) => "mulhu",
|
||||
(op32m::DIV, op32m::FUNCT7) => "div",
|
||||
(op32m::DIVU, op32m::FUNCT7) => "divu",
|
||||
(op32m::REM, op32m::FUNCT7) => "rem",
|
||||
(op32m::REMU, op32m::FUNCT7) => "remu",
|
||||
_ => "unknown",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
mod asm;
|
||||
mod compile;
|
||||
mod reg;
|
||||
mod instr;
|
||||
|
||||
use crate::util::BitsI32;
|
||||
|
||||
pub use asm::*;
|
||||
pub use compile::*;
|
||||
pub use reg::*;
|
||||
pub use instr::*;
|
||||
@@ -1,180 +0,0 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Reg(u8);
|
||||
|
||||
/// hard wired 0
|
||||
pub const zero: Reg = Reg(0);
|
||||
/// return address
|
||||
pub const ra: Reg = Reg(1);
|
||||
/// stack pointer
|
||||
pub const sp: Reg = Reg(2);
|
||||
/// global pointer
|
||||
pub const gp: Reg = Reg(3);
|
||||
/// thread pointer
|
||||
pub const tp: Reg = Reg(4);
|
||||
/// temp / alternate link
|
||||
pub const t0: Reg = Reg(5);
|
||||
pub const t1: Reg = Reg(6);
|
||||
pub const t2: Reg = Reg(7);
|
||||
|
||||
pub const fp: Reg = Reg(8);
|
||||
pub const s0: Reg = Reg(8);
|
||||
pub const s1: Reg = Reg(9);
|
||||
|
||||
pub const a0: Reg = Reg(10);
|
||||
pub const a1: Reg = Reg(11);
|
||||
pub const a2: Reg = Reg(12);
|
||||
pub const a3: Reg = Reg(13);
|
||||
pub const a4: Reg = Reg(14);
|
||||
pub const a5: Reg = Reg(15);
|
||||
pub const a6: Reg = Reg(16);
|
||||
pub const a7: Reg = Reg(17);
|
||||
|
||||
pub const s2: Reg = Reg(18);
|
||||
pub const s3: Reg = Reg(19);
|
||||
pub const s4: Reg = Reg(20);
|
||||
pub const s5: Reg = Reg(21);
|
||||
pub const s6: Reg = Reg(22);
|
||||
pub const s7: Reg = Reg(23);
|
||||
pub const s8: Reg = Reg(24);
|
||||
pub const s9: Reg = Reg(25);
|
||||
pub const s10: Reg = Reg(26);
|
||||
pub const s11: Reg = Reg(27);
|
||||
|
||||
pub const t3: Reg = Reg(28);
|
||||
pub const t4: Reg = Reg(29);
|
||||
pub const t5: Reg = Reg(30);
|
||||
pub const t6: Reg = Reg(31);
|
||||
|
||||
impl Reg {
|
||||
#[inline]
|
||||
pub const fn val(&self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
pub fn from_str(str: &str) -> Option<Self> {
|
||||
Some(match str {
|
||||
"zero" => zero,
|
||||
"ra" => ra,
|
||||
"sp" => sp,
|
||||
"gp" => gp,
|
||||
"tp" => tp,
|
||||
"t0" => t0,
|
||||
"t1" => t1,
|
||||
"t2" => t2,
|
||||
"fp" => fp,
|
||||
"s0" => s0,
|
||||
"s1" => s1,
|
||||
"a0" => a0,
|
||||
"a1" => a1,
|
||||
"a2" => a2,
|
||||
"a3" => a3,
|
||||
"a4" => a4,
|
||||
"a5" => a5,
|
||||
"a6" => a6,
|
||||
"a7" => a7,
|
||||
"s2" => s2,
|
||||
"s3" => s3,
|
||||
"s4" => s4,
|
||||
"s5" => s5,
|
||||
"s6" => s6,
|
||||
"s7" => s7,
|
||||
"s8" => s8,
|
||||
"s9" => s9,
|
||||
"s10" => s10,
|
||||
"s11" => s11,
|
||||
"t3" => t3,
|
||||
"t4" => t4,
|
||||
"t5" => t5,
|
||||
"t6" => t6,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Reg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self.0 {
|
||||
0 => "zero",
|
||||
1 => "ra",
|
||||
2 => "sp",
|
||||
3 => "gp",
|
||||
4 => "tp",
|
||||
5 => "t0",
|
||||
6 => "t1",
|
||||
7 => "t2",
|
||||
8 => "fp",
|
||||
9 => "s1",
|
||||
10 => "a0",
|
||||
11 => "a1",
|
||||
12 => "a2",
|
||||
13 => "a3",
|
||||
14 => "a4",
|
||||
15 => "a5",
|
||||
16 => "a6",
|
||||
17 => "a7",
|
||||
18 => "s2",
|
||||
19 => "s3",
|
||||
20 => "s4",
|
||||
21 => "s5",
|
||||
22 => "s6",
|
||||
23 => "s7",
|
||||
24 => "s8",
|
||||
25 => "s9",
|
||||
26 => "s10",
|
||||
27 => "s11",
|
||||
28 => "t3",
|
||||
29 => "t4",
|
||||
30 => "t5",
|
||||
31 => "t6",
|
||||
_ => "unknown",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// pub const ft0: Reg = Reg(0);
|
||||
// pub const ft1: Reg = Reg(1);
|
||||
// pub const ft2: Reg = Reg(2);
|
||||
// pub const ft3: Reg = Reg(3);
|
||||
// pub const ft4: Reg = Reg(4);
|
||||
// pub const ft5: Reg = Reg(5);
|
||||
// pub const ft6: Reg = Reg(6);
|
||||
// pub const ft7: Reg = Reg(7);
|
||||
//
|
||||
// pub const fs0: Reg = Reg(8);
|
||||
// pub const fs1: Reg = Reg(9);
|
||||
//
|
||||
// pub const fa0: Reg = Reg(10);
|
||||
// pub const fa1: Reg = Reg(11);
|
||||
// pub const fa2: Reg = Reg(12);
|
||||
// pub const fa3: Reg = Reg(13);
|
||||
// pub const fa4: Reg = Reg(14);
|
||||
// pub const fa5: Reg = Reg(15);
|
||||
// pub const fa6: Reg = Reg(16);
|
||||
// pub const fa7: Reg = Reg(17);
|
||||
//
|
||||
// pub const fs2: Reg = Reg(18);
|
||||
// pub const fs3: Reg = Reg(19);
|
||||
// pub const fs4: Reg = Reg(20);
|
||||
// pub const fs5: Reg = Reg(21);
|
||||
// pub const fs6: Reg = Reg(22);
|
||||
// pub const fs7: Reg = Reg(23);
|
||||
// pub const fs8: Reg = Reg(24);
|
||||
// pub const fs9: Reg = Reg(25);
|
||||
// pub const fs10: Reg = Reg(26);
|
||||
// pub const fs11: Reg = Reg(27);
|
||||
//
|
||||
// pub const ft8: Reg = Reg(28);
|
||||
// pub const ft9: Reg = Reg(29);
|
||||
// pub const ft10: Reg = Reg(30);
|
||||
// pub const ft11: Reg = Reg(31);
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use crate::ir::Symbol;
|
||||
|
||||
pub struct DebugInfo {
|
||||
pub sym_labels: Vec<Option<String>>,
|
||||
pub ir_lower: Vec<Vec<(usize, String)>>,
|
||||
}
|
||||
|
||||
impl DebugInfo {
|
||||
pub fn new(sym_labels: Vec<Option<String>>) -> Self {
|
||||
Self {
|
||||
ir_lower: Vec::new(),
|
||||
sym_labels,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_fn(&mut self, instrs: Vec<(usize, String)>) {
|
||||
self.ir_lower.push(instrs);
|
||||
}
|
||||
|
||||
pub fn sym_label(&self, s: Symbol) -> Option<&String> {
|
||||
self.sym_labels[*s].as_ref()
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use super::{program::Addr, LinkedProgram};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ELF64Header {
|
||||
magic: u32,
|
||||
class: u8,
|
||||
endianness: u8,
|
||||
ei_version: u8,
|
||||
os_abi: u8,
|
||||
os_abi_ver: u8,
|
||||
pad: [u8; 7],
|
||||
ty: u16,
|
||||
machine: u16,
|
||||
e_version: u32,
|
||||
entry: u64,
|
||||
program_header_offset: u64,
|
||||
section_header_offset: u64,
|
||||
flags: u32,
|
||||
header_size: u16,
|
||||
program_header_entry_size: u16,
|
||||
program_header_num: u16,
|
||||
section_header_entry_size: u16,
|
||||
section_header_num: u16,
|
||||
section_header_str_idx: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ProgramHeader {
|
||||
ty: u32,
|
||||
flags: u32,
|
||||
offset: u64,
|
||||
vaddr: u64,
|
||||
paddr: u64,
|
||||
filesz: u64,
|
||||
memsz: u64,
|
||||
align: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SectionHeader {
|
||||
name_idx: u32,
|
||||
ty: u32,
|
||||
flags: u64,
|
||||
addr: u64,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
link: u32,
|
||||
info: u32,
|
||||
addr_align: u64,
|
||||
entry_size: u64,
|
||||
}
|
||||
|
||||
// this is currently specialized for riscv64; obviously add params later
|
||||
pub fn create(program: &[u8], start_offset: Addr) -> Vec<u8> {
|
||||
let addr_start = 0x1000;
|
||||
let page_size = 0x1000;
|
||||
// I don't know if I have to add addr_start here, idk how it maps the memory
|
||||
let program_size = std::mem::size_of_val(program) as u64 + addr_start;
|
||||
let program_header = ProgramHeader {
|
||||
ty: 0x1, // LOAD
|
||||
flags: 0b101, // executable, readable
|
||||
offset: 0x0,
|
||||
vaddr: addr_start,
|
||||
paddr: addr_start,
|
||||
filesz: program_size,
|
||||
memsz: program_size,
|
||||
align: page_size,
|
||||
};
|
||||
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
|
||||
let program_pos = header_len;
|
||||
let header = ELF64Header {
|
||||
magic: 0x7f_45_4c_46u32.swap_bytes(),
|
||||
class: 0x2, // 64 bit
|
||||
endianness: 0x1, // little endian
|
||||
ei_version: 0x1,
|
||||
os_abi: 0x0, // system-v
|
||||
os_abi_ver: 0x0,
|
||||
pad: [0x0; 7],
|
||||
ty: 0x2, // executable
|
||||
machine: 0xf3, // risc-v
|
||||
e_version: 0x1,
|
||||
entry: addr_start + program_pos + start_offset.val(),
|
||||
program_header_offset: size_of::<ELF64Header>() as u64,
|
||||
section_header_offset: 0x0,
|
||||
// C ABI (16 bit instruction align) + double precision floats
|
||||
flags: 0x1 | 0x4,
|
||||
header_size: size_of::<ELF64Header>() as u16,
|
||||
program_header_entry_size: size_of::<ProgramHeader>() as u16,
|
||||
program_header_num: 0x1,
|
||||
section_header_entry_size: size_of::<SectionHeader>() as u16,
|
||||
section_header_num: 0x0,
|
||||
section_header_str_idx: 0x0,
|
||||
};
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
bytes.extend(as_u8_slice(&header));
|
||||
bytes.extend(as_u8_slice(&program_header));
|
||||
bytes.extend(program);
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
|
||||
core::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
|
||||
}
|
||||
|
||||
impl LinkedProgram {
|
||||
pub fn to_elf(&self) -> Vec<u8> {
|
||||
create(&self.code, self.start.expect("no start found"))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub enum Instruction {
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
pub mod arch;
|
||||
mod debug;
|
||||
mod elf;
|
||||
mod program;
|
||||
mod target;
|
||||
|
||||
use arch::riscv;
|
||||
pub use program::*;
|
||||
|
||||
use crate::ir::LProgram;
|
||||
|
||||
pub fn compile(program: &LProgram) -> UnlinkedProgram<riscv::LinkerInstruction> {
|
||||
arch::riscv::compile(program)
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
ir::Symbol,
|
||||
util::{Labelable, LabeledFmt},
|
||||
};
|
||||
|
||||
use super::debug::DebugInfo;
|
||||
|
||||
pub struct LinkedProgram {
|
||||
pub code: Vec<u8>,
|
||||
pub start: Option<Addr>,
|
||||
}
|
||||
|
||||
pub struct UnlinkedProgram<I: Instr> {
|
||||
pub fns: Vec<UnlinkedFunction<I>>,
|
||||
pub ro_data: Vec<(Vec<u8>, Symbol)>,
|
||||
pub sym_count: usize,
|
||||
pub start: Option<Symbol>,
|
||||
pub dbg: DebugInfo,
|
||||
}
|
||||
|
||||
pub struct UnlinkedFunction<I: Instr> {
|
||||
pub instrs: Vec<I>,
|
||||
pub sym: Symbol,
|
||||
pub locations: HashMap<usize, Symbol>,
|
||||
}
|
||||
|
||||
impl<I: Instr + std::fmt::Debug> UnlinkedProgram<I> {
|
||||
pub fn link(self) -> LinkedProgram {
|
||||
let mut data = Vec::new();
|
||||
let mut sym_table = SymTable::new(self.sym_count);
|
||||
let mut missing = HashMap::<Symbol, Vec<(Addr, I)>>::new();
|
||||
for (val, id) in self.ro_data {
|
||||
sym_table.insert(id, Addr(data.len() as u64));
|
||||
data.extend(val);
|
||||
}
|
||||
data.resize(data.len() + (4 - data.len() % 4), 0);
|
||||
for f in self.fns {
|
||||
let mut added = vec![f.sym];
|
||||
sym_table.insert(f.sym, Addr(data.len() as u64));
|
||||
for (i, instr) in f.instrs.into_iter().enumerate() {
|
||||
let i_pos = Addr(data.len() as u64);
|
||||
if let Some(sym) = f.locations.get(&i) {
|
||||
sym_table.insert(*sym, i_pos);
|
||||
added.push(*sym);
|
||||
}
|
||||
if let Some(sym) = instr.push_to(&mut data, &mut sym_table, i_pos, false) {
|
||||
if let Some(vec) = missing.get_mut(&sym) {
|
||||
vec.push((i_pos, instr));
|
||||
} else {
|
||||
missing.insert(sym, vec![(i_pos, instr)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for add in added {
|
||||
if let Some(vec) = missing.remove(&add) {
|
||||
for (addr, i) in vec {
|
||||
let mut replace = Vec::new();
|
||||
i.push_to(&mut replace, &mut sym_table, addr, true);
|
||||
let pos = addr.val() as usize;
|
||||
data[pos..pos + replace.len()].copy_from_slice(&replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(missing.is_empty());
|
||||
LinkedProgram {
|
||||
code: data,
|
||||
start: self
|
||||
.start
|
||||
.map(|s| sym_table.get(s).expect("start symbol doesn't exist")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instr {
|
||||
fn push_to(
|
||||
&self,
|
||||
data: &mut Vec<u8>,
|
||||
syms: &mut SymTable,
|
||||
pos: Addr,
|
||||
missing: bool,
|
||||
) -> Option<Symbol>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Addr(u64);
|
||||
impl Addr {
|
||||
const NONE: Self = Self(!0);
|
||||
pub fn val(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SymTable(Vec<Addr>);
|
||||
impl SymTable {
|
||||
pub fn new(len: usize) -> Self {
|
||||
Self(vec![Addr::NONE; len])
|
||||
}
|
||||
pub fn insert(&mut self, sym: Symbol, addr: Addr) {
|
||||
self.0[*sym] = addr;
|
||||
}
|
||||
pub fn get(&self, sym: Symbol) -> Option<Addr> {
|
||||
match self.0[*sym] {
|
||||
Addr::NONE => None,
|
||||
addr => Some(addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (fun, irli) in self.fns.iter().zip(&self.dbg.ir_lower) {
|
||||
writeln!(f, "{}:", self.dbg.sym_label(fun.sym).unwrap())?;
|
||||
let mut liter = irli.iter();
|
||||
let mut cur = liter.next();
|
||||
for (i, instr) in fun.instrs.iter().enumerate() {
|
||||
while let Some(c) = cur
|
||||
&& i == c.0
|
||||
{
|
||||
writeln!(f, " {}:", c.1)?;
|
||||
cur = liter.next();
|
||||
}
|
||||
writeln!(
|
||||
f,
|
||||
" {:?}",
|
||||
instr.labeled(&|f: &mut std::fmt::Formatter, s: &Symbol| write!(
|
||||
f,
|
||||
"{}",
|
||||
self.dbg.sym_label(*s).unwrap_or(&format!("{:?}", *s))
|
||||
))
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
pub trait Target {
|
||||
type Reg;
|
||||
}
|
||||
|
||||
pub trait RegType {
|
||||
type Size;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod riscv64;
|
||||
@@ -1,20 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{compiler::arch::riscv::*, ir::IdentID};
|
||||
|
||||
pub type RV64Instruction<V = IdentID> = LinkerInstruction<RegRef<V>, V>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum RegRef<V = IdentID, R = Reg> {
|
||||
Var(V),
|
||||
Reg(R),
|
||||
}
|
||||
|
||||
impl<V: Debug, R: Debug> Debug for RegRef<V, R> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Var(v) => write!(f, "{{{:?}}}", v),
|
||||
Self::Reg(r) => r.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
use super::{arch::riscv64::RegRef, IdentID};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IRAsmInstruction {
|
||||
op: String,
|
||||
args: Vec<RegRef<IdentID, String>>,
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
// I had an idea for why these were different... now I don't
|
||||
pub type Size = u32;
|
||||
pub type Len = u32;
|
||||
|
||||
pub struct ID<T>(pub usize, PhantomData<T>);
|
||||
|
||||
impl<T> ID<T> {
|
||||
pub fn new(i: usize) -> Self {
|
||||
Self(i, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<usize> for ID<T> {
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for ID<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{{{}}}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for ID<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for ID<T> {}
|
||||
|
||||
impl<T> std::hash::Hash for ID<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for ID<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for ID<T> {}
|
||||
|
||||
// :fear:
|
||||
impl<T> Index<ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&mut ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&mut ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: &ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&mut ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&mut ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
use super::*;
|
||||
use crate::{compiler::arch::riscv::Reg, ir::arch::riscv64::RegRef};
|
||||
use arch::riscv64::RV64Instruction;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IRLFunction {
|
||||
pub instructions: Vec<LInstruction>,
|
||||
pub stack: HashMap<VarID, Size>,
|
||||
pub subvar_map: HashMap<VarID, VarOffset>,
|
||||
pub args: Vec<(VarID, Size)>,
|
||||
pub ret_size: Size,
|
||||
pub makes_call: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LInstruction {
|
||||
Mv {
|
||||
dst: VarID,
|
||||
dst_offset: Size,
|
||||
src: VarID,
|
||||
src_offset: Size,
|
||||
},
|
||||
Ref {
|
||||
dst: VarID,
|
||||
src: VarID,
|
||||
},
|
||||
LoadAddr {
|
||||
dst: VarID,
|
||||
offset: Size,
|
||||
src: Symbol,
|
||||
},
|
||||
LoadData {
|
||||
dst: VarID,
|
||||
offset: Size,
|
||||
src: Symbol,
|
||||
len: Len,
|
||||
},
|
||||
Call {
|
||||
dst: Option<(VarID, Size)>,
|
||||
f: Symbol,
|
||||
args: Vec<(VarID, Size)>,
|
||||
},
|
||||
AsmBlock {
|
||||
instructions: Vec<RV64Instruction<VarID>>,
|
||||
inputs: Vec<(Reg, VarID)>,
|
||||
outputs: Vec<(Reg, VarID)>,
|
||||
},
|
||||
Ret {
|
||||
src: Option<VarID>,
|
||||
},
|
||||
// TODO I feel like this should be turned into control flow instructions, maybe...
|
||||
// not sure but LLVM has them so might be right play; seems optimal for optimization
|
||||
Jump(Symbol),
|
||||
Branch {
|
||||
to: Symbol,
|
||||
cond: VarID,
|
||||
},
|
||||
Mark(Symbol),
|
||||
}
|
||||
|
||||
impl LInstruction {
|
||||
pub fn is_ret(&self) -> bool {
|
||||
matches!(self, Self::Ret { .. })
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
mod func;
|
||||
mod program;
|
||||
mod symbol;
|
||||
mod res;
|
||||
|
||||
pub use func::*;
|
||||
pub use program::*;
|
||||
pub use symbol::*;
|
||||
|
||||
use super::*;
|
||||
@@ -1,465 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{
|
||||
IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, UInstruction, UProgram, VarID,
|
||||
};
|
||||
use crate::ir::{
|
||||
AsmBlockArgType, Size, StructInst, SymbolSpace, Type, TypeID, UFunc, UInstrInst, VarOffset,
|
||||
};
|
||||
|
||||
pub struct LProgram {
|
||||
sym_space: SymbolSpace,
|
||||
entry: Symbol,
|
||||
}
|
||||
|
||||
// NOTE: there are THREE places here where I specify size (8)
|
||||
|
||||
impl LProgram {
|
||||
pub fn create(p: &UProgram) -> Result<Self, String> {
|
||||
let start = p
|
||||
.names
|
||||
.id::<UFunc>(&[], "crate")
|
||||
.ok_or("no start method found")?;
|
||||
let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
|
||||
let entry = ssbuilder.func(&start);
|
||||
while let Some((sym, i)) = ssbuilder.pop_fn() {
|
||||
let f = &p.fns[i.0];
|
||||
let mut fbuilder = LFunctionBuilder::new(p, &mut ssbuilder);
|
||||
for i in &f.instructions {
|
||||
fbuilder.insert_instr(i);
|
||||
}
|
||||
if fbuilder.instrs.last().is_none_or(|i| !i.is_ret()) {
|
||||
fbuilder.instrs.push(LInstruction::Ret { src: None });
|
||||
}
|
||||
let res = fbuilder.finish(f);
|
||||
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
||||
}
|
||||
let sym_space = ssbuilder.finish().expect("we failed the mission");
|
||||
Ok(Self { sym_space, entry })
|
||||
}
|
||||
|
||||
pub fn entry(&self) -> Symbol {
|
||||
self.entry
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LStructInst {
|
||||
offsets: Vec<Len>,
|
||||
types: Vec<Type>,
|
||||
order: HashMap<String, usize>,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
impl LStructInst {
|
||||
pub fn offset(&self, name: &str) -> Option<Len> {
|
||||
Some(self.offsets[*self.order.get(name)?])
|
||||
}
|
||||
pub fn ty(&self, name: &str) -> Option<&Type> {
|
||||
Some(&self.types[*self.order.get(name)?])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LFunctionBuilder<'a> {
|
||||
data: LFunctionBuilderData<'a>,
|
||||
program: &'a UProgram,
|
||||
}
|
||||
|
||||
impl<'a> LFunctionBuilderData<'a> {
|
||||
pub fn new(builder: &'a mut SymbolSpaceBuilder) -> Self {
|
||||
Self {
|
||||
instrs: Vec::new(),
|
||||
struct_insts: HashMap::new(),
|
||||
stack: HashMap::new(),
|
||||
subvar_map: HashMap::new(),
|
||||
makes_call: false,
|
||||
builder,
|
||||
loopp: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LFunctionBuilderData<'a> {
|
||||
builder: &'a mut SymbolSpaceBuilder,
|
||||
instrs: Vec<LInstruction>,
|
||||
stack: HashMap<VarID, Size>,
|
||||
subvar_map: HashMap<VarID, VarOffset>,
|
||||
struct_insts: HashMap<StructInst, LStructInst>,
|
||||
makes_call: bool,
|
||||
loopp: Option<LoopCtx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LoopCtx {
|
||||
top: Symbol,
|
||||
bot: Symbol,
|
||||
}
|
||||
|
||||
impl<'a> LFunctionBuilder<'a> {
|
||||
pub fn new(program: &'a UProgram, builder: &'a mut SymbolSpaceBuilder) -> Self {
|
||||
Self {
|
||||
data: LFunctionBuilderData::new(builder),
|
||||
program,
|
||||
}
|
||||
}
|
||||
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
|
||||
if self
|
||||
.data
|
||||
.size_of_var(self.program, i)
|
||||
.expect("unsized type")
|
||||
== 0
|
||||
{
|
||||
return None;
|
||||
};
|
||||
self.map_subvar(i);
|
||||
let var = self.data.var_offset(self.program, i).expect("var offset");
|
||||
if !self.stack.contains_key(&var.id) {
|
||||
let size = self
|
||||
.data
|
||||
.size_of_var(self.program, var.id)
|
||||
.expect("unsized type");
|
||||
self.data.stack.insert(var.id, size);
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
pub fn map_subvar(&mut self, i: VarID) {
|
||||
let off = self.data.var_offset(self.program, i).expect("var offset");
|
||||
if off.id != i {
|
||||
self.subvar_map.insert(i, off);
|
||||
}
|
||||
}
|
||||
pub fn insert_instr(&mut self, i: &UInstrInst) -> Option<Option<String>> {
|
||||
match i
|
||||
.i
|
||||
.resolve(self.program)
|
||||
.expect("failed to resolve during lowering")
|
||||
{
|
||||
UInstruction::Mv { dst, src } => {
|
||||
self.alloc_stack(dst)?;
|
||||
self.map_subvar(src);
|
||||
self.instrs.push(LInstruction::Mv {
|
||||
dst,
|
||||
dst_offset: 0,
|
||||
src,
|
||||
src_offset: 0,
|
||||
});
|
||||
}
|
||||
UInstruction::Ref { dst, src } => {
|
||||
self.alloc_stack(dst)?;
|
||||
self.map_subvar(src);
|
||||
self.instrs.push(LInstruction::Ref { dst, src });
|
||||
}
|
||||
UInstruction::Deref { dst, src } => {
|
||||
todo!()
|
||||
}
|
||||
UInstruction::LoadData { dst, src } => {
|
||||
self.alloc_stack(dst)?;
|
||||
let data = &self.program.data[src];
|
||||
let sym = self.data.builder.ro_data(
|
||||
src,
|
||||
&data.content,
|
||||
Some(&self.program.data[src].name),
|
||||
);
|
||||
self.instrs.push(LInstruction::LoadData {
|
||||
dst,
|
||||
offset: 0,
|
||||
len: data.content.len() as Len,
|
||||
src: sym,
|
||||
});
|
||||
}
|
||||
UInstruction::LoadSlice { dst, src } => {
|
||||
self.alloc_stack(dst)?;
|
||||
let data = &self.program.data[src];
|
||||
let Type::Array(_, len) = &self.program.types[data.ty] else {
|
||||
return Some(Some(format!(
|
||||
"tried to load {} as slice",
|
||||
self.program.type_name(&data.ty)
|
||||
)));
|
||||
};
|
||||
let sym = self.data.builder.ro_data(
|
||||
src,
|
||||
&data.content,
|
||||
Some(&self.program.data[src].name),
|
||||
);
|
||||
self.instrs.push(LInstruction::LoadAddr {
|
||||
dst,
|
||||
offset: 0,
|
||||
src: sym,
|
||||
});
|
||||
|
||||
let sym = self
|
||||
.builder
|
||||
.anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len)));
|
||||
self.instrs.push(LInstruction::LoadData {
|
||||
dst,
|
||||
offset: 8,
|
||||
len: 8,
|
||||
src: sym,
|
||||
});
|
||||
}
|
||||
UInstruction::Call { dst, f, args } => {
|
||||
self.alloc_stack(dst);
|
||||
self.makes_call = true;
|
||||
let sym = self.builder.func(f.id);
|
||||
let ret_size = self
|
||||
.data
|
||||
.size_of_var(self.program, dst)
|
||||
.expect("unsized type");
|
||||
let dst = if ret_size > 0 {
|
||||
Some((dst, ret_size))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let call = LInstruction::Call {
|
||||
dst,
|
||||
f: sym,
|
||||
args: args
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
self.map_subvar(id);
|
||||
(
|
||||
id,
|
||||
self.data
|
||||
.size_of_var(self.program, id)
|
||||
.expect("unsized type"),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
self.instrs.push(call);
|
||||
}
|
||||
UInstruction::AsmBlock { instructions, args } => {
|
||||
let mut inputs = Vec::new();
|
||||
let mut outputs = Vec::new();
|
||||
for a in args {
|
||||
match a.ty {
|
||||
AsmBlockArgType::In => {
|
||||
self.map_subvar(a.var);
|
||||
inputs.push((a.reg, a.var))
|
||||
}
|
||||
AsmBlockArgType::Out => {
|
||||
self.alloc_stack(a.var)?;
|
||||
outputs.push((a.reg, a.var));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.instrs.push(LInstruction::AsmBlock {
|
||||
instructions: instructions.clone(),
|
||||
inputs,
|
||||
outputs,
|
||||
})
|
||||
}
|
||||
UInstruction::Ret { src } => {
|
||||
self.map_subvar(src);
|
||||
let src = if self
|
||||
.data
|
||||
.size_of_var(self.program, src)
|
||||
.expect("unsized var")
|
||||
== 0
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(src)
|
||||
};
|
||||
self.data.instrs.push(LInstruction::Ret { src })
|
||||
}
|
||||
UInstruction::Construct {
|
||||
dst,
|
||||
ref struc,
|
||||
ref fields,
|
||||
} => {
|
||||
self.alloc_stack(dst)?;
|
||||
for (field, &src) in fields {
|
||||
self.map_subvar(src);
|
||||
let i = LInstruction::Mv {
|
||||
dst,
|
||||
src,
|
||||
dst_offset: self
|
||||
.data
|
||||
.field_offset(self.program, struc, field)
|
||||
.expect("field offset"),
|
||||
src_offset: 0,
|
||||
};
|
||||
self.instrs.push(i)
|
||||
}
|
||||
}
|
||||
UInstruction::If { cond, body } => {
|
||||
self.map_subvar(cond);
|
||||
let sym = self.builder.reserve();
|
||||
self.instrs.push(LInstruction::Branch { to: *sym, cond });
|
||||
for i in body {
|
||||
self.insert_instr(&i);
|
||||
}
|
||||
self.instrs.push(LInstruction::Mark(*sym));
|
||||
}
|
||||
UInstruction::Loop { body } => {
|
||||
let top = self.builder.reserve();
|
||||
let bot = self.builder.reserve();
|
||||
let old = self.loopp;
|
||||
self.loopp = Some(LoopCtx {
|
||||
bot: *bot,
|
||||
top: *top,
|
||||
});
|
||||
self.instrs.push(LInstruction::Mark(*top));
|
||||
for i in body {
|
||||
self.insert_instr(i);
|
||||
}
|
||||
self.instrs.push(LInstruction::Jump(*top));
|
||||
self.instrs.push(LInstruction::Mark(*bot));
|
||||
self.loopp = old;
|
||||
}
|
||||
UInstruction::Break => {
|
||||
self.data.instrs.push(LInstruction::Jump(
|
||||
self.data.loopp.expect("Tried to break outside of loop").bot,
|
||||
));
|
||||
}
|
||||
UInstruction::Continue => {
|
||||
self.data.instrs.push(LInstruction::Jump(
|
||||
self.data.loopp.expect("Tried to break outside of loop").top,
|
||||
));
|
||||
}
|
||||
};
|
||||
Some(None)
|
||||
}
|
||||
|
||||
pub fn finish(mut self, f: &UFunc) -> IRLFunction {
|
||||
IRLFunction {
|
||||
args: f
|
||||
.args
|
||||
.iter()
|
||||
.map(|a| {
|
||||
(
|
||||
*a,
|
||||
self.data
|
||||
.size_of_var(self.program, *a)
|
||||
.expect("unsized type"),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
ret_size: self
|
||||
.data
|
||||
.size_of_type(self.program, &f.ret)
|
||||
.expect("unsized type"),
|
||||
instructions: self.data.instrs,
|
||||
makes_call: self.data.makes_call,
|
||||
stack: self.data.stack,
|
||||
subvar_map: self.data.subvar_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LFunctionBuilderData<'_> {
|
||||
pub fn var_offset(&mut self, p: &UProgram, mut var: VarID) -> Option<VarOffset> {
|
||||
let mut path = Vec::new();
|
||||
while let Type::Field(parent) = &p.get(var)?.ty {
|
||||
var = parent.parent;
|
||||
path.push(&parent.name);
|
||||
}
|
||||
let mut ty = &p.get(var)?.ty;
|
||||
let mut offset = 0;
|
||||
while let Type::Struct(sty) = ty {
|
||||
let Some(name) = path.pop() else {
|
||||
break;
|
||||
};
|
||||
offset += self.field_offset(p, sty, &name)?;
|
||||
ty = p.struct_field_type(sty, name).expect("bad field");
|
||||
}
|
||||
Some(VarOffset { id: var, offset })
|
||||
}
|
||||
pub fn addr_size(&self) -> Size {
|
||||
64
|
||||
}
|
||||
pub fn struct_inst(&mut self, p: &UProgram, ty: &StructInst) -> &LStructInst {
|
||||
// normally I'd let Some(..) here and return, but polonius does not exist :grief:
|
||||
if self.struct_insts.get(ty).is_none() {
|
||||
let LStructInst { id, args } = ty;
|
||||
let struc = p.expect(*id);
|
||||
let mut types = Vec::new();
|
||||
let mut sizes = struc
|
||||
.fields
|
||||
.iter()
|
||||
.map(|(n, f)| {
|
||||
let ty = if let Type::Generic { id } = &f.ty {
|
||||
struc
|
||||
.generics
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, g)| if *g == *id { args.get(i) } else { None })
|
||||
.unwrap_or(&f.ty)
|
||||
} else {
|
||||
&f.ty
|
||||
};
|
||||
types.push(ty.clone());
|
||||
(n, self.size_of_type(p, ty).expect("unsized type"))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
sizes.sort_by(|(n1, s1, ..), (n2, s2, ..)| s1.cmp(s2).then_with(|| n1.cmp(n2)));
|
||||
let mut offset = 0;
|
||||
let mut offsets = Vec::new();
|
||||
let mut order = HashMap::new();
|
||||
for (i, (name, size)) in sizes.iter().rev().enumerate() {
|
||||
// TODO: alignment!!!
|
||||
order.insert(name.to_string(), i);
|
||||
offsets.push(offset);
|
||||
offset += size;
|
||||
}
|
||||
self.struct_insts.insert(
|
||||
ty.clone(),
|
||||
LStructInst {
|
||||
offsets,
|
||||
order,
|
||||
types,
|
||||
size: offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
self.struct_insts.get(ty).unwrap()
|
||||
}
|
||||
|
||||
pub fn field_offset(&mut self, p: &UProgram, sty: &StructInst, field: &str) -> Option<Len> {
|
||||
let inst = self.struct_inst(p, sty);
|
||||
Some(inst.offset(field)?)
|
||||
}
|
||||
|
||||
pub fn size_of_type(&mut self, p: &UProgram, ty: &TypeID) -> Option<Size> {
|
||||
// TODO: target matters
|
||||
Some(match &p.types[ty] {
|
||||
Type::Bits(b) => *b,
|
||||
Type::Struct(ty) => self.struct_inst(p, ty).size,
|
||||
Type::Generic(id) => return None,
|
||||
// function references are resolved at compile time into direct calls,
|
||||
// so they don't have any size as arguments
|
||||
Type::FnInst(fi) => 0,
|
||||
Type::Ref(_) => self.addr_size(),
|
||||
Type::Array(ty, len) => self.size_of_type(p, ty)? * len,
|
||||
Type::Slice(_) => self.addr_size() * 2,
|
||||
Type::Unit => 0,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn size_of_var(&mut self, p: &UProgram, var: VarID) -> Option<Size> {
|
||||
self.size_of_type(p, &p.get(var)?.ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Deref for LFunctionBuilder<'a> {
|
||||
type Target = LFunctionBuilderData<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::DerefMut for LFunctionBuilder<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for LProgram {
|
||||
type Target = SymbolSpace;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.sym_space
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
use crate::ir::{
|
||||
arch::riscv64::{RV64Instruction, RegRef},
|
||||
AsmBlockArg, Resolved, UInstrInst, UInstruction, UProgram, VarID,
|
||||
};
|
||||
|
||||
impl UInstrInst {
|
||||
pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option<UInstrInst<Resolved>> {
|
||||
Some(UInstrInst {
|
||||
i: self.i.resolve(p)?,
|
||||
origin: self.origin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UInstruction {
|
||||
pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option<UInstruction<Resolved>> {
|
||||
use UInstruction as I;
|
||||
Some(match self {
|
||||
I::Mv { dst, src } => I::Mv {
|
||||
dst: dst.var(p)?,
|
||||
src: src.var(p)?,
|
||||
},
|
||||
I::Ref { dst, src } => I::Ref {
|
||||
dst: dst.var(p)?,
|
||||
src: src.var(p)?,
|
||||
},
|
||||
I::Deref { dst, src } => I::Deref {
|
||||
dst: dst.var(p)?,
|
||||
src: src.var(p)?,
|
||||
},
|
||||
I::LoadData { dst, src } => I::LoadData {
|
||||
dst: dst.var(p)?,
|
||||
src: *src,
|
||||
},
|
||||
I::LoadSlice { dst, src } => I::LoadSlice {
|
||||
dst: dst.var(p)?,
|
||||
src: *src,
|
||||
},
|
||||
I::Call { dst, f, args } => I::Call {
|
||||
dst: dst.var(p)?,
|
||||
f: f.fun(p)?.clone(),
|
||||
args: args.iter().map(|i| i.var(p)).try_collect()?,
|
||||
},
|
||||
I::AsmBlock { instructions, args } => I::AsmBlock {
|
||||
instructions: instructions
|
||||
.iter()
|
||||
.map(|i| i.resolve(p))
|
||||
.collect::<Option<_>>()?,
|
||||
args: args.iter().map(|a| a.resolve(p)).try_collect()?,
|
||||
},
|
||||
I::Ret { src } => I::Ret { src: src.var(p)? },
|
||||
I::Construct { dst, struc, fields } => I::Construct {
|
||||
dst: dst.var(p)?,
|
||||
struc: struc.struc(p)?.clone(),
|
||||
fields: fields
|
||||
.iter()
|
||||
.map(|(name, ident)| ident.var(p).map(|i| (name.clone(), i)))
|
||||
.collect::<Option<_>>()?,
|
||||
},
|
||||
I::If { cond, body } => I::If {
|
||||
cond: cond.var(p)?,
|
||||
body: body.iter().map(|i| i.resolve(p)).try_collect()?,
|
||||
},
|
||||
I::Loop { body } => I::Loop {
|
||||
body: body.iter().map(|i| i.resolve(p)).try_collect()?,
|
||||
},
|
||||
I::Break => I::Break,
|
||||
I::Continue => I::Continue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsmBlockArg {
|
||||
pub fn resolve(&self, p: &UProgram) -> Option<AsmBlockArg<VarID>> {
|
||||
Some(AsmBlockArg {
|
||||
var: self.var.var(p)?,
|
||||
reg: self.reg,
|
||||
ty: self.ty,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RV64Instruction {
|
||||
pub fn resolve(&self, p: &UProgram) -> Option<RV64Instruction<VarID>> {
|
||||
self.try_map(|i| {
|
||||
Some(match i {
|
||||
RegRef::Var(v) => RegRef::Var(v.var(p)?),
|
||||
RegRef::Reg(r) => RegRef::Reg(*r),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{DataID, FnID, IRLFunction};
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct Symbol(usize);
|
||||
/// intentionally does not have copy or clone;
|
||||
/// this should only be consumed once
|
||||
pub struct WritableSymbol(Symbol);
|
||||
|
||||
impl std::ops::Deref for WritableSymbol {
|
||||
type Target = Symbol;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SymbolSpace {
|
||||
ro_data: Vec<(Symbol, Vec<u8>)>,
|
||||
fns: Vec<(Symbol, IRLFunction)>,
|
||||
len: usize,
|
||||
labels: Vec<Option<String>>,
|
||||
}
|
||||
|
||||
pub struct SymbolSpaceBuilder {
|
||||
symbols: usize,
|
||||
unwritten_fns: Vec<(WritableSymbol, FnID)>,
|
||||
fn_map: HashMap<FnID, Symbol>,
|
||||
data_map: HashMap<DataID, Symbol>,
|
||||
ro_data: Vec<(Symbol, Vec<u8>)>,
|
||||
fns: Vec<(Symbol, IRLFunction)>,
|
||||
labels: Vec<Option<String>>,
|
||||
}
|
||||
|
||||
impl SymbolSpace {
|
||||
pub fn ro_data(&self) -> &[(Symbol, Vec<u8>)] {
|
||||
&self.ro_data
|
||||
}
|
||||
pub fn fns(&self) -> &[(Symbol, IRLFunction)] {
|
||||
&self.fns
|
||||
}
|
||||
pub fn labels(&self) -> &[Option<String>] {
|
||||
&self.labels
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolSpaceBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
symbols: 0,
|
||||
unwritten_fns: Vec::new(),
|
||||
fn_map: HashMap::new(),
|
||||
data_map: HashMap::new(),
|
||||
ro_data: Vec::new(),
|
||||
fns: Vec::new(),
|
||||
labels: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder {
|
||||
let mut s = Self::new();
|
||||
for e in entries {
|
||||
s.func(*e);
|
||||
}
|
||||
s
|
||||
}
|
||||
pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> {
|
||||
self.unwritten_fns.pop()
|
||||
}
|
||||
pub fn anon_ro_data(&mut self, data: &[u8], label: Option<String>) -> Symbol {
|
||||
let sym = self.reserve();
|
||||
self.write_ro_data(sym, data.to_vec(), label)
|
||||
}
|
||||
pub fn ro_data(&mut self, id: DataID, data: &[u8], label: Option<&str>) -> Symbol {
|
||||
match self.data_map.get(&id) {
|
||||
Some(s) => *s,
|
||||
None => {
|
||||
let sym = self.reserve();
|
||||
self.data_map.insert(id, *sym);
|
||||
self.write_ro_data(sym, data.to_vec(), label.map(|l| l.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn func(&mut self, id: FnID) -> Symbol {
|
||||
match self.fn_map.get(&id) {
|
||||
Some(s) => *s,
|
||||
None => {
|
||||
let wsym = self.reserve();
|
||||
let sym = *wsym;
|
||||
self.unwritten_fns.push((wsym, id));
|
||||
self.fn_map.insert(id, sym);
|
||||
sym
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn write_ro_data(
|
||||
&mut self,
|
||||
sym: WritableSymbol,
|
||||
data: Vec<u8>,
|
||||
name: Option<String>,
|
||||
) -> Symbol {
|
||||
self.ro_data.push((*sym, data));
|
||||
self.labels[sym.0 .0] = name;
|
||||
*sym
|
||||
}
|
||||
pub fn write_fn(
|
||||
&mut self,
|
||||
sym: WritableSymbol,
|
||||
func: IRLFunction,
|
||||
name: Option<String>,
|
||||
) -> Symbol {
|
||||
self.fns.push((*sym, func));
|
||||
self.labels[sym.0 .0] = name;
|
||||
*sym
|
||||
}
|
||||
pub fn reserve(&mut self) -> WritableSymbol {
|
||||
let val = self.symbols;
|
||||
self.symbols += 1;
|
||||
self.labels.push(None);
|
||||
WritableSymbol(Symbol(val))
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.symbols
|
||||
}
|
||||
pub fn finish(self) -> Option<SymbolSpace> {
|
||||
if self.unwritten_fns.is_empty() {
|
||||
Some(SymbolSpace {
|
||||
len: self.symbols,
|
||||
fns: self.fns,
|
||||
ro_data: self.ro_data,
|
||||
labels: self.labels,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "@{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Symbol {
|
||||
type Target = usize;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
//! the IR is split into 2 layers: upper and lower
|
||||
//! upper handles all of the main language features like types,
|
||||
//! and the lower is a very concrete format that can be easily
|
||||
//! translated to assembly and will probably also include
|
||||
//! the majority of optimization, but not sure
|
||||
|
||||
mod upper;
|
||||
mod lower;
|
||||
mod id;
|
||||
mod asm;
|
||||
pub mod arch;
|
||||
|
||||
pub use upper::*;
|
||||
pub use lower::*;
|
||||
pub use id::*;
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// a generic identifier for all (identifiable) kinds
|
||||
/// eg. a::b::c.d.e
|
||||
/// or a::Result<T,_>
|
||||
pub struct UIdent {
|
||||
pub status: IdentStatus,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
pub enum IdentStatus {
|
||||
Res(Res),
|
||||
// lets you do things like import and then specialize in multiple places
|
||||
// eg. import SomeStruct ...... f() -> SomeStruct // type ....... SomeStruct {} // struct
|
||||
// and then have correct errors like "expected struct, found type Bla"
|
||||
Ref(IdentID),
|
||||
Unres {
|
||||
base: ResBase,
|
||||
path: Vec<MemberIdent>,
|
||||
},
|
||||
Failed(Option<ResErr>),
|
||||
Cooked,
|
||||
}
|
||||
|
||||
pub struct MemberIdent {
|
||||
pub ty: MemberTy,
|
||||
pub name: String,
|
||||
pub gargs: Vec<TypeID>,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MemberTy {
|
||||
Member,
|
||||
Field,
|
||||
}
|
||||
|
||||
impl MemberTy {
|
||||
pub fn sep(&self) -> &'static str {
|
||||
match self {
|
||||
MemberTy::Member => "::",
|
||||
MemberTy::Field => ".",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MemberTy {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
MemberTy::Member => "member",
|
||||
MemberTy::Field => "field",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Res {
|
||||
Var(VarID),
|
||||
Fn(FnInst),
|
||||
Struct(StructInst),
|
||||
Type(TypeID),
|
||||
Generic(GenericID),
|
||||
Module(ModID),
|
||||
}
|
||||
|
||||
impl Res {
|
||||
pub fn kind(&self) -> KindTy {
|
||||
match self {
|
||||
Res::Var(..) => KindTy::Var,
|
||||
Res::Fn(..) => KindTy::Fn,
|
||||
Res::Struct(..) => KindTy::Struct,
|
||||
Res::Type(..) => KindTy::Type,
|
||||
Res::Module(..) => KindTy::Module,
|
||||
Res::Generic(..) => KindTy::Generic,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_str(&self, p: &UProgram) -> String {
|
||||
let name = match self {
|
||||
Res::Var(id) => &p.vars[id].name,
|
||||
Res::Fn(fi) => &p.fns[fi.id].name,
|
||||
Res::Struct(si) => &p.structs[si.id].name,
|
||||
Res::Type(id) => &p.type_name(id),
|
||||
Res::Generic(id) => &p.generics[id].name,
|
||||
Res::Module(id) => &p.modules[id].name,
|
||||
};
|
||||
format!("{} '{}'", self.kind(), name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ResBase {
|
||||
Unvalidated(MemRes),
|
||||
Validated(Res),
|
||||
}
|
||||
|
||||
impl ResBase {
|
||||
pub fn display_str(&self, p: &UProgram) -> String {
|
||||
match self {
|
||||
ResBase::Unvalidated(uv) => uv.display_str(p),
|
||||
ResBase::Validated(res) => res.display_str(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MemRes {
|
||||
pub mem: Member,
|
||||
pub origin: Origin,
|
||||
pub gargs: Vec<TypeID>,
|
||||
}
|
||||
|
||||
impl MemRes {
|
||||
pub fn display_str(&self, p: &UProgram) -> String {
|
||||
self.mem.id.display_str(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentID {
|
||||
pub fn var(&self, p: &UProgram) -> Option<VarID> {
|
||||
match p.idents[self].status {
|
||||
IdentStatus::Res(Res::Var(id)) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> {
|
||||
match &p.idents[self].status {
|
||||
IdentStatus::Res(Res::Fn(i)) => Some(&i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> {
|
||||
match &p.idents[self].status {
|
||||
IdentStatus::Res(Res::Struct(i)) => Some(&i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{arch::riscv64::RV64Instruction, *};
|
||||
use crate::compiler::arch::riscv::Reg;
|
||||
|
||||
pub trait ResStage {
|
||||
type Var;
|
||||
type Func;
|
||||
type Struct;
|
||||
type Type;
|
||||
}
|
||||
|
||||
pub struct Unresolved;
|
||||
impl ResStage for Unresolved {
|
||||
type Var = VarRes;
|
||||
type Func = IdentID;
|
||||
type Struct = IdentID;
|
||||
type Type = TypeRes;
|
||||
}
|
||||
|
||||
pub struct Resolved;
|
||||
impl ResStage for Resolved {
|
||||
type Var = VarID;
|
||||
type Func = FnInst;
|
||||
type Struct = StructInst;
|
||||
type Type = TypeID;
|
||||
}
|
||||
|
||||
pub enum UInstruction<S: ResStage = Unresolved> {
|
||||
Mv {
|
||||
dst: S::Var,
|
||||
src: S::Var,
|
||||
},
|
||||
Ref {
|
||||
dst: S::Var,
|
||||
src: S::Var,
|
||||
},
|
||||
Deref {
|
||||
dst: S::Var,
|
||||
src: S::Var,
|
||||
},
|
||||
LoadData {
|
||||
dst: S::Var,
|
||||
src: DataID,
|
||||
},
|
||||
LoadSlice {
|
||||
dst: S::Var,
|
||||
src: DataID,
|
||||
},
|
||||
Call {
|
||||
dst: S::Var,
|
||||
f: S::Func,
|
||||
args: Vec<S::Var>,
|
||||
},
|
||||
AsmBlock {
|
||||
instructions: Vec<RV64Instruction<S::Var>>,
|
||||
args: Vec<AsmBlockArg<S::Var>>,
|
||||
},
|
||||
Ret {
|
||||
src: S::Var,
|
||||
},
|
||||
Construct {
|
||||
dst: S::Var,
|
||||
struc: S::Struct,
|
||||
fields: HashMap<String, S::Var>,
|
||||
},
|
||||
If {
|
||||
cond: S::Var,
|
||||
body: Vec<InstrID>,
|
||||
},
|
||||
Loop {
|
||||
body: Vec<InstrID>,
|
||||
},
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
pub struct UInstrInst<S: ResStage = Unresolved> {
|
||||
pub i: UInstruction<S>,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsmBlockArg<V = IdentID> {
|
||||
pub var: V,
|
||||
pub reg: Reg,
|
||||
pub ty: AsmBlockArgType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AsmBlockArgType {
|
||||
In,
|
||||
Out,
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
//! all main IR Upper data structures stored in UProgram
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
common::FileSpan,
|
||||
ir::{Len, ID},
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
pub type NamePath = Vec<String>;
|
||||
|
||||
pub type FnID = ID<UFunc>;
|
||||
pub type VarID = ID<UVar>;
|
||||
pub type IdentID = ID<UIdent>;
|
||||
pub type TypeID = ID<Type>;
|
||||
pub type GenericID = ID<UGeneric>;
|
||||
pub type StructID = ID<UStruct>;
|
||||
pub type DataID = ID<UData>;
|
||||
pub type ModID = ID<UModule>;
|
||||
pub type InstrID = ID<UInstrInst>;
|
||||
|
||||
pub type VarRes = URes<VarID>;
|
||||
pub type TypeRes = URes<VarID>;
|
||||
|
||||
pub struct UFunc {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub args: Vec<VarID>,
|
||||
pub gargs: Vec<GenericID>,
|
||||
pub ret: TypeRes,
|
||||
pub instructions: Vec<InstrID>,
|
||||
}
|
||||
|
||||
pub struct StructField {
|
||||
pub ty: TypeRes,
|
||||
pub origin: Origin,
|
||||
// pub vis: Visibility
|
||||
}
|
||||
|
||||
pub struct UStruct {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub fields: HashMap<String, StructField>,
|
||||
pub gargs: Vec<GenericID>,
|
||||
}
|
||||
|
||||
pub struct UGeneric {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
pub struct UVar {
|
||||
pub name: String,
|
||||
pub origin: Origin,
|
||||
pub ty: TypeRes,
|
||||
pub parent: Option<VarID>,
|
||||
pub children: HashMap<String, VarID>,
|
||||
}
|
||||
|
||||
pub enum VarTy {
|
||||
Ident(IdentID),
|
||||
Res(TypeID),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub struct VarOffset {
|
||||
pub id: VarID,
|
||||
pub offset: Len,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UData {
|
||||
pub name: String,
|
||||
pub ty: TypeID,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UModule {
|
||||
pub name: String,
|
||||
pub members: HashMap<String, Member>,
|
||||
pub parent: Option<ModID>,
|
||||
pub func: FnID,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Member {
|
||||
pub id: MemberID,
|
||||
// pub visibility: Visibility
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MemberID {
|
||||
Fn(FnID),
|
||||
Struct(StructID),
|
||||
Var(VarID),
|
||||
Module(ModID),
|
||||
Type(TypeDef),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeDef {
|
||||
pub gargs: Vec<GenericID>,
|
||||
pub ty: TypeID,
|
||||
}
|
||||
|
||||
impl MemberID {
|
||||
pub fn kind(&self) -> KindTy {
|
||||
match self {
|
||||
MemberID::Fn(_) => KindTy::Fn,
|
||||
MemberID::Struct(_) => KindTy::Struct,
|
||||
MemberID::Var(_) => KindTy::Var,
|
||||
MemberID::Module(_) => KindTy::Module,
|
||||
MemberID::Type(_) => KindTy::Type,
|
||||
}
|
||||
}
|
||||
pub fn display_str(&self, p: &UProgram) -> String {
|
||||
let name = match self {
|
||||
MemberID::Var(id) => &p.vars[id].name,
|
||||
MemberID::Fn(id) => &p.fns[id].name,
|
||||
MemberID::Struct(id) => &p.structs[id].name,
|
||||
MemberID::Module(id) => &p.modules[id].name,
|
||||
MemberID::Type(def) => &p.type_name(def.ty),
|
||||
};
|
||||
format!("{} '{}'", self.kind(), name)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum URes<T> {
|
||||
Res(T),
|
||||
Unres(IdentID),
|
||||
}
|
||||
|
||||
pub type Origin = FileSpan;
|
||||
|
||||
// "effective" (externally visible) kinds
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum KindTy {
|
||||
Type,
|
||||
Var,
|
||||
Struct,
|
||||
Fn,
|
||||
Module,
|
||||
Generic,
|
||||
}
|
||||
|
||||
impl Display for KindTy {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
KindTy::Type => "type",
|
||||
KindTy::Var => "variable",
|
||||
KindTy::Fn => "function",
|
||||
KindTy::Struct => "struct",
|
||||
KindTy::Module => "module",
|
||||
KindTy::Generic => "generic",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
mod instr;
|
||||
mod kind;
|
||||
mod program;
|
||||
mod ty;
|
||||
mod resolve;
|
||||
mod error;
|
||||
mod ident;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub use instr::*;
|
||||
pub use kind::*;
|
||||
pub use program::*;
|
||||
pub use ty::*;
|
||||
pub use error::*;
|
||||
pub use resolve::*;
|
||||
pub use ident::*;
|
||||
@@ -1,178 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub struct UProgram {
|
||||
pub fns: Vec<UFunc>,
|
||||
pub structs: Vec<UStruct>,
|
||||
pub modules: Vec<UModule>,
|
||||
pub data: Vec<UData>,
|
||||
pub generics: Vec<UGeneric>,
|
||||
pub vars: Vec<UVar>,
|
||||
pub idents: Vec<UIdent>,
|
||||
pub types: Vec<Type>,
|
||||
pub instrs: Vec<UInstrInst>,
|
||||
|
||||
pub unres_idents: Vec<IdentID>,
|
||||
pub unres_instrs: Vec<(FnID, InstrID)>,
|
||||
pub tc: TypeCache,
|
||||
}
|
||||
|
||||
pub struct TypeCache {
|
||||
pub unit: TypeID,
|
||||
pub error: TypeID,
|
||||
}
|
||||
|
||||
impl UProgram {
|
||||
pub fn new() -> Self {
|
||||
let mut types = Vec::new();
|
||||
let tc = TypeCache {
|
||||
unit: push_id(&mut types, Type::Unit),
|
||||
error: push_id(&mut types, Type::Error),
|
||||
};
|
||||
Self {
|
||||
fns: Vec::new(),
|
||||
vars: Vec::new(),
|
||||
idents: Vec::new(),
|
||||
structs: Vec::new(),
|
||||
types: Vec::new(),
|
||||
generics: Vec::new(),
|
||||
data: Vec::new(),
|
||||
modules: Vec::new(),
|
||||
instrs: Vec::new(),
|
||||
unres_idents: Vec::new(),
|
||||
unres_instrs: Vec::new(),
|
||||
tc,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer(&mut self) -> TypeID {
|
||||
self.def_ty(Type::Infer)
|
||||
}
|
||||
|
||||
pub fn def_var(&mut self, v: UVar) -> VarID {
|
||||
push_id(&mut self.vars, v)
|
||||
}
|
||||
|
||||
pub fn def_fn(&mut self, f: UFunc) -> FnID {
|
||||
push_id(&mut self.fns, f)
|
||||
}
|
||||
|
||||
pub fn def_ty(&mut self, t: Type) -> TypeID {
|
||||
push_id(&mut self.types, t)
|
||||
}
|
||||
|
||||
pub fn def_ident(&mut self, i: UIdent) -> IdentID {
|
||||
let id = push_id(&mut self.idents, i);
|
||||
if let IdentStatus::Unres { .. } = self.idents[id].status {
|
||||
self.unres_idents.push(id);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
pub fn def_generic(&mut self, g: UGeneric) -> GenericID {
|
||||
push_id(&mut self.generics, g)
|
||||
}
|
||||
|
||||
pub fn def_data(&mut self, d: UData) -> DataID {
|
||||
push_id(&mut self.data, d)
|
||||
}
|
||||
|
||||
pub fn def_struct(&mut self, s: UStruct) -> StructID {
|
||||
push_id(&mut self.structs, s)
|
||||
}
|
||||
|
||||
pub fn def_module(&mut self, m: UModule) -> ModID {
|
||||
push_id(&mut self.modules, m)
|
||||
}
|
||||
|
||||
pub fn res_ty(&self, i: IdentID) -> Option<TypeID> {
|
||||
self.idents[i].status;
|
||||
}
|
||||
|
||||
pub fn type_name(&self, ty: impl Typed) -> String {
|
||||
match ty.ty(self) {
|
||||
Type::Struct(ty) => {
|
||||
format!(
|
||||
"{}{}",
|
||||
self.structs[ty.id].name,
|
||||
self.gparams_str(&ty.gargs)
|
||||
)
|
||||
}
|
||||
Type::FnInst(ty) => {
|
||||
format!(
|
||||
"fn{}({}) -> {}",
|
||||
&self.gparams_str(&ty.gargs),
|
||||
&self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)),
|
||||
&self.type_name(self.fns[ty.id].ret)
|
||||
)
|
||||
}
|
||||
Type::Ref(t) => format!("{}&", self.type_name(t)),
|
||||
Type::Bits(size) => format!("b{}", size),
|
||||
Type::Array(t, len) => format!("[{}; {len}]", self.type_name(t)),
|
||||
Type::Unit => "()".to_string(),
|
||||
Type::Slice(t) => format!("&[{}]", self.type_name(t)),
|
||||
Type::Infer => "{inferred}".to_string(),
|
||||
Type::Generic(id) => self.generics[id].name.clone(),
|
||||
Type::Deref(t) => format!("{}^", self.type_name(t)),
|
||||
Type::Error => "{error}".to_string(),
|
||||
Type::Ptr(id) => self.type_name(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_list_str(&self, mut args: impl Iterator<Item = TypeID>) -> String {
|
||||
let mut str = String::new();
|
||||
if let Some(arg) = args.next() {
|
||||
str += &self.type_name(arg);
|
||||
}
|
||||
for arg in args {
|
||||
str = str + ", " + &self.type_name(arg);
|
||||
}
|
||||
str
|
||||
}
|
||||
|
||||
pub fn gparams_str(&self, args: &[TypeID]) -> String {
|
||||
let mut str = String::new();
|
||||
if !args.is_empty() {
|
||||
str += "<";
|
||||
}
|
||||
str += &self.type_list_str(args.iter().cloned());
|
||||
if !args.is_empty() {
|
||||
str += ">";
|
||||
}
|
||||
str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
|
||||
let id = ID::new(v.len());
|
||||
v.push(t);
|
||||
id
|
||||
}
|
||||
|
||||
// I'm done with names...
|
||||
pub trait Typed {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type;
|
||||
}
|
||||
|
||||
impl Typed for &Type {
|
||||
fn ty(&self, _: &UProgram) -> &Type {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for TypeID {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
||||
&p.types[self]
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for &TypeID {
|
||||
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
|
||||
&p.types[*self]
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for &Box<Type> {
|
||||
fn ty<'a>(&'a self, _: &'a UProgram) -> &'a Type {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
use crate::common::{CompilerMsg, CompilerOutput};
|
||||
|
||||
use super::{
|
||||
IdentStatus, KindTy, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram,
|
||||
};
|
||||
|
||||
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
|
||||
for ident in &p.idents {
|
||||
match &ident.status {
|
||||
IdentStatus::Unres { path, base } => {
|
||||
let mem = path.last().unwrap();
|
||||
errs.push(ResErr::UnknownMember {
|
||||
ty: mem.ty,
|
||||
name: mem.name.clone(),
|
||||
origin: mem.origin,
|
||||
parent: base.clone(),
|
||||
})
|
||||
}
|
||||
IdentStatus::Failed(err) => {
|
||||
if let Some(err) = err {
|
||||
errs.push(err.clone())
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for err in errs {
|
||||
match err {
|
||||
ResErr::Type {
|
||||
dst,
|
||||
src,
|
||||
errs,
|
||||
origin,
|
||||
} => {
|
||||
let mut msg = type_assign_err(p, dst, src);
|
||||
for inner in errs {
|
||||
if inner.dst != dst && inner.src != src {
|
||||
msg.push_str("\n ");
|
||||
msg.push_str(&type_assign_err(p, inner.dst, inner.src));
|
||||
}
|
||||
}
|
||||
output.err(CompilerMsg::new(msg, origin));
|
||||
}
|
||||
ResErr::NotCallable { origin, ty } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Cannot call type '{}'", p.type_name(ty)),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::CannotDeref { origin, ty } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Cannot dereference type '{}'", p.type_name(ty)),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::CondType { origin, ty } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Condition types must be '64'; found '{}'", p.type_name(ty)),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::BadControlFlow { origin, op } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Cannot {} here (outside of loop)", op.str()),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::MissingField { origin, id, name } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!(
|
||||
"Missing field '{name}' in creation of struct '{}'",
|
||||
p.structs[id].name
|
||||
),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::UnknownStructField { origin, id, name } => {
|
||||
output.err(CompilerMsg::new(
|
||||
format!("Unknown field '{name}' in struct '{}'", p.structs[id].name),
|
||||
origin,
|
||||
));
|
||||
}
|
||||
ResErr::NoReturn { fid } => output.err(CompilerMsg::new(
|
||||
format!("Function must return a value"),
|
||||
p.fns[fid].origin,
|
||||
)),
|
||||
ResErr::GenericCount {
|
||||
origin,
|
||||
expected,
|
||||
found,
|
||||
} => output.err(CompilerMsg::new(
|
||||
if expected == 0 {
|
||||
format!("No generic arguments expected")
|
||||
} else {
|
||||
format!("Expected {expected} generic arguments, found {found}")
|
||||
},
|
||||
origin,
|
||||
)),
|
||||
ResErr::KindMismatch {
|
||||
origin,
|
||||
found,
|
||||
expected,
|
||||
} => output.err(CompilerMsg::new(
|
||||
format!("Expected {expected}, found {}", found.display_str(p)),
|
||||
origin,
|
||||
)),
|
||||
ResErr::UnknownMember {
|
||||
origin,
|
||||
ty,
|
||||
name,
|
||||
parent,
|
||||
} => output.err(CompilerMsg::new(
|
||||
format!("Unknown {ty} {name} of {}", parent.display_str(p)),
|
||||
origin,
|
||||
)),
|
||||
}
|
||||
}
|
||||
for var in &p.vars {
|
||||
if let Some(ty) = var.ty() {
|
||||
match &p.types[ty] {
|
||||
Type::Infer => output.err(CompilerMsg::new(
|
||||
format!("Type of {:?} cannot be inferred", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ResErr {
|
||||
UnknownMember {
|
||||
origin: Origin,
|
||||
ty: MemberTy,
|
||||
name: String,
|
||||
parent: ResBase,
|
||||
},
|
||||
KindMismatch {
|
||||
origin: Origin,
|
||||
expected: KindTy,
|
||||
found: Res,
|
||||
},
|
||||
GenericCount {
|
||||
origin: Origin,
|
||||
expected: usize,
|
||||
found: usize,
|
||||
},
|
||||
NotCallable {
|
||||
origin: Origin,
|
||||
ty: TypeID,
|
||||
},
|
||||
CannotDeref {
|
||||
origin: Origin,
|
||||
ty: TypeID,
|
||||
},
|
||||
CondType {
|
||||
origin: Origin,
|
||||
ty: TypeID,
|
||||
},
|
||||
NoReturn {
|
||||
fid: usize,
|
||||
},
|
||||
BadControlFlow {
|
||||
op: ControlFlowOp,
|
||||
origin: Origin,
|
||||
},
|
||||
MissingField {
|
||||
origin: Origin,
|
||||
id: StructID,
|
||||
name: String,
|
||||
},
|
||||
UnknownStructField {
|
||||
origin: Origin,
|
||||
id: StructID,
|
||||
name: String,
|
||||
},
|
||||
Type {
|
||||
dst: TypeID,
|
||||
src: TypeID,
|
||||
errs: Vec<TypeMismatch>,
|
||||
origin: Origin,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ControlFlowOp {
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl ControlFlowOp {
|
||||
pub fn str(&self) -> &'static str {
|
||||
match self {
|
||||
ControlFlowOp::Break => "break",
|
||||
ControlFlowOp::Continue => "continue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeMismatch {
|
||||
pub dst: TypeID,
|
||||
pub src: TypeID,
|
||||
}
|
||||
|
||||
pub fn type_assign_err(p: &UProgram, dst: TypeID, src: TypeID) -> String {
|
||||
format!(
|
||||
"Cannot assign type {} to {}",
|
||||
p.type_name(src),
|
||||
p.type_name(dst)
|
||||
)
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
impl UProgram {
|
||||
pub fn resolve_idents(&mut self, errs: &mut Vec<ResErr>) -> ResolveRes {
|
||||
let mut resolve_res = ResolveRes::Finished;
|
||||
'main: for i in std::mem::take(&mut self.unres_idents) {
|
||||
let mut j = i;
|
||||
// take from ref if possible
|
||||
while let IdentStatus::Ref(other) = &self.idents[j].status {
|
||||
match &self.idents[other].status {
|
||||
IdentStatus::Res(res) => self.idents[i].status = IdentStatus::Res(res.clone()),
|
||||
&IdentStatus::Ref(id) => j = id,
|
||||
IdentStatus::Unres { .. } => {
|
||||
self.unres_idents.push(i);
|
||||
continue 'main;
|
||||
}
|
||||
IdentStatus::Failed(..) => self.idents[i].status = IdentStatus::Cooked,
|
||||
IdentStatus::Cooked => self.idents[i].status = IdentStatus::Cooked,
|
||||
}
|
||||
}
|
||||
let status = &mut self.idents[i].status;
|
||||
// TOOD: there are some clones here that shouldn't be needed
|
||||
let IdentStatus::Unres { path, base } = status else {
|
||||
continue;
|
||||
};
|
||||
|
||||
while let Some(mem) = path.pop() {
|
||||
let res = match base {
|
||||
ResBase::Unvalidated(u) => {
|
||||
match u.validate(
|
||||
&self.fns,
|
||||
&self.structs,
|
||||
&self.generics,
|
||||
&mut self.types,
|
||||
errs,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
*status = IdentStatus::Failed(err);
|
||||
continue 'main;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResBase::Validated(res) => res.clone(),
|
||||
};
|
||||
*base = match (res, mem.ty) {
|
||||
(Res::Module(id), MemberTy::Member) => {
|
||||
let Some(m) = self.modules[id].members.get(&mem.name) else {
|
||||
self.unres_idents.push(i);
|
||||
continue 'main;
|
||||
};
|
||||
ResBase::Unvalidated(MemRes {
|
||||
mem: m.clone(),
|
||||
origin: mem.origin,
|
||||
gargs: mem.gargs,
|
||||
})
|
||||
}
|
||||
(Res::Var(id), MemberTy::Field) => {
|
||||
// trait resolution here
|
||||
let Some(&child) = self.vars[id].children.get(&mem.name) else {
|
||||
self.unres_idents.push(i);
|
||||
continue 'main;
|
||||
};
|
||||
ResBase::Unvalidated(MemRes {
|
||||
mem: Member {
|
||||
id: MemberID::Var(child),
|
||||
},
|
||||
origin: mem.origin,
|
||||
gargs: mem.gargs,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
*status = IdentStatus::Failed(Some(ResErr::UnknownMember {
|
||||
origin: mem.origin,
|
||||
ty: mem.ty,
|
||||
name: mem.name.clone(),
|
||||
parent: base.clone(),
|
||||
}));
|
||||
continue 'main;
|
||||
}
|
||||
};
|
||||
}
|
||||
let res = match base {
|
||||
ResBase::Unvalidated(u) => {
|
||||
match u.validate(
|
||||
&self.fns,
|
||||
&self.structs,
|
||||
&self.generics,
|
||||
&mut self.types,
|
||||
errs,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
*status = IdentStatus::Failed(err);
|
||||
continue 'main;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResBase::Validated(res) => res.clone(),
|
||||
};
|
||||
*status = IdentStatus::Res(res);
|
||||
resolve_res = ResolveRes::Unfinished;
|
||||
}
|
||||
resolve_res
|
||||
}
|
||||
}
|
||||
|
||||
impl MemRes {
|
||||
pub fn validate(
|
||||
&self,
|
||||
fns: &[UFunc],
|
||||
structs: &[UStruct],
|
||||
generics: &[UGeneric],
|
||||
types: &mut Vec<Type>,
|
||||
errs: &mut Vec<ResErr>,
|
||||
) -> Result<Res, Option<ResErr>> {
|
||||
let no_gargs = || {
|
||||
if self.gargs.len() > 0 {
|
||||
Err(ResErr::GenericCount {
|
||||
origin: self.origin,
|
||||
expected: 0,
|
||||
found: self.gargs.len(),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
Ok(match &self.mem.id {
|
||||
&MemberID::Fn(id) => {
|
||||
validate_gargs(
|
||||
&fns[id].gargs,
|
||||
&self.gargs,
|
||||
generics,
|
||||
types,
|
||||
errs,
|
||||
self.origin,
|
||||
)?;
|
||||
Res::Fn(FnInst {
|
||||
id,
|
||||
gargs: self.gargs.clone(),
|
||||
})
|
||||
}
|
||||
&MemberID::Struct(id) => {
|
||||
validate_gargs(
|
||||
&structs[id].gargs,
|
||||
&self.gargs,
|
||||
generics,
|
||||
types,
|
||||
errs,
|
||||
self.origin,
|
||||
)?;
|
||||
Res::Struct(StructInst {
|
||||
id,
|
||||
gargs: self.gargs.clone(),
|
||||
})
|
||||
}
|
||||
&MemberID::Var(id) => {
|
||||
no_gargs()?;
|
||||
Res::Var(id)
|
||||
}
|
||||
&MemberID::Module(id) => {
|
||||
no_gargs()?;
|
||||
Res::Module(id)
|
||||
}
|
||||
MemberID::Type(def) => {
|
||||
validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?;
|
||||
inst_typedef(def, &self.gargs, types);
|
||||
Res::Type(def.ty)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_gargs(
|
||||
dst: &[GenericID],
|
||||
src: &[TypeID],
|
||||
generics: &[UGeneric],
|
||||
types: &[Type],
|
||||
errs: &mut Vec<ResErr>,
|
||||
origin: Origin,
|
||||
) -> Result<(), Option<ResErr>> {
|
||||
if dst.len() != src.len() {
|
||||
return Err(Some(ResErr::GenericCount {
|
||||
origin,
|
||||
expected: dst.len(),
|
||||
found: src.len(),
|
||||
}));
|
||||
}
|
||||
for (dst, src) in dst.iter().zip(src.iter()) {
|
||||
let g = &generics[dst];
|
||||
let t = &types[src];
|
||||
// TODO: validate trait constraints
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn inst_fn_var(
|
||||
fi: FnInst,
|
||||
fns: &[UFunc],
|
||||
origin: Origin,
|
||||
vars: &mut Vec<UVar>,
|
||||
types: &mut Vec<Type>,
|
||||
) -> VarID {
|
||||
let name = fns[fi.id].name.clone();
|
||||
let ty = push_id(types, Type::FnInst(fi));
|
||||
push_id(
|
||||
vars,
|
||||
UVar {
|
||||
name,
|
||||
origin,
|
||||
ty: VarTy::Res(ty),
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn inst_struct_var(
|
||||
si: StructInst,
|
||||
structs: &[UStruct],
|
||||
origin: Origin,
|
||||
vars: &mut Vec<UVar>,
|
||||
types: &mut Vec<Type>,
|
||||
) -> VarID {
|
||||
let name = structs[si.id].name.clone();
|
||||
let ty = push_id(types, Type::Struct(si));
|
||||
let id = push_id(
|
||||
vars,
|
||||
UVar {
|
||||
name,
|
||||
origin,
|
||||
ty: VarTy::Res(ty),
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
},
|
||||
);
|
||||
id
|
||||
}
|
||||
|
||||
/// gargs assumed to be valid
|
||||
pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec<Type>) -> TypeID {
|
||||
let gmap = inst_gmap(&def.gargs, &gargs);
|
||||
inst_type(def.ty, types, &gmap)
|
||||
}
|
||||
|
||||
pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap<GenericID, TypeID> {
|
||||
let mut gmap = HashMap::new();
|
||||
for (&gid, &tid) in dst.iter().zip(src) {
|
||||
gmap.insert(gid, tid);
|
||||
}
|
||||
gmap
|
||||
}
|
||||
|
||||
pub fn inst_type(id: TypeID, types: &mut Vec<Type>, gmap: &HashMap<GenericID, TypeID>) -> TypeID {
|
||||
if gmap.len() == 0 {
|
||||
return id;
|
||||
}
|
||||
match inst_type_(id, types, gmap) {
|
||||
Some(new) => new,
|
||||
None => id,
|
||||
}
|
||||
}
|
||||
|
||||
fn inst_type_(
|
||||
id: TypeID,
|
||||
types: &mut Vec<Type>,
|
||||
gmap: &HashMap<GenericID, TypeID>,
|
||||
) -> Option<TypeID> {
|
||||
let ty = match types[id].clone() {
|
||||
Type::Bits(_) => return None,
|
||||
Type::Struct(struct_ty) => Type::Struct(StructInst {
|
||||
id: struct_ty.id,
|
||||
gargs: inst_all(&struct_ty.gargs, types, gmap)?,
|
||||
}),
|
||||
Type::FnInst(fn_ty) => Type::FnInst(FnInst {
|
||||
id: fn_ty.id,
|
||||
gargs: inst_all(&fn_ty.gargs, types, gmap)?,
|
||||
}),
|
||||
Type::Ref(id) => Type::Ref(inst_type_(id, types, gmap)?),
|
||||
Type::Slice(id) => Type::Slice(inst_type_(id, types, gmap)?),
|
||||
Type::Array(id, len) => Type::Array(inst_type_(id, types, gmap)?, len),
|
||||
Type::Unit => return None,
|
||||
Type::Generic(gid) => return gmap.get(&gid).map(|id| Some(*id)).unwrap_or_else(|| None),
|
||||
Type::Infer => Type::Infer,
|
||||
Type::Deref(id) => Type::Deref(inst_type_(id, types, gmap)?),
|
||||
Type::Ptr(id) => Type::Ptr(inst_type_(id, types, gmap)?),
|
||||
Type::Error => return None,
|
||||
};
|
||||
Some(push_id(types, ty))
|
||||
}
|
||||
|
||||
fn inst_all(
|
||||
ids: &[TypeID],
|
||||
types: &mut Vec<Type>,
|
||||
gmap: &HashMap<GenericID, TypeID>,
|
||||
) -> Option<Vec<TypeID>> {
|
||||
let mut vec = None;
|
||||
for (i, &id) in ids.iter().enumerate() {
|
||||
if let Some(id) = inst_type_(id, types, gmap) {
|
||||
vec.get_or_insert_with(|| ids.iter().take(i).cloned().collect::<Vec<_>>())
|
||||
.push(id);
|
||||
} else if let Some(vec) = &mut vec {
|
||||
vec.push(id)
|
||||
}
|
||||
}
|
||||
vec
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub enum UResEvent {
|
||||
VarUse(VarID),
|
||||
}
|
||||
|
||||
impl UProgram {
|
||||
pub fn resolve_instrs(&mut self, errs: &mut Vec<ResErr>) -> ResolveRes {
|
||||
let mut data = ResData {
|
||||
changed: false,
|
||||
types: &mut self.types,
|
||||
s: Sources {
|
||||
idents: &mut self.idents,
|
||||
vars: &mut self.vars,
|
||||
fns: &self.fns,
|
||||
structs: &self.structs,
|
||||
generics: &self.generics,
|
||||
data: &self.data,
|
||||
modules: &self.modules,
|
||||
},
|
||||
errs,
|
||||
};
|
||||
for ids in std::mem::take(&mut self.unres_instrs) {
|
||||
if let ResolveRes::Unfinished = resolve_instr(ids, &mut self.instrs, &mut data) {
|
||||
self.unres_instrs.push(ids);
|
||||
};
|
||||
}
|
||||
ResolveRes::Finished
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ResolveCtx {
|
||||
ret: IdentID,
|
||||
breakable: bool,
|
||||
i: InstrID,
|
||||
}
|
||||
|
||||
pub fn resolve_instr<'a>(
|
||||
(fi, ii): (FnID, InstrID),
|
||||
instrs: &mut Vec<UInstrInst>,
|
||||
data: &mut ResData<'a>,
|
||||
) -> ResolveRes {
|
||||
let instr = &mut instrs[ii];
|
||||
match &mut instr.i {
|
||||
UInstruction::Call { dst, f, args } => {
|
||||
let fi = data.res::<UFunc>(*f);
|
||||
for &a in args {
|
||||
data.res::<UVar>(a);
|
||||
}
|
||||
data.res::<UVar>(dst);
|
||||
match fi {
|
||||
Ok(fi) => {
|
||||
let f = &data.s.fns[fi.id];
|
||||
for (&src, &dst) in args.iter().zip(&f.args) {
|
||||
data.s.constraints.push(UResEvent::AssignVVI { dst, src });
|
||||
}
|
||||
}
|
||||
Err(r) => return r,
|
||||
}
|
||||
ResolveRes::Finished
|
||||
}
|
||||
UInstruction::Mv { dst, src } => {
|
||||
res |= data.match_types::<UVar, UVar>(dst, src, src);
|
||||
}
|
||||
UInstruction::Ref { dst, src } => {
|
||||
let dstty = &data.types[data.res_var_ty(dst)?];
|
||||
let &Type::Ref(dest_ty) = dstty else {
|
||||
compiler_error()
|
||||
};
|
||||
res |= data.match_types::<Type, UVar>(dest_ty, src, src);
|
||||
}
|
||||
UInstruction::Deref { dst, src } => {
|
||||
let srcid = data.res_var_ty(src)?;
|
||||
let &Type::Ref(src_ty) = data.types[srcid] else {
|
||||
let origin = src.origin(data);
|
||||
data.errs.push(ResErr::CannotDeref { origin, ty: srcid });
|
||||
return None;
|
||||
};
|
||||
res |= data.match_types::<UVar, Type>(dst, src_ty, src);
|
||||
}
|
||||
UInstruction::LoadData { dst, src } => {
|
||||
let srcid = src.type_id(&data.s);
|
||||
res |= data.match_types::<UVar, Type>(dst, srcid, dst);
|
||||
}
|
||||
UInstruction::LoadSlice { dst, src } => {
|
||||
let (dstty, dstid) = data.res_var_ty(dst, ctx)?;
|
||||
let &Type::Slice(dstty) = dstty else {
|
||||
compiler_error()
|
||||
};
|
||||
let srcid = src.type_id(&data.s);
|
||||
let Type::Array(srcty, _) = data.types[srcid] else {
|
||||
compiler_error()
|
||||
};
|
||||
res |= data.match_types(dstty, srcty, dst);
|
||||
}
|
||||
UInstruction::AsmBlock { instructions, args } => {
|
||||
// TODO
|
||||
}
|
||||
UInstruction::Ret { src } => {
|
||||
res |= data.match_types::<Type, UVar>(ctx.ret, src, src);
|
||||
}
|
||||
UInstruction::Construct { dst, struc, fields } => {
|
||||
let si = data.res::<UStruct>(dst, ctx)?;
|
||||
let sid = si.id;
|
||||
let st = &data.s.structs[sid];
|
||||
let mut used = HashSet::new();
|
||||
for (name, field) in &st.fields {
|
||||
if let Some(src) = fields.get(name) {
|
||||
used.insert(name);
|
||||
res |= data.match_types::<Type, UVar>(field.ty, src, src);
|
||||
} else {
|
||||
let origin = dst.origin(data);
|
||||
data.errs.push(ResErr::MissingField {
|
||||
origin,
|
||||
id: sid,
|
||||
name: name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
for (name, _) in fields {
|
||||
if !used.contains(name) {
|
||||
let origin = dst.origin(data);
|
||||
data.errs.push(ResErr::UnknownStructField {
|
||||
origin,
|
||||
id: sid,
|
||||
name: name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
UInstruction::If { cond, body } => {
|
||||
if let Some(ty) = data.res_var_ty(cond, ctx) {
|
||||
if !matches!(ty.0, RType::Bits(64)) {
|
||||
let id = ty.1;
|
||||
let origin = cond.origin(data);
|
||||
data.errs.push(ResErr::CondType { origin, ty: id });
|
||||
}
|
||||
}
|
||||
for i in body {
|
||||
resolve_instr(
|
||||
data,
|
||||
ResolveCtx {
|
||||
ret: ctx.ret,
|
||||
breakable: ctx.breakable,
|
||||
i,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
UInstruction::Loop { body } => {
|
||||
for i in body {
|
||||
resolve_instr(
|
||||
data,
|
||||
ResolveCtx {
|
||||
ret: ctx.ret,
|
||||
breakable: true,
|
||||
i,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
UInstruction::Break => {
|
||||
if !ctx.breakable {
|
||||
data.errs.push(ResErr::BadControlFlow {
|
||||
op: ControlFlowOp::Break,
|
||||
origin: ctx.i.origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
UInstruction::Continue => {
|
||||
if !ctx.breakable {
|
||||
data.errs.push(ResErr::BadControlFlow {
|
||||
op: ControlFlowOp::Continue,
|
||||
origin: ctx.i.origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub fn match_types(data: &mut ResData, dst: TypeID, src: TypeID) -> MatchRes {
|
||||
let Some(dst) = clean_type(data.types, dst) else {
|
||||
return MatchRes::Finished;
|
||||
};
|
||||
let Some(src) = clean_type(data.types, src) else {
|
||||
return MatchRes::Finished;
|
||||
};
|
||||
// prevents this from blowing up I think:
|
||||
// let mut x, y;
|
||||
// x = y;
|
||||
// y = x;
|
||||
if dst == src {
|
||||
return MatchRes::Finished;
|
||||
}
|
||||
let error = || MatchRes::Error(vec![TypeMismatch { dst, src }]);
|
||||
match (data.types[dst].clone(), data.types[src].clone()) {
|
||||
// prefer changing dst over src
|
||||
(Type::Infer, _) => {
|
||||
data.changed = true;
|
||||
data.types[dst] = Type::Ptr(src);
|
||||
MatchRes::Finished
|
||||
}
|
||||
(_, Type::Infer) => {
|
||||
data.changed = true;
|
||||
data.types[src] = Type::Ptr(dst);
|
||||
MatchRes::Finished
|
||||
}
|
||||
(Type::Struct(dest), Type::Struct(src)) => {
|
||||
if dest.id != src.id {
|
||||
return error();
|
||||
}
|
||||
match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned())
|
||||
}
|
||||
// (
|
||||
// Type::Fn {
|
||||
// args: dst_args,
|
||||
// ret: dst_ret,
|
||||
// },
|
||||
// Type::Fn {
|
||||
// args: src_args,
|
||||
// ret: src_ret,
|
||||
// },
|
||||
// ) => {
|
||||
// let dst = dst_args.into_iter().chain(once(dst_ret));
|
||||
// let src = src_args.into_iter().chain(once(src_ret));
|
||||
// match_all(data, dst, src)
|
||||
// }
|
||||
(Type::Ref(dest), Type::Ref(src)) => match_types(data, dest, src),
|
||||
(Type::Slice(dest), Type::Slice(src)) => match_types(data, dest, src),
|
||||
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
|
||||
if dlen == slen {
|
||||
match_types(data, dest, src)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
}
|
||||
_ => error(),
|
||||
}
|
||||
}
|
||||
|
||||
fn match_all(
|
||||
data: &mut ResData,
|
||||
dst: impl Iterator<Item = TypeID>,
|
||||
src: impl Iterator<Item = TypeID>,
|
||||
) -> MatchRes {
|
||||
let mut finished = true;
|
||||
let mut errors = Vec::new();
|
||||
for (dst, src) in dst.zip(src) {
|
||||
match match_types(data, dst, src) {
|
||||
MatchRes::Unfinished => finished = false,
|
||||
MatchRes::Error(errs) => errors.extend(errs),
|
||||
MatchRes::Finished => (),
|
||||
}
|
||||
}
|
||||
if finished {
|
||||
if errors.is_empty() {
|
||||
MatchRes::Finished
|
||||
} else {
|
||||
MatchRes::Error(errors)
|
||||
}
|
||||
} else {
|
||||
MatchRes::Unfinished
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ResData<'a> {
|
||||
pub fn match_types(
|
||||
&mut self,
|
||||
dst: impl MaybeTypeID,
|
||||
src: impl MaybeTypeID,
|
||||
origin: impl HasOrigin,
|
||||
) -> ResolveRes {
|
||||
let dst = dst.type_id(&self.s)?;
|
||||
let src = src.type_id(&self.s)?;
|
||||
let res = match_types(self, dst, src);
|
||||
match res {
|
||||
MatchRes::Unfinished => ResolveRes::Unfinished,
|
||||
MatchRes::Finished => ResolveRes::Finished,
|
||||
MatchRes::Error(es) => {
|
||||
self.errs.push(ResErr::Type {
|
||||
errs: es,
|
||||
origin: origin.origin(self),
|
||||
dst,
|
||||
src,
|
||||
});
|
||||
ResolveRes::Finished
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MatchRes {
|
||||
Unfinished,
|
||||
Finished,
|
||||
Error(Vec<TypeMismatch>),
|
||||
}
|
||||
|
||||
impl FromResidual<Result<Infallible, MatchRes>> for MatchRes {
|
||||
fn from_residual(residual: Result<Infallible, MatchRes>) -> Self {
|
||||
match residual {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MaybeTypeID {
|
||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes>;
|
||||
}
|
||||
|
||||
impl<T: TypeIDed> MaybeTypeID for T {
|
||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes> {
|
||||
Ok(self.type_id(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeTypeID for VarID {
|
||||
fn type_id(&self, s: &Sources) -> Result<TypeID, ResolveRes> {
|
||||
match s.vars[self].ty {
|
||||
VarTy::Ident(id) => todo!(),
|
||||
VarTy::Res(id) => Ok(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
common::CompilerOutput,
|
||||
ir::{MemRes, Member},
|
||||
};
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{BitOrAssign, FromResidual},
|
||||
};
|
||||
|
||||
mod error;
|
||||
mod ident;
|
||||
mod instantiate;
|
||||
mod instr;
|
||||
mod matc;
|
||||
|
||||
pub use error::*;
|
||||
use instantiate::*;
|
||||
|
||||
impl UProgram {
|
||||
pub fn resolve(&mut self, output: &mut CompilerOutput) {
|
||||
self.unres_instrs = (0..self.instrs.len()).map(|i| InstrID::from(i)).collect();
|
||||
let mut res = ResolveRes::Unfinished;
|
||||
let mut errs = Vec::new();
|
||||
while res == ResolveRes::Unfinished {
|
||||
res = ResolveRes::Finished;
|
||||
res |= self.resolve_idents(&mut errs);
|
||||
res |= self.resolve_instrs(&mut errs);
|
||||
}
|
||||
for (fid, f) in self.fns.iter().enumerate() {
|
||||
// this currently works bc expressions create temporary variables
|
||||
// although you can't do things like loop {return 3} (need to analyze control flow)
|
||||
if let Some(ty) = self.res_ty(f.ret)
|
||||
&& self.types[ty] != Type::Unit
|
||||
&& f.instructions
|
||||
.last()
|
||||
.is_none_or(|i| !matches!(self.instrs[i].i, UInstruction::Ret { .. }))
|
||||
{
|
||||
errs.push(ResErr::NoReturn { fid });
|
||||
}
|
||||
}
|
||||
report_errs(self, output, errs);
|
||||
}
|
||||
}
|
||||
|
||||
fn compiler_error() -> ! {
|
||||
// TODO: this is probably a compiler error / should never happen
|
||||
panic!("how could this happen to me (you)");
|
||||
}
|
||||
|
||||
struct Sources<'a> {
|
||||
idents: &'a mut [UIdent],
|
||||
vars: &'a mut Vec<UVar>,
|
||||
fns: &'a [UFunc],
|
||||
structs: &'a [UStruct],
|
||||
generics: &'a [UGeneric],
|
||||
data: &'a [UData],
|
||||
modules: &'a [UModule],
|
||||
}
|
||||
|
||||
struct ResData<'a> {
|
||||
changed: bool,
|
||||
types: &'a mut Vec<Type>,
|
||||
s: Sources<'a>,
|
||||
errs: &'a mut Vec<ResErr>,
|
||||
}
|
||||
|
||||
impl<'a> ResData<'a> {
|
||||
pub fn res<K: ResKind>(&mut self, i: IdentID) -> Result<K::Res, ResolveRes> {
|
||||
i.res_as::<K>(&mut self.s, &mut self.types)
|
||||
}
|
||||
|
||||
pub fn res_ty(&mut self, x: impl Resolvable<Type>) -> Result<TypeID, ResolveRes> {
|
||||
let id = Resolvable::<Type>::try_res(&x, &mut self.s, self.types, self.errs)?;
|
||||
resolved_type(self.types, id)
|
||||
}
|
||||
|
||||
pub fn res_var_ty(&mut self, i: IdentID) -> Result<TypeID, ResolveRes> {
|
||||
let id = self.res::<UVar>(i)?;
|
||||
let id = match self.s.vars[id].ty {
|
||||
VarTy::Res(t) => Ok(t),
|
||||
VarTy::Ident(i) => i.res_as::<Type>(&mut self.s, self.types),
|
||||
}?;
|
||||
resolved_type(self.types, id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ResolveRes {
|
||||
Finished,
|
||||
Unfinished,
|
||||
}
|
||||
|
||||
impl BitOrAssign for ResolveRes {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
match rhs {
|
||||
ResolveRes::Finished => (),
|
||||
ResolveRes::Unfinished => *self = ResolveRes::Unfinished,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromResidual<Option<Infallible>> for ResolveRes {
|
||||
fn from_residual(_: Option<Infallible>) -> Self {
|
||||
Self::Unfinished
|
||||
}
|
||||
}
|
||||
|
||||
trait Resolvable<K: ResKind> {
|
||||
fn try_res(
|
||||
&self,
|
||||
s: &mut Sources,
|
||||
types: &mut Vec<Type>,
|
||||
errs: &mut Vec<ResErr>,
|
||||
) -> Result<K::Res, ResolveRes>;
|
||||
}
|
||||
|
||||
impl IdentID {
|
||||
fn res_as<K: ResKind>(
|
||||
&self,
|
||||
s: &mut Sources,
|
||||
types: &mut Vec<Type>,
|
||||
) -> Result<K::Res, ResolveRes> {
|
||||
let origin = s.idents[self].origin;
|
||||
let res = match &s.idents[self].status {
|
||||
IdentStatus::Res(res) => res.clone(),
|
||||
IdentStatus::Ref { .. } => return Err(ResolveRes::Unfinished),
|
||||
IdentStatus::Unres { .. } => return Err(ResolveRes::Unfinished),
|
||||
IdentStatus::Failed(..) => return Err(ResolveRes::Finished),
|
||||
IdentStatus::Cooked => return Err(ResolveRes::Finished),
|
||||
};
|
||||
match K::from_res(res, types, s, origin) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(res) => {
|
||||
s.idents[self].status = IdentStatus::Failed(Some(ResErr::KindMismatch {
|
||||
origin,
|
||||
expected: K::ty(),
|
||||
found: res,
|
||||
}));
|
||||
Err(ResolveRes::Finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: ResKind> Resolvable<K> for &IdentID {
|
||||
fn try_res(
|
||||
&self,
|
||||
s: &mut Sources,
|
||||
types: &mut Vec<Type>,
|
||||
errs: &mut Vec<ResErr>,
|
||||
) -> Result<K::Res, ResolveRes> {
|
||||
Resolvable::<K>::try_res(*self, s, types, errs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<UVar> for VarID {
|
||||
fn try_res(
|
||||
&self,
|
||||
s: &mut Sources,
|
||||
types: &mut Vec<Type>,
|
||||
errs: &mut Vec<ResErr>,
|
||||
) -> Result<<UVar as ResKind>::Res, ResolveRes> {
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Type> for TypeID {
|
||||
fn try_res(
|
||||
&self,
|
||||
s: &mut Sources,
|
||||
types: &mut Vec<Type>,
|
||||
errs: &mut Vec<ResErr>,
|
||||
) -> Result<<Type as ResKind>::Res, ResolveRes> {
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResKind {
|
||||
type Res;
|
||||
fn ty() -> KindTy;
|
||||
fn from_res(
|
||||
res: Res,
|
||||
types: &mut Vec<Type>,
|
||||
s: &mut Sources,
|
||||
origin: Origin,
|
||||
) -> Result<Self::Res, Res>;
|
||||
}
|
||||
|
||||
impl ResKind for UFunc {
|
||||
type Res = FnInst;
|
||||
fn ty() -> KindTy {
|
||||
KindTy::Fn
|
||||
}
|
||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
||||
match res {
|
||||
Res::Fn(fi) => Ok(fi),
|
||||
_ => Err(res),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResKind for UVar {
|
||||
type Res = VarID;
|
||||
fn ty() -> KindTy {
|
||||
KindTy::Var
|
||||
}
|
||||
fn from_res(
|
||||
res: Res,
|
||||
types: &mut Vec<Type>,
|
||||
s: &mut Sources,
|
||||
origin: Origin,
|
||||
) -> Result<Self::Res, Res> {
|
||||
Ok(match res {
|
||||
Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types),
|
||||
Res::Var(id) => id,
|
||||
_ => return Err(res),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ResKind for UStruct {
|
||||
type Res = StructInst;
|
||||
fn ty() -> KindTy {
|
||||
KindTy::Struct
|
||||
}
|
||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
||||
match res {
|
||||
Res::Struct(si) => Ok(si),
|
||||
_ => Err(res),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResKind for Type {
|
||||
type Res = TypeID;
|
||||
fn ty() -> KindTy {
|
||||
KindTy::Type
|
||||
}
|
||||
fn from_res(
|
||||
res: Res,
|
||||
types: &mut Vec<Type>,
|
||||
s: &mut Sources,
|
||||
_: Origin,
|
||||
) -> Result<Self::Res, Res> {
|
||||
Ok(match res {
|
||||
Res::Struct(si) => push_id(types, Type::Struct(si)),
|
||||
Res::Type(id) => id,
|
||||
_ => return Err(res),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypeIDed {
|
||||
fn type_id(&self, s: &Sources) -> TypeID;
|
||||
}
|
||||
|
||||
impl TypeIDed for TypeID {
|
||||
fn type_id(&self, _: &Sources) -> TypeID {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeIDed for DataID {
|
||||
fn type_id(&self, s: &Sources) -> TypeID {
|
||||
s.data[self].ty
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeIDed> TypeIDed for &T {
|
||||
fn type_id(&self, s: &Sources) -> TypeID {
|
||||
(*self).type_id(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromResidual<Result<Infallible, ResolveRes>> for ResolveRes {
|
||||
fn from_residual(residual: Result<Infallible, ResolveRes>) -> Self {
|
||||
match residual {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait HasOrigin {
|
||||
fn origin(&self, data: &ResData) -> Origin;
|
||||
}
|
||||
|
||||
impl HasOrigin for &IdentID {
|
||||
fn origin(&self, data: &ResData) -> Origin {
|
||||
data.s.idents[*self].origin
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use super::{FnID, GenericID, Len, ResolveRes, StructID, TypeID, UProgram, VarID};
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct FieldRef {
|
||||
pub parent: VarID,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct StructInst {
|
||||
pub id: StructID,
|
||||
/// assumed to be valid
|
||||
pub gargs: Vec<TypeID>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct FnInst {
|
||||
pub id: FnID,
|
||||
/// assumed to be valid
|
||||
pub gargs: Vec<TypeID>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Type {
|
||||
Bits(u32),
|
||||
Struct(StructInst),
|
||||
// this can be added for constraints later (F: fn(...) -> ...)
|
||||
// Fn { args: Vec<TypeID>, ret: TypeID },
|
||||
// "fake" types
|
||||
FnInst(FnInst),
|
||||
Ref(TypeID),
|
||||
Slice(TypeID),
|
||||
Array(TypeID, Len),
|
||||
Unit,
|
||||
Infer,
|
||||
Generic(GenericID),
|
||||
Deref(TypeID),
|
||||
Ptr(TypeID),
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn rf(self, p: &mut UProgram) -> Self {
|
||||
p.def_ty(self).rf()
|
||||
}
|
||||
pub fn derf(self, p: &mut UProgram) -> Self {
|
||||
p.def_ty(self).derf()
|
||||
}
|
||||
pub fn arr(self, p: &mut UProgram, len: Len) -> Self {
|
||||
p.def_ty(self).arr(len)
|
||||
}
|
||||
pub fn slice(self, p: &mut UProgram) -> Self {
|
||||
p.def_ty(self).slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeID {
|
||||
pub fn rf(self) -> Type {
|
||||
Type::Ref(self)
|
||||
}
|
||||
pub fn derf(self) -> Type {
|
||||
Type::Deref(self)
|
||||
}
|
||||
pub fn arr(self, len: Len) -> Type {
|
||||
Type::Array(self, len)
|
||||
}
|
||||
pub fn slice(self) -> Type {
|
||||
Type::Slice(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn bx(self) -> Box<Self> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean_type(types: &[Type], id: TypeID) -> Option<TypeID> {
|
||||
match &types[id] {
|
||||
&Type::Ptr(id) => clean_type(types, id),
|
||||
&Type::Deref(did) => match &types[clean_type(types, did)?] {
|
||||
&Type::Ref(id) => clean_type(types, id),
|
||||
_ => Some(id),
|
||||
},
|
||||
Type::Error => None,
|
||||
_ => Some(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolved_type(types: &[Type], id: TypeID) -> Result<TypeID, ResolveRes> {
|
||||
match &types[id] {
|
||||
&Type::Ptr(id) => resolved_type(types, id),
|
||||
&Type::Deref(id) => match &types[resolved_type(types, id)?] {
|
||||
&Type::Ref(id) => resolved_type(types, id),
|
||||
Type::Infer => Err(ResolveRes::Unfinished),
|
||||
_ => Err(ResolveRes::Finished),
|
||||
},
|
||||
Type::Error => Err(ResolveRes::Finished),
|
||||
_ => Ok(id),
|
||||
}
|
||||
}
|
||||
+10
-171
@@ -1,178 +1,17 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(iterator_try_collect)]
|
||||
// dawg what
|
||||
#![feature(str_as_str)]
|
||||
|
||||
pub const FILE_EXT: &str = "lang";
|
||||
|
||||
use common::{CompilerOutput, SrcFile};
|
||||
use ir::{LProgram, UProgram};
|
||||
use parser::{Import, Imports, PModule, ParserCtx};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::{create_dir_all, OpenOptions},
|
||||
io::stdout,
|
||||
os::unix::fs::OpenOptionsExt,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
mod common;
|
||||
mod compiler;
|
||||
mod ir;
|
||||
mod parser;
|
||||
mod util;
|
||||
|
||||
fn main() {
|
||||
let file = std::env::args_os().nth(1);
|
||||
// TODO: professional arg parsing
|
||||
let gdb = std::env::args().nth(2).is_some_and(|a| a == "--debug");
|
||||
let asm = std::env::args().nth(2).is_some_and(|a| a == "--asm");
|
||||
if let Some(path) = file {
|
||||
let path = PathBuf::from(path);
|
||||
run_file(&path, gdb, asm);
|
||||
} else {
|
||||
run_stdin();
|
||||
}
|
||||
}
|
||||
|
||||
impl UProgram {
|
||||
pub fn from_path(path: &Path) -> (Self, CompilerOutput) {
|
||||
let parent = path.parent().expect("bruh");
|
||||
let mut program = Self::new();
|
||||
let mut output = CompilerOutput::new();
|
||||
|
||||
let mut imports = Imports::new();
|
||||
imports.insert(Import(vec![path
|
||||
.file_name()
|
||||
.expect("bruh")
|
||||
.to_str()
|
||||
.expect("bruh")
|
||||
.to_string()]));
|
||||
let mut imported = HashSet::new();
|
||||
let mut fid = 0;
|
||||
|
||||
while !imports.is_empty() {
|
||||
let iter = std::mem::take(&mut imports);
|
||||
for i in iter {
|
||||
let import_path = &i.0;
|
||||
if imported.contains(&i) {
|
||||
continue;
|
||||
}
|
||||
let mut file_path = parent.to_path_buf();
|
||||
file_path.extend(import_path);
|
||||
file_path.set_extension(FILE_EXT);
|
||||
let text = std::fs::read_to_string(&file_path).expect("failed to read file");
|
||||
output.file_map.insert(
|
||||
fid,
|
||||
SrcFile {
|
||||
path: file_path,
|
||||
text: text.clone(),
|
||||
},
|
||||
);
|
||||
let mut ctx = ParserCtx::new(fid, text.as_str(), &mut output);
|
||||
fid += 1;
|
||||
let res = PModule::parse(&mut ctx);
|
||||
// println!("Parsed:");
|
||||
// println!("{:#?}", res.node);
|
||||
res.lower(import_path.clone(), &mut program, &mut imports, &mut output);
|
||||
imported.insert(i);
|
||||
}
|
||||
}
|
||||
(program, output)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_file(path: &Path, gdb: bool, asm: bool) {
|
||||
let (mut program, mut output) = UProgram::from_path(path);
|
||||
program.resolve(&mut output);
|
||||
// println!("vars:");
|
||||
// for (id, def) in program.iter_vars() {
|
||||
// println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty));
|
||||
// }
|
||||
// for (id, f) in program.iter_fns() {
|
||||
// println!("{}:{id:?} = {:#?}", program.names.path(id), f);
|
||||
// }
|
||||
if !output.errs.is_empty() {
|
||||
output.write_to(&mut stdout());
|
||||
let mut args = std::env::args();
|
||||
let Some(path) = args.nth(1) else {
|
||||
println!("file expected");
|
||||
return;
|
||||
}
|
||||
let program = LProgram::create(&program).expect("morir");
|
||||
let unlinked = compiler::compile(&program);
|
||||
if asm {
|
||||
println!("{:?}", unlinked);
|
||||
} else {
|
||||
let bin = unlinked.link().to_elf();
|
||||
println!("compiled");
|
||||
save_run(&bin, gdb);
|
||||
}
|
||||
output.write_to(&mut stdout());
|
||||
}
|
||||
|
||||
fn save_run(binary: &[u8], run_gdb: bool) {
|
||||
use std::io::prelude::*;
|
||||
let dir = Path::new("./build");
|
||||
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
||||
let name = Path::new("test");
|
||||
let path = dir.join(name);
|
||||
let path = path.as_os_str();
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.mode(0o750)
|
||||
.open(path)
|
||||
.expect("Failed to create file");
|
||||
file.write_all(binary).expect("Failed to write to file");
|
||||
file.sync_all().expect("Failed to sync file");
|
||||
println!("running...");
|
||||
let mut p = Command::new("qemu-riscv64");
|
||||
let proc = if run_gdb {
|
||||
p.arg("-g").arg("1234").arg(path).spawn()
|
||||
} else {
|
||||
p.arg(path).spawn()
|
||||
};
|
||||
if let Ok(mut process) = proc {
|
||||
if run_gdb {
|
||||
match Command::new("gdb")
|
||||
.arg("-q")
|
||||
.arg("-ex")
|
||||
.arg("target remote :1234")
|
||||
.arg(path)
|
||||
.spawn()
|
||||
{
|
||||
Ok(mut gdb) => {
|
||||
gdb.wait().expect("xd");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("gdb error: {e:?}");
|
||||
process.kill().expect("uh oh");
|
||||
}
|
||||
}
|
||||
let code = match std::fs::read_to_string(path) {
|
||||
Ok(code) => code,
|
||||
Err(err) => {
|
||||
println!("Failed to read input file: {err}");
|
||||
return;
|
||||
}
|
||||
if let Ok(status) = process.wait() {
|
||||
if let Some(code) = status.code() {
|
||||
std::process::exit(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_stdin() {
|
||||
println!("todo");
|
||||
// for line in BufReader::new(std::io::stdin()).lines() {
|
||||
// let str = &line.expect("failed to read line");
|
||||
// let mut ctx = ParserCtx::from(&str[..]);
|
||||
// if let Some(expr) = PStatement::parse_node(&mut ctx).node.as_ref() {
|
||||
// if ctx.next().is_none() {
|
||||
// println!("{:?}", expr);
|
||||
// } else {
|
||||
// println!("uhhhh ehehe");
|
||||
// }
|
||||
// }
|
||||
// ctx.output.write_for(&mut stdout(), str);
|
||||
// }
|
||||
};
|
||||
parser::parse(&code);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
use crate::parser::error::ParseError;
|
||||
pub use span::*;
|
||||
use std::iter::Peekable;
|
||||
pub use token::*;
|
||||
|
||||
mod span;
|
||||
mod token;
|
||||
|
||||
pub struct Cursor<'a> {
|
||||
pub span: Span,
|
||||
tokens: Peekable<Tokens<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
pub fn new(text: &'a str) -> Self {
|
||||
Self {
|
||||
span: Span { first: 0, last: 0 },
|
||||
tokens: Tokens::new(text).peekable(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Token> {
|
||||
self.tokens.next().map(|inst| {
|
||||
self.span = inst.span;
|
||||
inst.inner
|
||||
})
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> Option<&Token> {
|
||||
self.tokens.peek().map(|inst| &inst.inner)
|
||||
}
|
||||
|
||||
pub fn expect_next(&mut self) -> Result<Token, ParseError> {
|
||||
self.next().ok_or_else(|| ParseError {
|
||||
spans: Vec::new(),
|
||||
msg: "unexpected end of file".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expect(&mut self, token: Token) -> Result<Token, ParseError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == token {
|
||||
Ok(next)
|
||||
} else {
|
||||
self.unexpected(next, &format!("{token:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_ident(&mut self) -> Result<String, ParseError> {
|
||||
let next = self.expect_next()?;
|
||||
if let Token::Ident(s) = next {
|
||||
Ok(s)
|
||||
} else {
|
||||
self.unexpected(next, "identifier")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unexpected<T>(&self, token: Token, expected: &str) -> Result<T, ParseError> {
|
||||
Err(ParseError::unexpected_token(
|
||||
Spanned {
|
||||
inner: token,
|
||||
span: self.span,
|
||||
},
|
||||
expected,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn peek_first(&mut self) -> usize {
|
||||
self.tokens.peek().map(|i| i.span.first).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn cur_last(&mut self) -> usize {
|
||||
self.span.last
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Span {
|
||||
pub first: usize,
|
||||
pub last: usize,
|
||||
}
|
||||
|
||||
pub struct Spanned<T> {
|
||||
pub inner: T,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for Spanned<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for Spanned<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
use super::{Span, Spanned};
|
||||
use std::{iter::Peekable, str::CharIndices};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Token {
|
||||
// symbols
|
||||
Equal,
|
||||
Colon,
|
||||
Semicolon,
|
||||
OpenCurly,
|
||||
CloseCurly,
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
Dash,
|
||||
Plus,
|
||||
Asterisk,
|
||||
Slash,
|
||||
// keywords
|
||||
Let,
|
||||
Fn,
|
||||
// other
|
||||
Ident(String),
|
||||
Lit(Lit),
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Lit {
|
||||
Number(String),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl From<Lit> for Token {
|
||||
fn from(value: Lit) -> Self {
|
||||
Self::Lit(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub type TokenInst = Spanned<Token>;
|
||||
|
||||
pub struct Tokens<'a> {
|
||||
text: Peekable<CharIndices<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Tokens<'a> {
|
||||
pub fn new(code: &'a str) -> Self {
|
||||
Self {
|
||||
text: code.char_indices().peekable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Tokens<'_> {
|
||||
type Item = Spanned<Token>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (i, c) = self.text.next()?;
|
||||
let mut span = Span { first: i, last: i };
|
||||
Some(Spanned {
|
||||
inner: match c {
|
||||
'=' => Token::Equal,
|
||||
'{' => Token::OpenCurly,
|
||||
'}' => Token::CloseCurly,
|
||||
'(' => Token::OpenParen,
|
||||
')' => Token::CloseParen,
|
||||
':' => Token::Colon,
|
||||
';' => Token::Semicolon,
|
||||
'-' => Token::Dash,
|
||||
'+' => Token::Plus,
|
||||
'*' => Token::Asterisk,
|
||||
'/' => Token::Slash,
|
||||
'\n' | ' ' => {
|
||||
return self.next();
|
||||
}
|
||||
'0'..='9' => {
|
||||
let mut s = c.to_string();
|
||||
while let Some((i, c)) = self.text.peek()
|
||||
&& c.is_alphanumeric()
|
||||
{
|
||||
s.push(*c);
|
||||
span.last = *i;
|
||||
self.text.next();
|
||||
}
|
||||
Lit::Number(s).into()
|
||||
}
|
||||
'"' => {
|
||||
let mut s = c.to_string();
|
||||
while let Some((i, c)) = self.text.peek()
|
||||
&& !matches!(c, '"')
|
||||
{
|
||||
s.push(*c);
|
||||
span.last = *i;
|
||||
self.text.next();
|
||||
}
|
||||
self.text.next();
|
||||
Lit::String(s).into()
|
||||
}
|
||||
_ => {
|
||||
let mut s = c.to_string();
|
||||
while let Some((i, c)) = self.text.peek()
|
||||
&& !matches!(
|
||||
c,
|
||||
'=' | '{' | '}' | '(' | ')' | ':' | ';' | '\n' | '"' | ' '
|
||||
)
|
||||
{
|
||||
s.push(*c);
|
||||
span.last = *i;
|
||||
self.text.next();
|
||||
}
|
||||
match s.as_str() {
|
||||
"let" => Token::Let,
|
||||
"fn" => Token::Fn,
|
||||
"true" => Lit::Bool(true).into(),
|
||||
"false" => Lit::Bool(false).into(),
|
||||
_ => Token::Ident(s),
|
||||
}
|
||||
}
|
||||
},
|
||||
span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Equal => write!(f, "="),
|
||||
Token::Colon => write!(f, ":"),
|
||||
Token::Plus => write!(f, "+"),
|
||||
Token::Dash => write!(f, "-"),
|
||||
Token::Asterisk => write!(f, "*"),
|
||||
Token::Slash => write!(f, "/"),
|
||||
Token::Semicolon => write!(f, ";"),
|
||||
Token::OpenCurly => write!(f, "{{"),
|
||||
Token::CloseCurly => write!(f, "}}"),
|
||||
Token::OpenParen => write!(f, "("),
|
||||
Token::CloseParen => write!(f, ")"),
|
||||
Token::Let => write!(f, "let"),
|
||||
Token::Fn => write!(f, "fn"),
|
||||
Token::Ident(s) => write!(f, "{s}"),
|
||||
Token::Lit(lit) => write!(f, "{lit}"),
|
||||
}?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Lit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Lit::Number(n) => write!(f, "{n}"),
|
||||
Lit::Bool(b) => write!(f, "{b}"),
|
||||
Lit::String(s) => write!(f, "{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{self}'")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use crate::parser::cursor::{Span, TokenInst};
|
||||
|
||||
pub struct ParseError {
|
||||
pub spans: Vec<Span>,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn unexpected_token(inst: TokenInst, expected: &str) -> Self {
|
||||
Self {
|
||||
spans: vec![inst.span],
|
||||
msg: format!("Unexpected token {}; expected {expected}", inst.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
// mod v1;
|
||||
// mod v2;
|
||||
mod v3;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod node;
|
||||
|
||||
pub use v3::*;
|
||||
pub fn parse(code: &str) {}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::parser::{
|
||||
cursor::{Cursor, Span, Token},
|
||||
error::ParseError,
|
||||
};
|
||||
|
||||
pub trait Parsable: Sized {
|
||||
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError>;
|
||||
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self>;
|
||||
}
|
||||
|
||||
pub struct ParseCtx<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
nodes: Nodes,
|
||||
}
|
||||
|
||||
pub struct Nodes {
|
||||
statements: NodeVec<Statement>,
|
||||
exprs: NodeVec<Expr>,
|
||||
idents: NodeVec<Ident>,
|
||||
}
|
||||
|
||||
pub struct NodeVec<N> {
|
||||
vec: Vec<N>,
|
||||
spans: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<N> NodeVec<N> {
|
||||
pub fn add(&mut self, v: N, span: Span) -> Id<N> {
|
||||
let id = self.vec.len();
|
||||
self.vec.push(v);
|
||||
self.spans.push(span);
|
||||
Id {
|
||||
id,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Let(Id<Ident>, Id<Expr>),
|
||||
}
|
||||
|
||||
pub enum Expr {
|
||||
Ident(Id<Ident>),
|
||||
Negate(Id<Expr>),
|
||||
Assign(Id<Expr>, Id<Expr>),
|
||||
}
|
||||
|
||||
pub struct Id<T> {
|
||||
id: usize,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct Ident {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl Parsable for Expr {
|
||||
fn parse(ctx: &mut ParseCtx) -> Result<Self, ParseError> {
|
||||
Ok(match ctx.cursor.expect_next()? {
|
||||
Token::Dash => Self::Negate(ctx.parse()?),
|
||||
Token::Ident(s) => Self::Ident(ctx.ident(s)),
|
||||
other => return ctx.cursor.unexpected(other, "an expression"),
|
||||
})
|
||||
}
|
||||
|
||||
fn vec(nodes: &mut Nodes) -> &mut NodeVec<Self> {
|
||||
&mut nodes.exprs
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseCtx<'_> {
|
||||
pub fn parse<P: Parsable>(&mut self) -> Result<Id<P>, ParseError> {
|
||||
let first = self.cursor.peek_first();
|
||||
P::parse(self).map(|r| {
|
||||
let last = self.cursor.cur_last();
|
||||
P::vec(&mut self.nodes).add(r, Span { first, last })
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ident(&mut self, s: String) -> Id<Ident> {
|
||||
let span = self.cursor.span;
|
||||
self.nodes.idents.add(Ident { inner: s }, span)
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
use super::error::ParserError;
|
||||
use super::token::{CharCursor, Keyword, Symbol, Token, TokenInstance};
|
||||
use super::FilePos;
|
||||
|
||||
pub struct TokenCursor<'a> {
|
||||
cursor: CharCursor<'a>,
|
||||
next: Option<TokenInstance>,
|
||||
next_pos: FilePos,
|
||||
prev_end: FilePos,
|
||||
}
|
||||
|
||||
impl<'a> TokenCursor<'a> {
|
||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
||||
self.prev_end = self.cursor.prev_pos();
|
||||
self.next_pos = self.cursor.next_pos();
|
||||
std::mem::replace(&mut self.next, TokenInstance::parse(&mut self.cursor))
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<TokenInstance, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())?;
|
||||
Ok(self.next().unwrap())
|
||||
}
|
||||
pub fn expect_token(&mut self, t: Token) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if t == next.token {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::unexpected_token(&next, &format!("{t:?}")))
|
||||
}
|
||||
}
|
||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), ParserError> {
|
||||
self.expect_token(Token::Symbol(symbol))
|
||||
}
|
||||
pub fn seek_sym(&mut self, symbol: Symbol) {
|
||||
while self
|
||||
.next()
|
||||
.is_some_and(|n| n.token != Token::Symbol(symbol))
|
||||
{}
|
||||
}
|
||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
||||
while self
|
||||
.peek()
|
||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
||||
{
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
||||
loop {
|
||||
if f(self.peek()?) {
|
||||
return self.peek();
|
||||
}
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), ParserError> {
|
||||
self.expect_token(Token::Keyword(kw))
|
||||
}
|
||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
||||
self.next.as_ref()
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
||||
&mut self.cursor
|
||||
}
|
||||
pub fn prev_end(&self) -> FilePos {
|
||||
self.prev_end
|
||||
}
|
||||
pub fn next_pos(&self) -> FilePos {
|
||||
self.next_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for TokenCursor<'a> {
|
||||
fn from(string: &'a str) -> Self {
|
||||
Self::from(CharCursor::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
||||
let cur = TokenInstance::parse(&mut cursor);
|
||||
Self {
|
||||
cursor,
|
||||
next: cur,
|
||||
next_pos: FilePos::start(),
|
||||
prev_end: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
use super::{
|
||||
token::{FileSpan, TokenInstance},
|
||||
FilePos,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParserError {
|
||||
pub msg: String,
|
||||
pub spans: Vec<FileSpan>,
|
||||
}
|
||||
|
||||
pub struct ParserErrors {
|
||||
pub errs: Vec<ParserError>,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
||||
ParserError {
|
||||
msg,
|
||||
spans: instances.iter().map(|i| i.span).collect(),
|
||||
}
|
||||
}
|
||||
pub fn from_msg(msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
spans: vec![FileSpan::at(pos)],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("unexpected end of input".to_string())
|
||||
}
|
||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
||||
let t = &inst.token;
|
||||
ParserError::from_instances(
|
||||
&[inst],
|
||||
format!("unexpected token {t:?}; expected {expected}"),
|
||||
)
|
||||
}
|
||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
||||
let after = if self.spans.is_empty() { "" } else { ":" };
|
||||
writeln!(writer, "error: {}{}", self.msg, after)?;
|
||||
for span in &self.spans {
|
||||
span.write_for(writer, file)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserErrors {
|
||||
pub fn new() -> Self {
|
||||
Self { errs: Vec::new() }
|
||||
}
|
||||
pub fn add(&mut self, err: ParserError) {
|
||||
self.errs.push(err);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
use std::io::{stdout, BufRead, BufReader};
|
||||
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod node;
|
||||
mod nodes;
|
||||
mod parse;
|
||||
mod token;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
pub use parse::*;
|
||||
use token::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
let mut errors = ParserErrors::new();
|
||||
let res = Module::parse_node(&mut TokenCursor::from(file), &mut errors);
|
||||
println!("{:?}", res.node);
|
||||
if errors.errs.is_empty() {
|
||||
let module = res.node.resolve().expect("what");
|
||||
}
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(out, file).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_stdin() {
|
||||
for line in BufReader::new(std::io::stdin()).lines() {
|
||||
let mut errors = ParserErrors::new();
|
||||
let str = &line.expect("failed to read line");
|
||||
let mut cursor = TokenCursor::from(&str[..]);
|
||||
if let Ok(expr) = Statement::parse_node(&mut cursor, &mut errors).node.as_ref() {
|
||||
if cursor.next().is_none() {
|
||||
println!("{:?}", expr);
|
||||
} else {
|
||||
println!("uhhhh ehehe");
|
||||
}
|
||||
}
|
||||
let out = &mut stdout();
|
||||
for err in errors.errs {
|
||||
err.write_for(out, str).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::FileSpan;
|
||||
|
||||
pub trait MaybeResolved {
|
||||
type Inner<T>;
|
||||
}
|
||||
|
||||
pub struct Resolved;
|
||||
impl MaybeResolved for Resolved {
|
||||
type Inner<T> = T;
|
||||
}
|
||||
|
||||
pub struct Unresolved;
|
||||
impl MaybeResolved for Unresolved {
|
||||
type Inner<T> = Result<T, ()>;
|
||||
}
|
||||
|
||||
pub struct Node<T, R: MaybeResolved> {
|
||||
pub inner: <R as MaybeResolved>::Inner<T>,
|
||||
pub span: FileSpan,
|
||||
}
|
||||
|
||||
impl<T> Node<T, Unresolved> {
|
||||
pub fn new(inner: T, span: FileSpan) -> Self {
|
||||
Self {
|
||||
inner: Ok(inner),
|
||||
span,
|
||||
}
|
||||
}
|
||||
pub fn bx(self) -> Node<Box<T>, Unresolved> {
|
||||
Node {
|
||||
inner: self.inner.map(|v| Box::new(v)),
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: MaybeResolved> Deref for Node<T, R> {
|
||||
type Target = <R as MaybeResolved>::Inner<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: MaybeResolved> DerefMut for Node<T, R> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T, Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.inner {
|
||||
Ok(v) => v.fmt(f),
|
||||
Err(_) => f.write_str("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T, Resolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Resolvable<Res> {
|
||||
fn resolve(self) -> Result<Res, ()>;
|
||||
}
|
||||
|
||||
impl<T: Resolvable<Res>, Res> Resolvable<Node<Res, Resolved>> for Node<T, Unresolved> {
|
||||
fn resolve(self) -> Result<Node<Res, Resolved>, ()> {
|
||||
if let Ok(inner) = self.inner {
|
||||
return Ok(Node {
|
||||
inner: inner.resolve()?,
|
||||
span: self.span,
|
||||
});
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Resolvable<Res>, Res> Resolvable<Box<Res>> for Box<T> {
|
||||
fn resolve(self) -> Result<Box<Res>, ()> {
|
||||
Ok(Box::new((*self).resolve()?))
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
token::Symbol, MaybeResolved, Node, NodeParsable, Parsable, ParseResult, ParserError,
|
||||
ParserErrors, Resolvable, Resolved, Statement, TokenCursor, Unresolved,
|
||||
};
|
||||
use crate::util::Padder;
|
||||
|
||||
pub struct Body<R: MaybeResolved> {
|
||||
pub statements: Vec<Node<Statement<R>, R>>,
|
||||
}
|
||||
|
||||
impl Parsable for Body<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let mut statements = Vec::new();
|
||||
cursor.expect_sym(Symbol::OpenCurly)?;
|
||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseCurly) {
|
||||
cursor.next();
|
||||
return ParseResult::Ok(Self { statements });
|
||||
}
|
||||
let mut expect_semi = false;
|
||||
let mut recover = false;
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
recover = true;
|
||||
errors.add(ParserError::unexpected_end());
|
||||
break;
|
||||
};
|
||||
if next.is_symbol(Symbol::CloseCurly) {
|
||||
cursor.next();
|
||||
break;
|
||||
}
|
||||
if next.is_symbol(Symbol::Semicolon) {
|
||||
cursor.next();
|
||||
expect_semi = false;
|
||||
continue;
|
||||
} else if expect_semi {
|
||||
errors.add(ParserError {
|
||||
msg: "expected ';'".to_string(),
|
||||
spans: vec![cursor.next_pos().char_span()],
|
||||
});
|
||||
}
|
||||
let res = Statement::parse_node(cursor, errors);
|
||||
statements.push(res.node);
|
||||
expect_semi = true;
|
||||
if res.recover {
|
||||
cursor.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
|
||||
if cursor.peek().is_none() {
|
||||
recover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseResult::from_recover(Self { statements }, recover)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Body<Resolved>> for Body<Unresolved> {
|
||||
fn resolve(self) -> Result<Body<Resolved>, ()> {
|
||||
Ok(Body {
|
||||
statements: self
|
||||
.statements
|
||||
.into_iter()
|
||||
.map(|s| s.resolve())
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Body<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.statements.first().is_some() {
|
||||
f.write_str("{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
for s in &self.statements {
|
||||
// they don't expose wrap_buf :grief:
|
||||
padder.write_str(&format!("{s:?}\n"))?;
|
||||
}
|
||||
f.write_char('}')?;
|
||||
} else {
|
||||
f.write_str("{}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
BinaryOperator, Body, Ident, Literal, MaybeResolved, Node, NodeParsable, Parsable, ParseResult,
|
||||
ParserError, ParserErrors, Resolvable, Resolved, Symbol, TokenCursor, UnaryOperator,
|
||||
Unresolved,
|
||||
};
|
||||
|
||||
type BoxNode<R> = Node<Box<Expr<R>>, R>;
|
||||
|
||||
pub enum Expr<R: MaybeResolved> {
|
||||
Lit(Node<Literal, R>),
|
||||
Ident(Node<Ident, R>),
|
||||
BinaryOp(BinaryOperator, BoxNode<R>, BoxNode<R>),
|
||||
UnaryOp(UnaryOperator, BoxNode<R>),
|
||||
Block(Node<Body<R>, R>),
|
||||
Call(BoxNode<R>, Vec<Node<Expr<R>, R>>),
|
||||
Group(BoxNode<R>),
|
||||
}
|
||||
|
||||
impl Parsable for Expr<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let start = cursor.next_pos();
|
||||
let next = cursor.expect_peek()?;
|
||||
let mut e1 = if next.is_symbol(Symbol::OpenParen) {
|
||||
cursor.next();
|
||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||
cursor.next();
|
||||
return ParseResult::Ok(Expr::Lit(Node::new(
|
||||
Literal::Unit,
|
||||
cursor.next_pos().char_span(),
|
||||
)));
|
||||
}
|
||||
let res = Node::parse(cursor, errors);
|
||||
if res.recover {
|
||||
cursor.seek_sym(Symbol::CloseParen);
|
||||
}
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
Self::Group(res.node.bx())
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
Self::Block(Body::parse_node(cursor, errors)?)
|
||||
} else if let Some(op) = UnaryOperator::from_token(next) {
|
||||
cursor.next();
|
||||
return Node::parse(cursor, errors).map(|n| {
|
||||
let n = n.bx();
|
||||
if let Ok(box Self::BinaryOp(op2, n1, n2)) = n.inner {
|
||||
let span = start.to(n1.span.end);
|
||||
Self::BinaryOp(op2, Node::new(Self::UnaryOp(op, n1), span).bx(), n2)
|
||||
} else {
|
||||
Self::UnaryOp(op, n)
|
||||
}
|
||||
});
|
||||
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
||||
Self::Lit(val)
|
||||
} else {
|
||||
let res = Node::parse(cursor, &mut ParserErrors::new());
|
||||
if res.node.is_ok() {
|
||||
Self::Ident(res.node)
|
||||
} else {
|
||||
let next = cursor.expect_peek()?;
|
||||
return ParseResult::Err(ParserError::unexpected_token(next, "an expression"));
|
||||
}
|
||||
};
|
||||
let Some(mut next) = cursor.peek() else {
|
||||
return ParseResult::Ok(e1);
|
||||
};
|
||||
while next.is_symbol(Symbol::OpenParen) {
|
||||
cursor.next();
|
||||
let mut args = Vec::new();
|
||||
loop {
|
||||
let next = cursor.expect_peek()?;
|
||||
if next.is_symbol(Symbol::CloseParen) {
|
||||
break;
|
||||
}
|
||||
let res = Node::<Expr<Unresolved>, Unresolved>::parse(cursor, errors);
|
||||
args.push(res.node);
|
||||
if res.recover {
|
||||
cursor.seek_syms(&[Symbol::CloseParen, Symbol::Comma]);
|
||||
}
|
||||
let next = cursor.expect_peek()?;
|
||||
if !next.is_symbol(Symbol::Comma) {
|
||||
break;
|
||||
}
|
||||
cursor.next();
|
||||
}
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
let end = cursor.prev_end();
|
||||
e1 = Self::Call(Node::new(Box::new(e1), start.to(end)), args);
|
||||
let Some(next2) = cursor.peek() else {
|
||||
return ParseResult::Ok(e1);
|
||||
};
|
||||
next = next2
|
||||
}
|
||||
let end = cursor.prev_end();
|
||||
let mut recover = false;
|
||||
let res = if let Some(mut op) = BinaryOperator::from_token(&next.token) {
|
||||
cursor.next();
|
||||
let mut n1 = Node::new(e1, start.to(end)).bx();
|
||||
let res = Node::parse(cursor, errors);
|
||||
let mut n2 = res.node.bx();
|
||||
recover = res.recover;
|
||||
if let Ok(box Self::BinaryOp(op2, _, _)) = n2.as_ref() {
|
||||
if op.presedence() > op2.presedence() {
|
||||
let Ok(box Self::BinaryOp(op2, n21, n22)) = n2.inner else {
|
||||
unreachable!();
|
||||
};
|
||||
let end = n21.span.end;
|
||||
n1 = Node::new(Self::BinaryOp(op, n1, n21), start.to(end)).bx();
|
||||
op = op2;
|
||||
n2 = n22;
|
||||
}
|
||||
}
|
||||
Self::BinaryOp(op, n1, n2)
|
||||
} else {
|
||||
e1
|
||||
};
|
||||
ParseResult::from_recover(res, recover)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Expr<Resolved>> for Expr<Unresolved> {
|
||||
fn resolve(self) -> Result<Expr<Resolved>, ()> {
|
||||
Ok(match self {
|
||||
Expr::Lit(l) => Expr::Lit(l.resolve()?),
|
||||
Expr::Ident(n) => Expr::Ident(n.resolve()?),
|
||||
Expr::BinaryOp(o, e1, e2) => Expr::BinaryOp(o, e1.resolve()?, e2.resolve()?),
|
||||
Expr::UnaryOp(o, e) => Expr::UnaryOp(o, e.resolve()?),
|
||||
Expr::Block(b) => Expr::Block(b.resolve()?),
|
||||
Expr::Call(f, args) => Expr::Call(
|
||||
f.resolve()?,
|
||||
args.into_iter()
|
||||
.map(|arg| arg.resolve())
|
||||
.collect::<Result<_, ()>>()?,
|
||||
),
|
||||
Expr::Group(e) => Expr::Group(e.resolve()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expr::Lit(c) => c.fmt(f)?,
|
||||
Expr::Ident(n) => n.fmt(f)?,
|
||||
Expr::Block(b) => b.fmt(f)?,
|
||||
Expr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
Expr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
Expr::UnaryOp(op, e) => {
|
||||
write!(f, "(")?;
|
||||
write!(f, "{}", op.str())?;
|
||||
write!(f, "{:?})", *e)?;
|
||||
}
|
||||
Expr::Group(inner) => inner.fmt(f)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
use super::{
|
||||
Body, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable,
|
||||
Resolved, Symbol, TokenCursor, Unresolved,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct Function<R: MaybeResolved> {
|
||||
pub name: Node<Ident, R>,
|
||||
pub body: Node<Body<R>, R>,
|
||||
}
|
||||
|
||||
impl Parsable for Function<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
cursor.expect_kw(Keyword::Fn)?;
|
||||
let name = Node::parse(cursor, errors)?;
|
||||
cursor.expect_sym(Symbol::OpenParen)?;
|
||||
cursor.expect_sym(Symbol::CloseParen)?;
|
||||
Node::parse(cursor, errors).map(|body| Self { name, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Function<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
self.name.fmt(f)?;
|
||||
f.write_str("() ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Function<Resolved>> for Function<Unresolved> {
|
||||
fn resolve(self) -> Result<Function<Resolved>, ()> {
|
||||
Ok(Function {
|
||||
name: self.name.resolve()?,
|
||||
body: self.body.resolve()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use super::{Parsable, ParseResult, ParserError, Resolvable, Token};
|
||||
|
||||
pub struct Ident(String);
|
||||
|
||||
impl Ident {
|
||||
pub fn val(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for Ident {
|
||||
fn parse(cursor: &mut super::TokenCursor, errors: &mut super::ParserErrors) -> ParseResult<Self> {
|
||||
let next = cursor.expect_peek()?;
|
||||
let Token::Ident(name) = &next.token else {
|
||||
return ParseResult::Err(ParserError::unexpected_token(next, "an identifier"));
|
||||
};
|
||||
let name = name.to_string();
|
||||
cursor.next();
|
||||
ParseResult::Ok(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Ident> for Ident {
|
||||
fn resolve(self) -> Result<Ident, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use super::{
|
||||
CharCursor, MaybeParsable, ParserError, ParserErrors, Resolvable, Symbol, Token, TokenCursor,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
Char(char),
|
||||
Number(Number),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Number {
|
||||
pub whole: String,
|
||||
pub decimal: Option<String>,
|
||||
pub ty: Option<String>,
|
||||
}
|
||||
|
||||
impl MaybeParsable for Literal {
|
||||
fn maybe_parse(
|
||||
cursor: &mut TokenCursor,
|
||||
_: &mut ParserErrors,
|
||||
) -> Result<Option<Self>, ParserError> {
|
||||
let inst = cursor.expect_peek()?;
|
||||
let mut res = match &inst.token {
|
||||
Token::Symbol(Symbol::SingleQuote) => {
|
||||
let chars = cursor.chars();
|
||||
let c = chars.expect_next()?;
|
||||
chars.expect('\'')?;
|
||||
Self::Char(c)
|
||||
}
|
||||
Token::Symbol(Symbol::DoubleQuote) => Self::String(string_from(cursor.chars())?),
|
||||
Token::Ident(text) => {
|
||||
let first = text.chars().next().unwrap();
|
||||
if first.is_ascii_digit() {
|
||||
Self::Number(Number {
|
||||
whole: text.to_string(),
|
||||
decimal: None,
|
||||
ty: None,
|
||||
})
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
_ => return Ok(None),
|
||||
};
|
||||
cursor.next();
|
||||
if let (Some(next), Self::Number(num)) = (cursor.peek(), &mut res) {
|
||||
if next.token.is_symbol(Symbol::Dot) {
|
||||
cursor.next();
|
||||
if let Some(next) = cursor.peek() {
|
||||
if let Token::Ident(i) = &next.token {
|
||||
if i.chars().next().unwrap().is_ascii_digit() {
|
||||
num.decimal = Some(i.to_string());
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(res))
|
||||
}
|
||||
}
|
||||
pub fn string_from(cursor: &mut CharCursor) -> Result<String, ParserError> {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let c = cursor.expect_next()?;
|
||||
if c == '"' {
|
||||
return Ok(str);
|
||||
}
|
||||
str.push(match c {
|
||||
'\\' => {
|
||||
let next = cursor.expect_next()?;
|
||||
match next {
|
||||
'"' => '"',
|
||||
'\'' => '\'',
|
||||
't' => '\t',
|
||||
'n' => '\n',
|
||||
'0' => '\0',
|
||||
_ => {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => c,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Literal> for Literal {
|
||||
fn resolve(self) -> Result<Literal, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(str) => str.fmt(f),
|
||||
Self::Char(c) => c.fmt(f),
|
||||
Self::Number(n) => n.fmt(f),
|
||||
Self::Unit => f.write_str("()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Number {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.whole)?;
|
||||
if let Some(d) = &self.decimal {
|
||||
write!(f, ".{}", d)?;
|
||||
}
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, "T{}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
mod body;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod module;
|
||||
mod op;
|
||||
mod statement;
|
||||
mod lit;
|
||||
mod ident;
|
||||
|
||||
pub use body::*;
|
||||
pub use expr::*;
|
||||
pub use func::*;
|
||||
pub use module::*;
|
||||
pub use op::*;
|
||||
pub use statement::*;
|
||||
pub use lit::*;
|
||||
pub use ident::*;
|
||||
|
||||
use super::*;
|
||||
@@ -1,48 +0,0 @@
|
||||
use super::{
|
||||
Function, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserError, ParserErrors,
|
||||
Resolvable, Resolved, TokenCursor, Unresolved,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct Module<R: MaybeResolved> {
|
||||
pub functions: Vec<Node<Function<R>, R>>,
|
||||
}
|
||||
|
||||
impl Parsable for Module<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let mut functions = Vec::new();
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return ParseResult::Ok(Self { functions });
|
||||
};
|
||||
if next.is_keyword(Keyword::Fn) {
|
||||
let res = Node::parse(cursor, errors);
|
||||
functions.push(res.node);
|
||||
if res.recover {
|
||||
return ParseResult::Recover(Self { functions });
|
||||
}
|
||||
} else {
|
||||
errors.add(ParserError::unexpected_token(next, "fn"));
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Module<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.functions.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Module<Resolved>> for Module<Unresolved> {
|
||||
fn resolve(self) -> Result<Module<Resolved>, ()> {
|
||||
Ok(Module {
|
||||
functions: self
|
||||
.functions
|
||||
.into_iter()
|
||||
.map(|f| f.resolve())
|
||||
.collect::<Result<_, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use super::{Symbol, Token};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum BinaryOperator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Access,
|
||||
Assign,
|
||||
}
|
||||
|
||||
impl BinaryOperator {
|
||||
pub fn presedence(&self) -> u32 {
|
||||
match self {
|
||||
Self::Assign => 0,
|
||||
Self::LessThan => 1,
|
||||
Self::GreaterThan => 1,
|
||||
Self::Add => 2,
|
||||
Self::Sub => 3,
|
||||
Self::Mul => 4,
|
||||
Self::Div => 5,
|
||||
Self::Access => 6,
|
||||
}
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::Access => ".",
|
||||
Self::Assign => "=",
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::OpenAngle => Self::LessThan,
|
||||
Symbol::CloseAngle => Self::GreaterThan,
|
||||
Symbol::Plus => Self::Add,
|
||||
Symbol::Minus => Self::Sub,
|
||||
Symbol::Asterisk => Self::Mul,
|
||||
Symbol::Slash => Self::Div,
|
||||
Symbol::Dot => Self::Access,
|
||||
Symbol::Equals => Self::Assign,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn pad(&self) -> bool {
|
||||
match self {
|
||||
Self::Add => true,
|
||||
Self::Sub => true,
|
||||
Self::Mul => true,
|
||||
Self::Div => true,
|
||||
Self::LessThan => true,
|
||||
Self::GreaterThan => true,
|
||||
Self::Access => false,
|
||||
Self::Assign => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum UnaryOperator {
|
||||
Not,
|
||||
Ref,
|
||||
}
|
||||
|
||||
impl UnaryOperator {
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Not => "!",
|
||||
Self::Ref => "&",
|
||||
}
|
||||
}
|
||||
pub fn from_token(token: &Token) -> Option<Self> {
|
||||
let Token::Symbol(symbol) = token else {
|
||||
return None;
|
||||
};
|
||||
Some(match symbol {
|
||||
Symbol::Ampersand => Self::Ref,
|
||||
Symbol::Bang => Self::Not,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
use super::{
|
||||
Expr, Ident, Keyword, MaybeResolved, Node, Parsable, ParseResult, ParserErrors, Resolvable, Resolved, Symbol, Token, TokenCursor, Unresolved
|
||||
};
|
||||
|
||||
pub enum Statement<R: MaybeResolved> {
|
||||
Let(Node<Ident, R>, Node<Expr<R>, R>),
|
||||
Return(Node<Expr<R>, R>),
|
||||
Expr(Node<Expr<R>, R>),
|
||||
}
|
||||
|
||||
impl Parsable for Statement<Unresolved> {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self> {
|
||||
let next = cursor.expect_peek()?;
|
||||
match next.token {
|
||||
Token::Keyword(Keyword::Let) => {
|
||||
cursor.next();
|
||||
let name = Node::parse(cursor, errors)?;
|
||||
cursor.expect_sym(Symbol::Equals)?;
|
||||
Node::parse(cursor, errors).map(|expr| Self::Let(name, expr))
|
||||
}
|
||||
Token::Keyword(Keyword::Return) => {
|
||||
cursor.next();
|
||||
Node::parse(cursor, errors).map(Self::Return)
|
||||
}
|
||||
_ => Node::parse(cursor, errors).map(Self::Expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolvable<Statement<Resolved>> for Statement<Unresolved> {
|
||||
fn resolve(self) -> Result<Statement<Resolved>, ()> {
|
||||
Ok(match self {
|
||||
Self::Let(i, e) => Statement::Let(i.resolve()?, e.resolve()?),
|
||||
Self::Return(e) => Statement::Return(e.resolve()?),
|
||||
Self::Expr(e) => Statement::Expr(e.resolve()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Statement<Unresolved> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Statement::Let(n, e) => {
|
||||
f.write_str("let ")?;
|
||||
n.fmt(f);
|
||||
f.write_str(" = ")?;
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
Statement::Return(e) => {
|
||||
f.write_str("return ")?;
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
e.fmt(f)?;
|
||||
f.write_char(';')?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
};
|
||||
|
||||
use super::{Node, ParserError, ParserErrors, TokenCursor, Unresolved};
|
||||
|
||||
pub enum ParseResult<T> {
|
||||
Ok(T),
|
||||
Recover(T),
|
||||
Err(ParserError),
|
||||
SubErr,
|
||||
}
|
||||
|
||||
impl<T> ParseResult<T> {
|
||||
pub fn from_recover(data: T, recover: bool) -> Self {
|
||||
if recover {
|
||||
Self::Recover(data)
|
||||
} else {
|
||||
Self::Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for ParseResult<T> {
|
||||
type Output = Result<T, T>;
|
||||
type Residual = Option<ParserError>;
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
match output {
|
||||
Ok(v) => Self::Ok(v),
|
||||
Err(v) => Self::Recover(v),
|
||||
}
|
||||
}
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
ParseResult::Ok(v) => ControlFlow::Continue(Ok(v)),
|
||||
ParseResult::Recover(v) => ControlFlow::Continue(Err(v)),
|
||||
ParseResult::Err(e) => ControlFlow::Break(Some(e)),
|
||||
ParseResult::SubErr => ControlFlow::Break(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for ParseResult<T> {
|
||||
fn from_residual(residual: <Self as Try>::Residual) -> Self {
|
||||
match residual {
|
||||
Some(err) => Self::Err(err),
|
||||
None => Self::SubErr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual<Result<Infallible, ParserError>> for ParseResult<T> {
|
||||
fn from_residual(residual: Result<Infallible, ParserError>) -> Self {
|
||||
match residual {
|
||||
Err(e) => Self::Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> FromResidual<ParseResult<T>> for ParseResult<U> {
|
||||
fn from_residual(residual: ParseResult<T>) -> Self {
|
||||
match residual {
|
||||
ParseResult::Err(e) => Self::Err(e),
|
||||
ParseResult::SubErr => Self::SubErr,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeParseResult<T> {
|
||||
pub node: Node<T, Unresolved>,
|
||||
pub recover: bool,
|
||||
}
|
||||
|
||||
impl<T> NodeParseResult<T> {
|
||||
pub fn map<F: FnOnce(Node<T, Unresolved>) -> U, U>(self, op: F) -> ParseResult<U> {
|
||||
let res = op(self.node);
|
||||
if self.recover {
|
||||
ParseResult::Recover(res)
|
||||
} else {
|
||||
ParseResult::Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for NodeParseResult<T> {
|
||||
type Output = Node<T, Unresolved>;
|
||||
type Residual = ParseResult<T>;
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self {
|
||||
node: output,
|
||||
recover: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
if self.recover {
|
||||
ControlFlow::Break(ParseResult::SubErr)
|
||||
} else {
|
||||
ControlFlow::Continue(self.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual for NodeParseResult<T> {
|
||||
fn from_residual(_: <Self as Try>::Residual) -> Self {
|
||||
// I hope this is unreachable ???
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Parsable: Sized {
|
||||
fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult<Self>;
|
||||
}
|
||||
|
||||
pub trait MaybeParsable: Sized {
|
||||
fn maybe_parse(
|
||||
cursor: &mut TokenCursor,
|
||||
errors: &mut ParserErrors,
|
||||
) -> Result<Option<Self>, ParserError>;
|
||||
}
|
||||
|
||||
impl<T: Parsable> Node<T, Unresolved> {
|
||||
pub fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<T> {
|
||||
let start = cursor.next_pos();
|
||||
let (inner, recover) = match T::parse(cursor, errors) {
|
||||
ParseResult::Ok(v) => (Ok(v), false),
|
||||
ParseResult::Recover(v) => (Ok(v), true),
|
||||
ParseResult::Err(e) => {
|
||||
errors.add(e);
|
||||
(Err(()), true)
|
||||
}
|
||||
ParseResult::SubErr => (Err(()), true),
|
||||
};
|
||||
let end = cursor.prev_end();
|
||||
NodeParseResult {
|
||||
node: Self {
|
||||
inner,
|
||||
span: start.to(end),
|
||||
},
|
||||
recover,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MaybeParsable> Node<T, Unresolved> {
|
||||
pub fn maybe_parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> Option<Self> {
|
||||
let start = cursor.next_pos();
|
||||
let inner = match T::maybe_parse(cursor, errors) {
|
||||
Ok(v) => Ok(v?),
|
||||
Err(e) => {
|
||||
errors.add(e);
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
let end = cursor.prev_end();
|
||||
Some(Self {
|
||||
inner,
|
||||
span: start.to(end),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeParsable {
|
||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
impl<T: Parsable> NodeParsable for T {
|
||||
fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Node::<Self, Unresolved>::parse(cursor, errors)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
use super::super::ParserError;
|
||||
use super::FilePos;
|
||||
|
||||
pub struct CharCursor<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
next_pos: FilePos,
|
||||
prev_pos: FilePos,
|
||||
}
|
||||
|
||||
impl CharCursor<'_> {
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let res = self.peek()?;
|
||||
self.advance();
|
||||
Some(res)
|
||||
}
|
||||
pub fn expect(&mut self, c: char) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == c {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::at(
|
||||
self.prev_pos,
|
||||
format!("unexpected char '{next}'; expected '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
pub fn advance(&mut self) {
|
||||
let Some(next) = self.chars.next() else {
|
||||
return;
|
||||
};
|
||||
self.prev_pos = self.next_pos;
|
||||
if next == '\n' {
|
||||
self.next_pos.col = 0;
|
||||
self.next_pos.line += 1;
|
||||
} else {
|
||||
self.next_pos.col += 1;
|
||||
}
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
||||
self.next().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn next_pos(&self) -> FilePos {
|
||||
self.next_pos
|
||||
}
|
||||
pub fn prev_pos(&self) -> FilePos {
|
||||
self.prev_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self {
|
||||
chars: value.chars().peekable(),
|
||||
next_pos: FilePos::start(),
|
||||
prev_pos: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FilePos {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileSpan {
|
||||
pub start: FilePos,
|
||||
pub end: FilePos,
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn start() -> Self {
|
||||
Self { line: 0, col: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn to(self, end: FilePos) -> FileSpan {
|
||||
FileSpan { start: self, end }
|
||||
}
|
||||
pub fn char_span(self) -> FileSpan {
|
||||
FileSpan::at(self)
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE: usize = 1;
|
||||
const AFTER: usize = 0;
|
||||
|
||||
impl FileSpan {
|
||||
pub fn at(pos: FilePos) -> Self {
|
||||
Self {
|
||||
start: pos,
|
||||
end: pos,
|
||||
}
|
||||
}
|
||||
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
|
||||
let start = self.start.line.saturating_sub(BEFORE);
|
||||
let num_before = self.start.line - start;
|
||||
let mut lines = file.lines().skip(start);
|
||||
let width = format!("{}", self.end.line + AFTER).len();
|
||||
let same_line = self.start.line == self.end.line;
|
||||
for i in 0..num_before {
|
||||
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
|
||||
let len = if same_line {
|
||||
self.end.col - self.start.col + 1
|
||||
} else {
|
||||
line.len() - self.start.col
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
" ".repeat(self.start.col) + &"^".repeat(len)
|
||||
)?;
|
||||
if !same_line {
|
||||
for _ in 0..self.end.line - self.start.line - 1 {
|
||||
lines.next();
|
||||
}
|
||||
let line = lines.next().unwrap();
|
||||
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
|
||||
writeln!(
|
||||
writer,
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
"^".repeat(self.end.col + 1)
|
||||
)?;
|
||||
}
|
||||
// for i in 0..AFTER {
|
||||
// if let Some(next) = lines.next() {
|
||||
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
|
||||
// }
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Keyword {
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Return,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
pub fn from_string(str: &str) -> Option<Self> {
|
||||
Some(match str {
|
||||
"fn" => Self::Fn,
|
||||
"let" => Self::Let,
|
||||
"if" => Self::If,
|
||||
"return" => Self::Return,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
mod cursor;
|
||||
mod file;
|
||||
mod keyword;
|
||||
mod symbol;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use file::*;
|
||||
pub use keyword::*;
|
||||
pub use symbol::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Token {
|
||||
Symbol(Symbol),
|
||||
Ident(String),
|
||||
Keyword(Keyword),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenInstance {
|
||||
pub token: Token,
|
||||
pub span: FileSpan,
|
||||
}
|
||||
|
||||
impl TokenInstance {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<TokenInstance> {
|
||||
cursor.skip_whitespace();
|
||||
cursor.peek()?;
|
||||
let start = cursor.next_pos();
|
||||
if let Some(s) = Symbol::parse(cursor) {
|
||||
if s == Symbol::DoubleSlash {
|
||||
while cursor.next() != Some('\n') {}
|
||||
return Self::parse(cursor);
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
return Some(Self {
|
||||
token: Token::Symbol(s),
|
||||
span: FileSpan { start, end },
|
||||
});
|
||||
}
|
||||
let mut word = String::new();
|
||||
while let Some(c) = cursor.peek() {
|
||||
if c.is_whitespace() || Symbol::from_char(c).is_some() {
|
||||
break;
|
||||
}
|
||||
word.push(c);
|
||||
cursor.advance();
|
||||
}
|
||||
let end = cursor.prev_pos();
|
||||
let token = if let Some(keyword) = Keyword::from_string(&word) {
|
||||
Token::Keyword(keyword)
|
||||
} else {
|
||||
Token::Ident(word)
|
||||
};
|
||||
Some(Self {
|
||||
token,
|
||||
span: FileSpan { start, end },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn is_symbol(&self, symbol: Symbol) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => *s == symbol,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_symbol_and(&self, f: impl Fn(Symbol) -> bool) -> bool {
|
||||
match self {
|
||||
Token::Symbol(s) => f(*s),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyword(&self, kw: Keyword) -> bool {
|
||||
match self {
|
||||
Token::Keyword(k) => *k == kw,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TokenInstance {
|
||||
type Target = Token;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.token
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::CharCursor;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Symbol {
|
||||
Semicolon,
|
||||
Colon,
|
||||
DoubleColon,
|
||||
Equals,
|
||||
DoubleEquals,
|
||||
Arrow,
|
||||
DoubleArrow,
|
||||
Plus,
|
||||
Minus,
|
||||
Asterisk,
|
||||
Slash,
|
||||
DoubleSlash,
|
||||
Dot,
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
OpenCurly,
|
||||
CloseCurly,
|
||||
OpenSquare,
|
||||
CloseSquare,
|
||||
OpenAngle,
|
||||
CloseAngle,
|
||||
SingleQuote,
|
||||
DoubleQuote,
|
||||
Bang,
|
||||
Ampersand,
|
||||
DoubleAmpersand,
|
||||
Pipe,
|
||||
DoublePipe,
|
||||
Comma,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
||||
Self::from_char(cursor.peek()?).map(|mut s| {
|
||||
cursor.advance();
|
||||
s.finish(cursor);
|
||||
s
|
||||
})
|
||||
}
|
||||
pub fn from_char(c: char) -> Option<Self> {
|
||||
Some(match c {
|
||||
'(' => Self::OpenParen,
|
||||
')' => Self::CloseParen,
|
||||
'[' => Self::OpenSquare,
|
||||
']' => Self::CloseSquare,
|
||||
'{' => Self::OpenCurly,
|
||||
'}' => Self::CloseCurly,
|
||||
'<' => Self::OpenAngle,
|
||||
'>' => Self::CloseAngle,
|
||||
';' => Self::Semicolon,
|
||||
':' => Self::Colon,
|
||||
'+' => Self::Plus,
|
||||
'-' => Self::Minus,
|
||||
'*' => Self::Asterisk,
|
||||
'/' => Self::Slash,
|
||||
'=' => Self::Equals,
|
||||
'.' => Self::Dot,
|
||||
'\'' => Self::SingleQuote,
|
||||
'"' => Self::DoubleQuote,
|
||||
'!' => Self::Bang,
|
||||
'&' => Self::Ampersand,
|
||||
'|' => Self::Pipe,
|
||||
',' => Self::Comma,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
pub fn finish(&mut self, cursor: &mut CharCursor) {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return;
|
||||
};
|
||||
*self = match self {
|
||||
Self::Colon => match next {
|
||||
':' => Self::DoubleColon,
|
||||
_ => return,
|
||||
},
|
||||
Self::Minus => match next {
|
||||
'>' => Self::Arrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Equals => match next {
|
||||
'=' => Self::DoubleEquals,
|
||||
'>' => Self::DoubleArrow,
|
||||
_ => return,
|
||||
},
|
||||
Self::Slash => match next {
|
||||
'/' => Self::DoubleSlash,
|
||||
_ => return,
|
||||
},
|
||||
Self::Ampersand => match next {
|
||||
'&' => Self::DoubleAmpersand,
|
||||
_ => return,
|
||||
},
|
||||
Self::Pipe => match next {
|
||||
'&' => Self::DoublePipe,
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
cursor.advance();
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Semicolon => ";",
|
||||
Self::Colon => ":",
|
||||
Self::DoubleColon => "::",
|
||||
Self::Equals => "=",
|
||||
Self::DoubleEquals => "==",
|
||||
Self::Arrow => "->",
|
||||
Self::DoubleArrow => "=>",
|
||||
Self::Plus => "+",
|
||||
Self::Minus => "-",
|
||||
Self::Asterisk => "*",
|
||||
Self::Slash => "/",
|
||||
Self::DoubleSlash => "//",
|
||||
Self::Dot => ".",
|
||||
Self::OpenParen => "(",
|
||||
Self::CloseParen => ")",
|
||||
Self::OpenCurly => "{",
|
||||
Self::CloseCurly => "}",
|
||||
Self::OpenSquare => "[",
|
||||
Self::CloseSquare => "]",
|
||||
Self::OpenAngle => "<",
|
||||
Self::CloseAngle => ">",
|
||||
Self::SingleQuote => "'",
|
||||
Self::DoubleQuote => "\"",
|
||||
Self::Bang => "!",
|
||||
Self::Comma => ",",
|
||||
Self::Ampersand => "&",
|
||||
Self::DoubleAmpersand => "&&",
|
||||
Self::Pipe => "|",
|
||||
Self::DoublePipe => "||",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Symbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "'{}'", self.str())
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Debug, Write};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::util::Padder;
|
||||
|
||||
use super::util::WHITESPACE_SET;
|
||||
use super::CharCursor;
|
||||
use super::Expr;
|
||||
use super::ParserError;
|
||||
|
||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
set.extend(&['(']);
|
||||
set
|
||||
});
|
||||
|
||||
pub struct Body {
|
||||
statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Let(String, Expr),
|
||||
Return(Expr),
|
||||
Expr(Expr),
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let mut statements = Vec::new();
|
||||
cursor.expect_char('{')?;
|
||||
loop {
|
||||
cursor.skip_whitespace();
|
||||
let next = cursor.expect_peek()?;
|
||||
if next == '}' {
|
||||
cursor.next();
|
||||
return Ok(Self { statements });
|
||||
}
|
||||
statements.push(Statement::parse(cursor)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
Ok(if cursor.advance_if_str("let", &WHITESPACE_SET) {
|
||||
cursor.skip_whitespace();
|
||||
let name = cursor.until(&NAME_END);
|
||||
if name.is_empty() {
|
||||
return Err(ParserError::at(
|
||||
cursor.pos(),
|
||||
"Expected variable name".to_string(),
|
||||
));
|
||||
}
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char('=')?;
|
||||
let expr = Expr::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(';')?;
|
||||
Self::Let(name, expr)
|
||||
} else if cursor.advance_if_str("return", &WHITESPACE_SET) {
|
||||
let expr = Expr::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(';')?;
|
||||
Self::Return(expr)
|
||||
} else {
|
||||
let expr = Expr::parse(cursor)?;
|
||||
match cursor.expect_peek()? {
|
||||
';' => {
|
||||
cursor.next();
|
||||
Self::Expr(expr)
|
||||
}
|
||||
'}' => Self::Return(expr),
|
||||
_ => {
|
||||
cursor.next();
|
||||
return Err(ParserError::at(
|
||||
cursor.prev_pos(),
|
||||
"unexpected end of statement; expected a ';' or '}'".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Statement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Statement::Let(n, e) => {
|
||||
write!(f, "let {n} = {e:?};")?;
|
||||
}
|
||||
Statement::Return(e) => {
|
||||
write!(f, "return {e:?};")?;
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
write!(f, "{e:?};")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Body {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.statements.first().is_some() {
|
||||
write!(f, "{{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
for s in &self.statements {
|
||||
// they don't expose wrap_buf :grief:
|
||||
writeln!(padder, "{s:?}")?;
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
} else {
|
||||
write!(f, "{{}}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
use std::{collections::HashSet, iter::Peekable, str::Chars};
|
||||
|
||||
use super::{error::ParserError, util::WHITESPACE_SET};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FilePos {
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileRegion {
|
||||
pub start: FilePos,
|
||||
pub end: FilePos,
|
||||
}
|
||||
|
||||
pub struct CharCursor<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
pos: FilePos,
|
||||
prev_pos: FilePos,
|
||||
}
|
||||
|
||||
impl CharCursor<'_> {
|
||||
pub fn until(&mut self, set: &HashSet<char>) -> String {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let Some(next) = self.peek() else {
|
||||
return str;
|
||||
};
|
||||
if set.contains(&next) {
|
||||
return str;
|
||||
}
|
||||
str.push(next);
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
pub fn skip_whitespace(&mut self) {
|
||||
while self.peek().is_some_and(|c| c.is_whitespace()) {
|
||||
self.advance();
|
||||
}
|
||||
let mut copy = self.chars.clone();
|
||||
if let Some('/') = copy.next() {
|
||||
if let Some('/') = copy.next() {
|
||||
self.advance();
|
||||
self.advance();
|
||||
while self.next() != Some('\n') {}
|
||||
self.skip_whitespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let res = self.peek()?;
|
||||
self.advance();
|
||||
Some(res)
|
||||
}
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
pub fn advance(&mut self) {
|
||||
self.prev_pos = self.pos;
|
||||
if self.peek().is_some_and(|c| c == '\n') {
|
||||
self.pos.col = 0;
|
||||
self.pos.line += 1;
|
||||
} else {
|
||||
self.pos.col += 1;
|
||||
}
|
||||
self.chars.next();
|
||||
}
|
||||
pub fn advance_if(&mut self, c: char) -> bool {
|
||||
if let Some(c2) = self.peek() {
|
||||
if c2 == c {
|
||||
self.advance();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn advance_if_str(&mut self, exp: &str, end: &HashSet<char>) -> bool {
|
||||
let mut new = self.chars.clone();
|
||||
for e in exp.chars() {
|
||||
let Some(c) = new.next() else {
|
||||
return false;
|
||||
};
|
||||
if e != c {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if new.peek().is_some_and(|c| !end.contains(c)) {
|
||||
return false;
|
||||
}
|
||||
for _ in 0..exp.len() {
|
||||
self.advance();
|
||||
}
|
||||
true
|
||||
}
|
||||
pub fn expect_char(&mut self, c: char) -> Result<(), ParserError> {
|
||||
let next = self.expect_next()?;
|
||||
if next == c {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::at(
|
||||
self.prev_pos,
|
||||
format!("unexpected char '{next}'; expected '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<char, ParserError> {
|
||||
self.next().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<char, ParserError> {
|
||||
self.peek().ok_or(ParserError::unexpected_end())
|
||||
}
|
||||
pub fn pos(&self) -> FilePos {
|
||||
self.pos
|
||||
}
|
||||
pub fn prev_pos(&self) -> FilePos {
|
||||
self.prev_pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for CharCursor<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self {
|
||||
chars: value.chars().peekable(),
|
||||
pos: FilePos::start(),
|
||||
prev_pos: FilePos::start(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePos {
|
||||
pub fn start() -> Self {
|
||||
Self { line: 0, col: 0 }
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use super::{FilePos, FileRegion};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParserError {
|
||||
pub msg: String,
|
||||
pub regions: Vec<FileRegion>,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn from_msg(msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
regions: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn at(pos: FilePos, msg: String) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
regions: vec![FileRegion {
|
||||
start: pos,
|
||||
end: pos,
|
||||
}],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("Unexpected end of input".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
const BEFORE: usize = 1;
|
||||
const AFTER: usize = 1;
|
||||
|
||||
pub fn print_error(err: ParserError, file: &str) {
|
||||
let after = if err.regions.is_empty() {""} else {":"};
|
||||
println!("error: {}{}", err.msg, after);
|
||||
for reg in err.regions {
|
||||
print_region(file, reg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_region(file: &str, reg: FileRegion) {
|
||||
let start = reg.start.line.saturating_sub(BEFORE);
|
||||
let num_before = reg.start.line - start;
|
||||
let mut lines = file.lines().skip(start);
|
||||
let len = reg.end.col - reg.start.col + 1;
|
||||
let width = format!("{}", reg.end.line + AFTER).len();
|
||||
for i in 0..num_before + 1 {
|
||||
println!("{:>width$} | {}", start + i, lines.next().unwrap());
|
||||
}
|
||||
println!(
|
||||
"{} | {}",
|
||||
" ".repeat(width),
|
||||
" ".repeat(reg.start.col) + &"^".repeat(len)
|
||||
);
|
||||
for i in 0..AFTER {
|
||||
if let Some(next) = lines.next() {
|
||||
println!("{:>width$} | {}", reg.end.line + i + 1, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||
|
||||
static SYMBOLS: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = HashSet::new();
|
||||
for o in Operator::ALL {
|
||||
for c in o.str().chars() {
|
||||
set.insert(c);
|
||||
}
|
||||
}
|
||||
set
|
||||
});
|
||||
|
||||
static IDENT_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
let symbols = &SYMBOLS;
|
||||
set.extend(symbols.iter().chain(&[';', '(', ')']));
|
||||
set
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Val {
|
||||
String(String),
|
||||
Number(String),
|
||||
Unit,
|
||||
}
|
||||
|
||||
pub enum Expr {
|
||||
Block(Body),
|
||||
Val(Val),
|
||||
Ident(String),
|
||||
BinaryOp(Operator, Box<Expr>, Box<Expr>),
|
||||
Call(Box<Expr>, Vec<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Offset,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let Some(next) = cursor.peek() else {
|
||||
return Ok(Self::Val(Val::Unit));
|
||||
};
|
||||
let mut e1 = match next {
|
||||
'(' => {
|
||||
cursor.advance();
|
||||
let expr = Self::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(')')?;
|
||||
expr
|
||||
}
|
||||
'{' => {
|
||||
Self::Block(Body::parse(cursor)?)
|
||||
}
|
||||
_ => {
|
||||
if let Some(val) = Val::parse_nonunit(cursor)? {
|
||||
Self::Val(val)
|
||||
} else {
|
||||
let name = cursor.until(&IDENT_END);
|
||||
Self::Ident(name)
|
||||
}
|
||||
}
|
||||
};
|
||||
cursor.skip_whitespace();
|
||||
let Some(mut next) = cursor.peek() else {
|
||||
return Ok(e1);
|
||||
};
|
||||
while next == '(' {
|
||||
cursor.advance();
|
||||
let inner = Self::parse(cursor)?;
|
||||
cursor.skip_whitespace();
|
||||
cursor.expect_char(')')?;
|
||||
e1 = Self::Call(Box::new(e1), vec![inner]);
|
||||
let Some(next2) = cursor.peek() else {
|
||||
return Ok(e1);
|
||||
};
|
||||
next = next2
|
||||
}
|
||||
if let Some(op) = Operator::parse(cursor) {
|
||||
let e2 = Self::parse(cursor)?;
|
||||
return Ok(if let Self::BinaryOp(op_next, e2, e3) = e2 {
|
||||
if op.presedence() > op_next.presedence() {
|
||||
Self::BinaryOp(op_next, Box::new(Self::BinaryOp(op, Box::new(e1), e2)), e3)
|
||||
} else {
|
||||
Self::BinaryOp(op, Box::new(e1), Box::new(Self::BinaryOp(op_next, e2, e3)))
|
||||
}
|
||||
} else {
|
||||
Self::BinaryOp(op, Box::new(e1), Box::new(e2))
|
||||
});
|
||||
};
|
||||
Ok(e1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Val {
|
||||
pub fn parse_nonunit(cursor: &mut CharCursor) -> Result<Option<Self>, ParserError> {
|
||||
let Some(next) = cursor.peek() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(match next {
|
||||
'"' => {
|
||||
cursor.advance();
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let mut next = cursor.expect_next()?;
|
||||
if next == '"' {
|
||||
break;
|
||||
}
|
||||
if next == '\\' {
|
||||
next = match cursor.expect_next()? {
|
||||
'"' => '"',
|
||||
c => {
|
||||
return Err(ParserError::at(
|
||||
cursor.pos(),
|
||||
format!("unexpected escape char '{c}'"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
str.push(next);
|
||||
}
|
||||
Self::String(str)
|
||||
}
|
||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
||||
let mut str = String::new();
|
||||
loop {
|
||||
let Some(next) = cursor.peek() else {
|
||||
break;
|
||||
};
|
||||
match next {
|
||||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
||||
str.push(next);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
cursor.advance();
|
||||
}
|
||||
Self::Number(str)
|
||||
}
|
||||
_ => {
|
||||
return Ok(None);
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
const ALL: [Self; 7] = [
|
||||
Self::Add,
|
||||
Self::Sub,
|
||||
Self::Mul,
|
||||
Self::Div,
|
||||
Self::Offset,
|
||||
Self::GreaterThan,
|
||||
Self::LessThan,
|
||||
];
|
||||
pub fn presedence(&self) -> u32 {
|
||||
match self {
|
||||
Operator::LessThan => 0,
|
||||
Operator::GreaterThan => 0,
|
||||
Operator::Add => 1,
|
||||
Operator::Sub => 2,
|
||||
Operator::Mul => 3,
|
||||
Operator::Div => 4,
|
||||
Operator::Offset => 5,
|
||||
}
|
||||
}
|
||||
pub fn str(&self) -> &str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::Offset => ".",
|
||||
}
|
||||
}
|
||||
pub fn parse(cursor: &mut CharCursor) -> Option<Self> {
|
||||
let res = match cursor.peek()? {
|
||||
'+' => Operator::Add,
|
||||
'-' => Operator::Sub,
|
||||
'*' => Operator::Mul,
|
||||
'/' => Operator::Div,
|
||||
'.' => Operator::Offset,
|
||||
_ => return None,
|
||||
};
|
||||
for _ in 0..res.str().len() {
|
||||
cursor.advance();
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
pub fn pad(&self) -> bool {
|
||||
match self {
|
||||
Operator::Add => true,
|
||||
Operator::Sub => true,
|
||||
Operator::Mul => true,
|
||||
Operator::Div => true,
|
||||
Operator::LessThan => true,
|
||||
Operator::GreaterThan => true,
|
||||
Operator::Offset => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expr::Block(b) => write!(f, "{:?}", b)?,
|
||||
Expr::Ident(n) => f.write_str(n)?,
|
||||
Expr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
Expr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
write!(f, "(")?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
write!(f, ", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
}
|
||||
Expr::Val(v) => {
|
||||
write!(f, "{:?}", v)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
mod body;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod expr;
|
||||
mod module;
|
||||
mod util;
|
||||
|
||||
pub use body::*;
|
||||
pub use cursor::*;
|
||||
pub use error::*;
|
||||
pub use expr::*;
|
||||
pub use module::*;
|
||||
|
||||
pub fn parse_file(file: &str) {
|
||||
match Module::parse(&mut CharCursor::from(file)) {
|
||||
Err(err) => print_error(err, file),
|
||||
Ok(module) => println!("{module:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_stdin() {
|
||||
for line in BufReader::new(std::io::stdin()).lines() {
|
||||
let str = &line.expect("failed to read line");
|
||||
let mut cursor = CharCursor::from(&str[..]);
|
||||
match Statement::parse(&mut cursor) {
|
||||
Ok(expr) => println!("{:?}", expr),
|
||||
Err(err) => print_error(err, str),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
functions: Vec<Function>,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub body: Body,
|
||||
}
|
||||
|
||||
static NAME_END: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||
let mut set = WHITESPACE_SET.clone();
|
||||
set.extend(&['(']);
|
||||
set
|
||||
});
|
||||
|
||||
impl Module {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
let mut functions = Vec::new();
|
||||
loop {
|
||||
let next = cursor.until(&WHITESPACE_SET);
|
||||
if next.is_empty() {
|
||||
return Ok(Self { functions });
|
||||
}
|
||||
if next == "fn" {
|
||||
functions.push(Function::parse(cursor)?);
|
||||
} else {
|
||||
return Err(ParserError::at(cursor.pos(), "expected fn".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn parse(cursor: &mut CharCursor) -> Result<Self, ParserError> {
|
||||
cursor.skip_whitespace();
|
||||
let name = cursor.until(&NAME_END);
|
||||
if name.is_empty() {
|
||||
return Err(ParserError::at(cursor.pos(), "expected function name".to_string()));
|
||||
}
|
||||
cursor.expect_char('(')?;
|
||||
cursor.expect_char(')')?;
|
||||
let body = Body::parse(cursor)?;
|
||||
Ok(Self { name, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
f.write_str(&self.name)?;
|
||||
f.write_str("() ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
pub const WHITESPACE: [char; 25] = [
|
||||
'\u{0009}', '\u{000A}', '\u{000B}', '\u{000C}', '\u{000D}', '\u{0020}', '\u{0085}', '\u{00A0}',
|
||||
'\u{1680}', '\u{2000}', '\u{2001}', '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}',
|
||||
'\u{2007}', '\u{2008}', '\u{2009}', '\u{200A}', '\u{2028}', '\u{2029}', '\u{202F}', '\u{205F}',
|
||||
'\u{3000}',
|
||||
];
|
||||
|
||||
pub static WHITESPACE_SET: LazyLock<HashSet<char>> = LazyLock::new(|| HashSet::from_iter(WHITESPACE));
|
||||
@@ -1,53 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::common::FileID;
|
||||
|
||||
use super::{
|
||||
CompilerMsg, CompilerOutput, Node, NodeParseResult, Parsable, ParsableWith, TokenCursor,
|
||||
};
|
||||
|
||||
pub struct ParserCtx<'a> {
|
||||
pub cursor: TokenCursor<'a>,
|
||||
pub output: &'a mut CompilerOutput,
|
||||
}
|
||||
|
||||
impl<'a> Deref for ParserCtx<'a> {
|
||||
type Target = TokenCursor<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ParserCtx<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ParserCtx<'a> {
|
||||
pub fn err(&mut self, msg: CompilerMsg) {
|
||||
self.output.err(msg);
|
||||
}
|
||||
pub fn hint(&mut self, msg: CompilerMsg) {
|
||||
self.output.hint(msg);
|
||||
}
|
||||
pub fn parse<T: Parsable>(&mut self) -> NodeParseResult<T> {
|
||||
Node::parse(self)
|
||||
}
|
||||
pub fn maybe_parse<T>(&mut self) -> Option<NodeParseResult<T>>
|
||||
where
|
||||
Option<T>: Parsable,
|
||||
{
|
||||
Node::maybe_parse(self)
|
||||
}
|
||||
pub fn parse_with<T: ParsableWith>(&mut self, data: T::Data) -> NodeParseResult<T> {
|
||||
Node::parse_with(self, data)
|
||||
}
|
||||
pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self {
|
||||
Self {
|
||||
cursor: TokenCursor::from_file_str(file, string),
|
||||
output,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use crate::common::FileID;
|
||||
|
||||
use super::{
|
||||
token::{CharCursor, Keyword, Symbol, Token, TokenInstance},
|
||||
CompilerMsg, FilePos,
|
||||
};
|
||||
|
||||
pub struct TokenCursor<'a> {
|
||||
cursor: CharCursor<'a>,
|
||||
next: Option<TokenInstance>,
|
||||
next_start: FilePos,
|
||||
prev_end: FilePos,
|
||||
}
|
||||
|
||||
impl<'a> TokenCursor<'a> {
|
||||
pub fn next(&mut self) -> Option<TokenInstance> {
|
||||
self.prev_end = self.cursor.prev_pos();
|
||||
let next = TokenInstance::parse(&mut self.cursor);
|
||||
self.next_start = next
|
||||
.as_ref()
|
||||
.map(|i| i.span.end)
|
||||
.unwrap_or(FilePos::start(self.file()));
|
||||
std::mem::replace(&mut self.next, next)
|
||||
}
|
||||
pub fn expect_next(&mut self) -> Result<TokenInstance, CompilerMsg> {
|
||||
self.peek().ok_or(CompilerMsg::unexpected_end())?;
|
||||
Ok(self.next().unwrap())
|
||||
}
|
||||
pub fn expect_token(&mut self, t: Token) -> Result<(), CompilerMsg> {
|
||||
let next = self.expect_next()?;
|
||||
if t == next.token {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CompilerMsg::unexpected_token(&next, &format!("{t:?}")))
|
||||
}
|
||||
}
|
||||
pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), CompilerMsg> {
|
||||
self.expect_token(Token::Symbol(symbol))
|
||||
}
|
||||
pub fn next_on_new_line(&mut self) -> bool {
|
||||
self.next_start.line != self.prev_end.line
|
||||
}
|
||||
pub fn seek_sym(&mut self, sym: Symbol) {
|
||||
while self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
||||
}
|
||||
pub fn seek_syms(&mut self, syms: &[Symbol]) {
|
||||
while self
|
||||
.peek()
|
||||
.is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s)))
|
||||
{
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn seek_sym_on_line(&mut self, sym: Symbol) {
|
||||
while !self.next_on_new_line() && self.next().is_some_and(|n| !n.is_symbol(sym)) {}
|
||||
}
|
||||
pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> {
|
||||
loop {
|
||||
if f(self.peek()?) {
|
||||
return self.peek();
|
||||
}
|
||||
self.next();
|
||||
}
|
||||
}
|
||||
pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), CompilerMsg> {
|
||||
self.expect_token(Token::Keyword(kw))
|
||||
}
|
||||
pub fn peek(&self) -> Option<&TokenInstance> {
|
||||
self.next.as_ref()
|
||||
}
|
||||
pub fn expect_peek(&mut self) -> Result<&TokenInstance, CompilerMsg> {
|
||||
self.peek().ok_or(CompilerMsg::unexpected_end())
|
||||
}
|
||||
pub fn chars(&mut self) -> &mut CharCursor<'a> {
|
||||
&mut self.cursor
|
||||
}
|
||||
pub fn prev_end(&self) -> FilePos {
|
||||
self.prev_end
|
||||
}
|
||||
pub fn next_start(&self) -> FilePos {
|
||||
self.next_start
|
||||
}
|
||||
pub fn from_file_str(id: FileID, string: &'a str) -> Self {
|
||||
Self::from(CharCursor::from_file_str(id, string))
|
||||
}
|
||||
pub fn file(&self) -> FileID {
|
||||
self.cursor.file()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CharCursor<'a>> for TokenCursor<'a> {
|
||||
fn from(mut cursor: CharCursor<'a>) -> Self {
|
||||
let cur = TokenInstance::parse(&mut cursor);
|
||||
Self {
|
||||
next_start: FilePos::start(cursor.file()),
|
||||
prev_end: FilePos::start(cursor.file()),
|
||||
cursor,
|
||||
next: cur,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use super::Node;
|
||||
use super::PIdent;
|
||||
use super::CompilerMsg;
|
||||
use super::TokenInstance;
|
||||
|
||||
impl CompilerMsg {
|
||||
pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self {
|
||||
CompilerMsg {
|
||||
msg,
|
||||
spans: instances.iter().map(|i| i.span).collect(),
|
||||
}
|
||||
}
|
||||
pub fn unexpected_end() -> Self {
|
||||
Self::from_msg("unexpected end of input".to_string())
|
||||
}
|
||||
pub fn identifier_not_found(id: &Node<PIdent>) -> Self {
|
||||
Self {
|
||||
msg: format!("Identifier '{}' not found", id.as_ref().unwrap()),
|
||||
spans: vec![id.origin],
|
||||
}
|
||||
}
|
||||
pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self {
|
||||
let t = &inst.token;
|
||||
CompilerMsg::from_instances(
|
||||
&[inst],
|
||||
format!("unexpected token {t:?}; expected {expected}"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Import(pub Vec<String>);
|
||||
pub type Imports = HashSet<Import>;
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod riscv64;
|
||||
pub use super::*;
|
||||
@@ -1,216 +0,0 @@
|
||||
use super::{FnLowerCtx, Node, PAsmArg, PIdent, PInstruction};
|
||||
use crate::{
|
||||
compiler::arch::riscv::*,
|
||||
ir::{
|
||||
arch::riscv64::{RV64Instruction, RegRef},
|
||||
UIdent,
|
||||
},
|
||||
};
|
||||
|
||||
impl RV64Instruction {
|
||||
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
let args = &inst.args[..];
|
||||
let opstr = &**inst.op.inner.as_ref()?;
|
||||
// TODO: surely this can be abstracted...
|
||||
let opi = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3| -> Option<Self> {
|
||||
let [dest, src, imm] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Some(Self::OpImm { op, dest, src, imm })
|
||||
};
|
||||
let op = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||
let [dest, src1, src2] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src1 = RegRef::from_arg(src1, ctx)?;
|
||||
let src2 = RegRef::from_arg(src2, ctx)?;
|
||||
Some(Self::Op {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src1,
|
||||
src2,
|
||||
})
|
||||
};
|
||||
let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||
let [dest, src, imm] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Some(Self::OpImmF7 {
|
||||
op,
|
||||
funct,
|
||||
dest,
|
||||
src,
|
||||
imm,
|
||||
})
|
||||
};
|
||||
let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||
let [src, offset, base] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
let offset = i32_from_arg(offset, ctx)?;
|
||||
let base = RegRef::from_arg(base, ctx)?;
|
||||
Some(Self::Store {
|
||||
width,
|
||||
src,
|
||||
offset,
|
||||
base,
|
||||
})
|
||||
};
|
||||
let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||
let [dest, offset, base] = args else {
|
||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let offset = i32_from_arg(offset, ctx)?;
|
||||
let base = RegRef::from_arg(base, ctx)?;
|
||||
Some(Self::Load {
|
||||
width,
|
||||
dest,
|
||||
offset,
|
||||
base,
|
||||
})
|
||||
};
|
||||
Some(match opstr {
|
||||
"ecall" => Self::ECall,
|
||||
"li" => {
|
||||
let [dest, imm] = args else {
|
||||
ctx.err("li requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let imm = i32_from_arg(imm, ctx)?;
|
||||
Self::Li { dest, imm }
|
||||
}
|
||||
"la" => {
|
||||
let [dest, src] = args else {
|
||||
ctx.err("la requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = arg_to_var(src, ctx)?;
|
||||
Self::La { dest, src }
|
||||
}
|
||||
"mv" => {
|
||||
let [dest, src] = args else {
|
||||
ctx.err("la requires 2 arguments".to_string());
|
||||
return None;
|
||||
};
|
||||
let dest = RegRef::from_arg(dest, ctx)?;
|
||||
let src = RegRef::from_arg(src, ctx)?;
|
||||
Self::Mv { dest, src }
|
||||
}
|
||||
|
||||
"lb" => load(ctx, width::B)?,
|
||||
"lh" => load(ctx, width::H)?,
|
||||
"lw" => load(ctx, width::W)?,
|
||||
"ld" => load(ctx, width::D)?,
|
||||
"lbu" => load(ctx, width::BU)?,
|
||||
"lhu" => load(ctx, width::HU)?,
|
||||
"lwu" => load(ctx, width::WU)?,
|
||||
|
||||
"sb" => store(ctx, width::B)?,
|
||||
"sh" => store(ctx, width::H)?,
|
||||
"sw" => store(ctx, width::W)?,
|
||||
"sd" => store(ctx, width::D)?,
|
||||
|
||||
"addi" => opi(ctx, op32i::ADD)?,
|
||||
"slti" => opi(ctx, op32i::SLT)?,
|
||||
"sltiu" => opi(ctx, op32i::SLTU)?,
|
||||
"xori" => opi(ctx, op32i::XOR)?,
|
||||
"ori" => opi(ctx, op32i::OR)?,
|
||||
"andi" => opi(ctx, op32i::AND)?,
|
||||
|
||||
"slli" => opif7(ctx, op32i::SL, op32i::LOGICAL)?,
|
||||
"srli" => opif7(ctx, op32i::SR, op32i::LOGICAL)?,
|
||||
"srla" => opif7(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
||||
|
||||
"add" => op(ctx, op32i::ADD, op32i::F7ADD)?,
|
||||
"sub" => op(ctx, op32i::ADD, op32i::F7SUB)?,
|
||||
"sll" => op(ctx, op32i::SL, op32i::FUNCT7)?,
|
||||
"slt" => op(ctx, op32i::SLT, op32i::FUNCT7)?,
|
||||
"sltu" => op(ctx, op32i::SLTU, op32i::FUNCT7)?,
|
||||
"xor" => op(ctx, op32i::XOR, op32i::FUNCT7)?,
|
||||
"srl" => op(ctx, op32i::SR, op32i::LOGICAL)?,
|
||||
"sra" => op(ctx, op32i::SR, op32i::ARITHMETIC)?,
|
||||
"or" => op(ctx, op32i::OR, op32i::FUNCT7)?,
|
||||
"and" => op(ctx, op32i::AND, op32i::FUNCT7)?,
|
||||
|
||||
"mul" => op(ctx, op32m::MUL, op32m::FUNCT7)?,
|
||||
"mulh" => op(ctx, op32m::MULH, op32m::FUNCT7)?,
|
||||
"mulhsu" => op(ctx, op32m::MULHSU, op32m::FUNCT7)?,
|
||||
"mulhu" => op(ctx, op32m::MULHU, op32m::FUNCT7)?,
|
||||
"div" => op(ctx, op32m::DIV, op32m::FUNCT7)?,
|
||||
"divu" => op(ctx, op32m::DIVU, op32m::FUNCT7)?,
|
||||
"rem" => op(ctx, op32m::REM, op32m::FUNCT7)?,
|
||||
"remu" => op(ctx, op32m::REMU, op32m::FUNCT7)?,
|
||||
|
||||
w => {
|
||||
ctx.err_at(inst.op.origin, format!("Unknown instruction '{}'", w));
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg_to_var(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<UIdent> {
|
||||
let PAsmArg::Ref(node) = node.inner.as_ref()? else {
|
||||
ctx.err_at(
|
||||
node.origin,
|
||||
"Expected variable / function reference".to_string(),
|
||||
);
|
||||
return None;
|
||||
};
|
||||
ctx.ident(node)
|
||||
}
|
||||
|
||||
impl RegRef {
|
||||
pub fn from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
Some(match node.inner.as_ref()? {
|
||||
PAsmArg::Value(node) => {
|
||||
let reg = Reg::from_ident(node, ctx)?;
|
||||
Self::Reg(reg)
|
||||
}
|
||||
PAsmArg::Ref(node) => Self::Var(ctx.ident(node)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
pub fn from_ident(node: &Node<PIdent>, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||
let s = &**node.inner.as_ref()?;
|
||||
let res = Reg::from_str(s);
|
||||
if res.is_none() {
|
||||
ctx.err_at(node.origin, format!("Unknown reg name '{}'", s));
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn i32_from_arg(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<i32> {
|
||||
let PAsmArg::Value(node) = node.inner.as_ref()? else {
|
||||
ctx.err_at(node.origin, "Expected an i32, found reference".to_string());
|
||||
return None;
|
||||
};
|
||||
let word = node.inner.as_ref()?;
|
||||
match word.parse::<i32>() {
|
||||
Ok(x) => Some(x),
|
||||
Err(_) => {
|
||||
ctx.err_at(node.origin, format!("Expected an i64, found {}", word));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
use crate::{
|
||||
compiler::arch::riscv::Reg,
|
||||
ir::{
|
||||
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IdentID, Type, UInstruction,
|
||||
},
|
||||
parser::PAsmBlockArg,
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
|
||||
|
||||
type PLAsmBlockArg = PAsmBlockArg<Reg, IdentID>;
|
||||
|
||||
impl FnLowerable for PInstruction {
|
||||
type Output = RV64Instruction;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<RV64Instruction> {
|
||||
RV64Instruction::parse(self, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PAsmBlock {
|
||||
type Output = IdentID;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
let mut args = Vec::new();
|
||||
let mut output = None;
|
||||
for a in &self.args {
|
||||
if let Some(a) = a.lower(ctx) {
|
||||
match a {
|
||||
PAsmBlockArg::In { reg, var } => args.push(AsmBlockArg {
|
||||
reg,
|
||||
var,
|
||||
ty: AsmBlockArgType::In,
|
||||
}),
|
||||
PAsmBlockArg::Out { reg } => {
|
||||
if output.is_some() {
|
||||
ctx.err("cannot evaluate to more than one register".to_string());
|
||||
continue;
|
||||
}
|
||||
let var = ctx.temp(Type::Bits(64));
|
||||
args.push(AsmBlockArg {
|
||||
var,
|
||||
reg,
|
||||
ty: AsmBlockArgType::Out,
|
||||
});
|
||||
output = Some(var)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let block = UInstruction::AsmBlock {
|
||||
instructions: {
|
||||
let mut v = Vec::new();
|
||||
for i in &self.instructions {
|
||||
if let Some(i) = i.lower(ctx) {
|
||||
v.push(i);
|
||||
}
|
||||
}
|
||||
v
|
||||
},
|
||||
args,
|
||||
};
|
||||
ctx.push(block);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PUAsmBlockArg {
|
||||
type Output = PLAsmBlockArg;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
Some(match self {
|
||||
PAsmBlockArg::In { reg, var } => PLAsmBlockArg::In {
|
||||
reg: Reg::from_ident(reg, ctx)?,
|
||||
var: var.as_ref()?.lower(ctx)?,
|
||||
},
|
||||
PAsmBlockArg::Out { reg } => PLAsmBlockArg::Out {
|
||||
reg: Reg::from_ident(reg, ctx)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
use crate::{
|
||||
ir::{IdentID, Type, UIdent, UInstruction, UVar},
|
||||
parser::{PConstStatement, PStatementLike},
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
|
||||
|
||||
impl FnLowerable for PBlock {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
ctx.ident_stack.push();
|
||||
let mut last = None;
|
||||
let mut statements = Vec::new();
|
||||
let mut fn_nodes = Vec::new();
|
||||
let mut struct_nodes = Vec::new();
|
||||
let mut import_nodes = Vec::new();
|
||||
// first sort statements
|
||||
for s in &self.statements {
|
||||
let Some(s) = s.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
match s {
|
||||
PStatementLike::Statement(s) => statements.push(s),
|
||||
PStatementLike::Const(pconst_statement) => match pconst_statement {
|
||||
PConstStatement::Fn(f) => fn_nodes.push(f),
|
||||
PConstStatement::Struct(s) => struct_nodes.push(s),
|
||||
PConstStatement::Import(i) => import_nodes.push(i),
|
||||
},
|
||||
}
|
||||
}
|
||||
// then lower imports
|
||||
for i_n in &import_nodes {
|
||||
if let Some(i) = i_n.as_ref() {
|
||||
let name = &i.0;
|
||||
let path = ctx.path_for(name);
|
||||
let import = Import(path.clone());
|
||||
if ctx.imports.insert(import) {
|
||||
ctx.def_var(UVar {
|
||||
name: name.clone(),
|
||||
ty: Type::Module(path),
|
||||
origin: i_n.origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// then lower const things
|
||||
let mut structs = Vec::new();
|
||||
for s in &struct_nodes {
|
||||
structs.push(s.lower(ctx.ctx));
|
||||
}
|
||||
for (s, id) in struct_nodes.iter().zip(structs) {
|
||||
if let Some(id) = id {
|
||||
s.lower(ctx.ctx);
|
||||
}
|
||||
}
|
||||
let mut fns = Vec::new();
|
||||
for f in &fn_nodes {
|
||||
fns.push(f.lower(ctx.ctx));
|
||||
}
|
||||
for (f, id) in fn_nodes.iter().zip(fns) {
|
||||
if let Some(id) = id {
|
||||
f.lower(ctx.ctx);
|
||||
}
|
||||
}
|
||||
// then lower statements
|
||||
for s in statements {
|
||||
last = s.lower(ctx);
|
||||
}
|
||||
ctx.ident_stack.pop();
|
||||
last
|
||||
}
|
||||
}
|
||||
|
||||
impl FnLowerable for PStatement {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
match self {
|
||||
PStatement::Let(def, e) => {
|
||||
let def = def.lower(ctx.ctx)?;
|
||||
let res = e.lower(ctx);
|
||||
if let Some(res) = res {
|
||||
ctx.push(UInstruction::Mv { dst: def, src: res });
|
||||
}
|
||||
None
|
||||
}
|
||||
PStatement::Return(e) => {
|
||||
if let Some(e) = e {
|
||||
let src = e.lower(ctx)?;
|
||||
ctx.push_at(UInstruction::Ret { src }, src.origin);
|
||||
} else {
|
||||
let src = ctx.temp(Type::Unit);
|
||||
ctx.push_at(UInstruction::Ret { src }, src.origin);
|
||||
}
|
||||
None
|
||||
}
|
||||
PStatement::Expr(e) => e.lower(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ir::{UVar, VarID};
|
||||
|
||||
use super::{ModuleLowerCtx, Node, PVarDef};
|
||||
|
||||
impl Node<PVarDef> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<VarID> {
|
||||
let s = self.as_ref()?;
|
||||
let name = s.name.as_ref().map_or("{error}", |v| v).to_string();
|
||||
let ty = match &s.ty {
|
||||
Some(ty) => ty.lower(ctx),
|
||||
None => ctx.infer(),
|
||||
};
|
||||
Some(ctx.def_var(UVar {
|
||||
name,
|
||||
ty,
|
||||
origin: self.origin,
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
|
||||
use crate::{
|
||||
ir::{IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, Type, UData, UInstruction},
|
||||
parser::InfixOp,
|
||||
};
|
||||
|
||||
impl FnLowerable for PExpr {
|
||||
type Output = IdentID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
|
||||
let mut e = self;
|
||||
let mut path = Vec::new();
|
||||
let mut gargs = None;
|
||||
loop {
|
||||
match e {
|
||||
PExpr::Member(node, ty, ident) => {
|
||||
e = if let Some(t) = node.as_ref() {
|
||||
ctx.origin = node.origin;
|
||||
path.push((ty, ident, gargs.unwrap_or_default()));
|
||||
&**t
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
}
|
||||
PExpr::Generic(node, nodes) => match gargs {
|
||||
None => gargs = Some(nodes.iter().map(|t| t.lower(ctx)).collect::<Vec<_>>()),
|
||||
Some(_) => {
|
||||
// this should cover the more specific area of ::<...>
|
||||
// but too lazy rn
|
||||
ctx.err("Cannot specify generics here".to_string());
|
||||
return None;
|
||||
}
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
while let PExpr::Member(node, ty, ident) = e {}
|
||||
if path.len() > 0 {
|
||||
// UIdent {
|
||||
// origin: ctx.origin,
|
||||
// status: IdentStatus::Unres { base: (), path: () },
|
||||
// }
|
||||
}
|
||||
let origin = ctx.origin;
|
||||
Some(match e {
|
||||
PExpr::Lit(l) => match l {
|
||||
super::PLiteral::String(s) => {
|
||||
let sty = Type::Bits(8).slice(ctx.p);
|
||||
let dst = ctx.temp_var(origin, sty);
|
||||
let data = s.as_bytes().to_vec();
|
||||
let dty = Type::Bits(8).arr(ctx.ctx.p, data.len() as u32);
|
||||
let dty = ctx.def_ty(dty);
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("string \"{}\"", s.replace("\n", "\\n")),
|
||||
ty: dty,
|
||||
content: data,
|
||||
});
|
||||
ctx.push(UInstruction::LoadSlice { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Char(c) => {
|
||||
let ty = ctx.def_ty(Type::Bits(8));
|
||||
let dst = ctx.temp_var(origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("char '{c}'"),
|
||||
ty,
|
||||
content: c.to_string().as_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Number(n) => {
|
||||
// TODO: temp
|
||||
let ty = ctx.def_ty(Type::Bits(64));
|
||||
let dst = ctx.temp_var(origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("num {n:?}"),
|
||||
ty,
|
||||
content: n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Unit => ctx.temp_var(origin, Type::Unit),
|
||||
},
|
||||
PExpr::Ident(i) => ctx.ident(i),
|
||||
PExpr::BinaryOp(op, e1, e2) => match op {
|
||||
InfixOp::Add => todo!(),
|
||||
InfixOp::Sub => todo!(),
|
||||
InfixOp::Mul => todo!(),
|
||||
InfixOp::Div => todo!(),
|
||||
InfixOp::LessThan => todo!(),
|
||||
InfixOp::GreaterThan => todo!(),
|
||||
InfixOp::Assign => {
|
||||
let res1 = e1.lower(ctx)?;
|
||||
let res2 = e2.lower(ctx)?;
|
||||
ctx.push(UInstruction::Mv {
|
||||
dst: res1,
|
||||
src: res2,
|
||||
});
|
||||
res1
|
||||
}
|
||||
},
|
||||
PExpr::PostfixOp(e, op) => {
|
||||
let res = e.lower(ctx)?;
|
||||
match op {
|
||||
PostfixOp::Ref => {
|
||||
let ty = Type::Ref(ctx.ctx.infer());
|
||||
let dest = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Ref {
|
||||
dst: dest,
|
||||
src: res,
|
||||
});
|
||||
dest
|
||||
}
|
||||
PostfixOp::Deref => {
|
||||
let ty = Type::Deref(ctx.ctx.infer());
|
||||
let dst = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Deref { dst, src: res });
|
||||
dst
|
||||
}
|
||||
PostfixOp::Not => todo!(),
|
||||
}
|
||||
}
|
||||
PExpr::Block(b) => b.lower(ctx)?,
|
||||
PExpr::AsmBlock(b) => b.lower(ctx)?,
|
||||
PExpr::Call(e, args) => {
|
||||
let fe = e.lower(ctx)?;
|
||||
let mut nargs = Vec::new();
|
||||
for arg in args.iter() {
|
||||
let arg = arg.lower(ctx)?;
|
||||
nargs.push(arg);
|
||||
}
|
||||
let dest = ctx.temp(Type::Infer);
|
||||
ctx.push(UInstruction::Call {
|
||||
dst: dest,
|
||||
f: fe,
|
||||
args: nargs,
|
||||
});
|
||||
dest
|
||||
}
|
||||
PExpr::Group(e) => e.lower(ctx)?,
|
||||
PExpr::Construct(e, map) => {
|
||||
let dst = ctx.temp(Type::Infer);
|
||||
let struc = e.lower(ctx)?;
|
||||
let fields = map.lower(ctx)?;
|
||||
ctx.push(UInstruction::Construct { dst, struc, fields });
|
||||
dst
|
||||
}
|
||||
PExpr::If(cond, body) => {
|
||||
let cond = cond.lower(ctx)?;
|
||||
ctx.ident_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.ident_stack.pop();
|
||||
ctx.push(UInstruction::If { cond, body });
|
||||
return None;
|
||||
}
|
||||
PExpr::Loop(body) => {
|
||||
ctx.ident_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.ident_stack.pop();
|
||||
ctx.push(UInstruction::Loop { body });
|
||||
return None;
|
||||
}
|
||||
PExpr::Break => {
|
||||
ctx.push(UInstruction::Break);
|
||||
return None;
|
||||
}
|
||||
PExpr::Continue => {
|
||||
ctx.push(UInstruction::Continue);
|
||||
return None;
|
||||
}
|
||||
PExpr::Member(e, ty, name) => {
|
||||
let id = e.lower(ctx)?;
|
||||
let name_str = name.as_ref()?.0;
|
||||
let cur = &mut ctx.p.idents[id];
|
||||
match cur.status {
|
||||
IdentStatus::Res(res) => {
|
||||
cur.status = IdentStatus::Unres {
|
||||
base: MemRes {
|
||||
mem: Member {
|
||||
id: MemberID
|
||||
},
|
||||
origin: (),
|
||||
gargs: (),
|
||||
},
|
||||
path: (),
|
||||
}
|
||||
}
|
||||
IdentStatus::Unres { base, path } => path.push(MemberIdent {
|
||||
ty: *ty,
|
||||
name: name_str,
|
||||
origin: name.origin,
|
||||
gargs: Vec::new(),
|
||||
}),
|
||||
IdentStatus::Failed(res_err) => return None,
|
||||
IdentStatus::Cooked => return None,
|
||||
}
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::{CompilerMsg, FileSpan, ModuleLowerCtx, Node, PFunction, Typable};
|
||||
use crate::{
|
||||
ir::{
|
||||
FnID, IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, MemberPath, MemberTy,
|
||||
Origin, Res, Type, UFunc, UIdent, UInstrInst, UInstruction,
|
||||
},
|
||||
parser,
|
||||
};
|
||||
|
||||
impl Node<PFunction> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<FnID> {
|
||||
self.as_ref().map(|s| s.lower(ctx, self.origin)).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl PFunction {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, origin: Origin) -> Option<FnID> {
|
||||
let header = self.header.as_ref()?;
|
||||
let name = header.name.as_ref()?.0.clone();
|
||||
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
|
||||
(
|
||||
header
|
||||
.gargs
|
||||
.iter()
|
||||
.flat_map(|a| a.lower(ctx).map(|g| (g.0, g.1, a.origin)))
|
||||
.collect(),
|
||||
header
|
||||
.args
|
||||
.iter()
|
||||
.flat_map(|a| Some(a.lower(ctx)?))
|
||||
.collect(),
|
||||
match &header.ret {
|
||||
Some(ty) => ty.lower(ctx),
|
||||
None => ctx.def_ty(Type::Unit),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), ctx.tc.error)
|
||||
};
|
||||
let gargs = generics.iter().map(|g| g.1).collect();
|
||||
let generics = generics
|
||||
.into_iter()
|
||||
.map(|g| {
|
||||
(
|
||||
g.0,
|
||||
ctx.def_ident(UIdent {
|
||||
status: IdentStatus::Res(Res::Generic(g.1)),
|
||||
origin: g.2,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
ctx.ident_stack.extend(generics.into_iter());
|
||||
let instructions = {
|
||||
let mut fctx = FnLowerCtx {
|
||||
instructions: Vec::new(),
|
||||
ctx,
|
||||
origin: self.body.origin,
|
||||
};
|
||||
let res = self.body.lower(&mut fctx);
|
||||
let mut instructions = fctx.instructions;
|
||||
if let Some(src) = res {
|
||||
let origin = ctx.idents[src].origin;
|
||||
instructions.push(UInstrInst {
|
||||
origin,
|
||||
i: UInstruction::Ret { src },
|
||||
});
|
||||
}
|
||||
instructions
|
||||
};
|
||||
let f = UFunc {
|
||||
origin,
|
||||
gargs,
|
||||
name,
|
||||
args,
|
||||
ret,
|
||||
instructions,
|
||||
};
|
||||
Some(ctx.def_fn(f))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FnLowerCtx<'a, 'b> {
|
||||
pub ctx: &'a mut ModuleLowerCtx<'b>,
|
||||
pub instructions: Vec<UInstrInst>,
|
||||
pub origin: FileSpan,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
pub fn ident(&mut self, node: &Node<parser::PIdent>) -> IdentID {
|
||||
let inst = UIdent {
|
||||
status: if let Some(n) = node.as_ref() {
|
||||
if let Some(&res) = self.ident_stack.search(&n.0) {
|
||||
return res;
|
||||
} else {
|
||||
IdentStatus::Unres {
|
||||
path: vec![MemberIdent {
|
||||
ty: MemberTy::Member,
|
||||
name: n.0.clone(),
|
||||
origin: node.origin,
|
||||
gargs: Vec::new(),
|
||||
}],
|
||||
base: MemRes {
|
||||
mem: Member {
|
||||
id: MemberID::Module(self.module),
|
||||
},
|
||||
origin: self.origin,
|
||||
gargs: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IdentStatus::Cooked
|
||||
},
|
||||
origin: node.origin,
|
||||
};
|
||||
self.def_ident(inst)
|
||||
}
|
||||
pub fn err(&mut self, msg: String) {
|
||||
let origin = self.origin;
|
||||
self.output.err(CompilerMsg::new(msg, origin))
|
||||
}
|
||||
pub fn err_at(&mut self, span: FileSpan, msg: String) {
|
||||
self.output.err(CompilerMsg::new(msg, span))
|
||||
}
|
||||
pub fn temp<T: Typable>(&mut self, ty: T) -> IdentID {
|
||||
self.ctx.temp_var(self.origin, ty)
|
||||
}
|
||||
pub fn push(&mut self, i: UInstruction) {
|
||||
self.push_at(i, self.origin);
|
||||
}
|
||||
pub fn push_at(&mut self, i: UInstruction, span: FileSpan) {
|
||||
self.instructions.push(UInstrInst { i, origin: span });
|
||||
}
|
||||
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
|
||||
FnLowerCtx {
|
||||
ctx: self.ctx,
|
||||
instructions: Vec::new(),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Deref for FnLowerCtx<'_, 'b> {
|
||||
type Target = ModuleLowerCtx<'b>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FnLowerCtx<'_, '_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FnLowerable {
|
||||
type Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T: FnLowerable> FnLowerable for Node<T> {
|
||||
type Output = T::Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
||||
let old_span = ctx.origin;
|
||||
ctx.origin = self.origin;
|
||||
let res = self.as_ref()?.lower(ctx);
|
||||
ctx.origin = old_span;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FnLowerable> FnLowerable for Box<T> {
|
||||
type Output = T::Output;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
||||
self.as_ref().lower(ctx)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ir::IdentID, parser::PMap};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl FnLowerable for PMap {
|
||||
type Output = HashMap<String, IdentID>;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
Some(
|
||||
self.0
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let expr = def.val.as_ref()?.lower(ctx)?;
|
||||
Some((name, expr))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
mod arch;
|
||||
mod asm;
|
||||
mod block;
|
||||
mod def;
|
||||
mod expr;
|
||||
mod func;
|
||||
mod map;
|
||||
mod struc;
|
||||
mod ty;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
ir::{
|
||||
IdentID, IdentStatus, ModID, Origin, Res, Type, TypeID, UFunc, UIdent, UModule, UProgram,
|
||||
UVar,
|
||||
},
|
||||
util::NameStack,
|
||||
};
|
||||
pub use func::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl PModule {
|
||||
pub fn lower(
|
||||
&self,
|
||||
path: Vec<String>,
|
||||
p: &mut UProgram,
|
||||
imports: &mut Imports,
|
||||
output: &mut CompilerOutput,
|
||||
) -> ModID {
|
||||
let name = path.last().unwrap().clone();
|
||||
let f = UFunc {
|
||||
name: name.clone(),
|
||||
args: Vec::new(),
|
||||
instructions: Vec::new(),
|
||||
gargs: Vec::new(),
|
||||
ret: p.def_ty(Type::Unit),
|
||||
origin: self.block.origin,
|
||||
};
|
||||
let fid = p.def_fn(f);
|
||||
let mid = p.def_module(UModule {
|
||||
name,
|
||||
members: HashMap::new(),
|
||||
parent: None,
|
||||
func: fid,
|
||||
});
|
||||
let mut ctx = ModuleLowerCtx {
|
||||
p,
|
||||
output,
|
||||
module: mid,
|
||||
temp: 0,
|
||||
ident_stack: NameStack::new(),
|
||||
};
|
||||
let mut fctx = FnLowerCtx {
|
||||
ctx: &mut ctx,
|
||||
instructions: Vec::new(),
|
||||
origin: self.block.origin,
|
||||
};
|
||||
self.block.lower(&mut fctx);
|
||||
p.fns[fid].instructions = fctx.instructions;
|
||||
mid
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModuleLowerCtx<'a> {
|
||||
pub p: &'a mut UProgram,
|
||||
pub output: &'a mut CompilerOutput,
|
||||
pub module: ModID,
|
||||
pub temp: usize,
|
||||
pub ident_stack: NameStack<IdentID>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleLowerCtx<'a> {
|
||||
pub fn new(program: &'a mut UProgram, output: &'a mut CompilerOutput, id: ModID) -> Self {
|
||||
Self {
|
||||
p: program,
|
||||
output,
|
||||
module: id,
|
||||
temp: 0,
|
||||
ident_stack: NameStack::new(),
|
||||
}
|
||||
}
|
||||
pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
|
||||
self.temp_var_inner(origin, ty)
|
||||
}
|
||||
fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
|
||||
let var = UVar {
|
||||
name: format!("temp{}", self.temp),
|
||||
ty: ty.ty(self),
|
||||
origin,
|
||||
parent: None,
|
||||
children: HashMap::new(),
|
||||
};
|
||||
let id = self.p.def_var(var);
|
||||
self.temp += 1;
|
||||
self.def_ident(UIdent {
|
||||
status: IdentStatus::Res(Res::Var(id)),
|
||||
origin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Typable {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID;
|
||||
}
|
||||
|
||||
impl Typable for Type {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
p.def_ty(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Typable for TypeID {
|
||||
fn ty(self, p: &mut UProgram) -> TypeID {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ModuleLowerCtx<'_> {
|
||||
type Target = UProgram;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ModuleLowerCtx<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.p
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
use crate::{
|
||||
common::FileSpan,
|
||||
ir::{StructField, StructID, UStruct},
|
||||
parser::{PStruct, PStructFields},
|
||||
};
|
||||
|
||||
use super::ModuleLowerCtx;
|
||||
|
||||
impl PStruct {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, span: FileSpan) -> Option<StructID> {
|
||||
ctx.ident_stack.push();
|
||||
let gmap: Vec<_> = self.generics.iter().flat_map(|a| a.lower(ctx)).collect();
|
||||
let gargs = gmap.iter().map(|(_, id)| *id).collect();
|
||||
let fields = match &self.fields {
|
||||
PStructFields::Named(nodes) => nodes
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let tynode = def.ty.as_ref()?;
|
||||
let ty = tynode.lower(ctx);
|
||||
Some((name, ty))
|
||||
})
|
||||
.collect(),
|
||||
PStructFields::Tuple(nodes) => nodes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, n)| {
|
||||
let ty = n.as_ref()?.lower(ctx, span);
|
||||
Some((format!("{i}"), ty))
|
||||
})
|
||||
.collect(),
|
||||
PStructFields::None => vec![],
|
||||
}
|
||||
.into_iter()
|
||||
.map(|(name, ty)| (name, StructField { ty }))
|
||||
.collect();
|
||||
let name = self.name.as_ref()?.to_string();
|
||||
ctx.ident_stack.pop();
|
||||
Some(ctx.def_struct(UStruct {
|
||||
name,
|
||||
gargs,
|
||||
fields,
|
||||
origin: span,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
use crate::{
|
||||
ir::{GenericID, MemberIdent, MemberPath, Type, TypeID, UGeneric, UProgram},
|
||||
parser::PGenericDef,
|
||||
};
|
||||
|
||||
use super::{FileSpan, ModuleLowerCtx, Node, PType};
|
||||
|
||||
impl Node<Box<PType>> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
|
||||
self.as_ref()
|
||||
.map(|t| t.lower(ctx, self.origin))
|
||||
.unwrap_or(ctx.tc.error)
|
||||
}
|
||||
}
|
||||
impl Node<PType> {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
|
||||
self.as_ref()
|
||||
.map(|t| t.lower(ctx, self.origin))
|
||||
.unwrap_or(ctx.tc.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl PType {
|
||||
pub fn lower(&self, ctx: &mut ModuleLowerCtx, mut origin: FileSpan) -> TypeID {
|
||||
let mut ty = self;
|
||||
let mut path = Vec::new();
|
||||
while let PType::Member(node, ident) = ty {
|
||||
ty = if let Some(t) = node.as_ref() {
|
||||
let Some(name) = ident.as_ref() else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
origin = node.origin;
|
||||
path.push(MemberIdent {
|
||||
name: name.0.clone(),
|
||||
origin: ident.origin,
|
||||
});
|
||||
&**t
|
||||
} else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
}
|
||||
if !path.is_empty() {
|
||||
let PType::Ident(id) = ty else {
|
||||
return ctx.tc.error;
|
||||
};
|
||||
path.push(MemberIdent {
|
||||
name: id.0.clone(),
|
||||
origin,
|
||||
});
|
||||
path.reverse();
|
||||
let ty = Type::Unres(MemberPath {
|
||||
id: ctx.module,
|
||||
path,
|
||||
});
|
||||
return ctx.def_ty(ty);
|
||||
}
|
||||
let ty = match ty {
|
||||
PType::Member(_, _) => unreachable!(),
|
||||
PType::Ident(node) => {
|
||||
path.push(MemberIdent {
|
||||
name: node.0.clone(),
|
||||
origin,
|
||||
});
|
||||
path.reverse();
|
||||
Type::Unres(MemberPath {
|
||||
id: ctx.module,
|
||||
path,
|
||||
})
|
||||
}
|
||||
PType::Ref(node) => node.lower(ctx).rf(),
|
||||
PType::Generic(node, nodes) => todo!(),
|
||||
};
|
||||
ctx.def_ty(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<PGenericDef> {
|
||||
pub fn lower(&self, p: &mut UProgram) -> Option<(String, GenericID)> {
|
||||
let s = self.as_ref()?;
|
||||
let name = s.name.as_ref()?.to_string();
|
||||
Some((
|
||||
name.clone(),
|
||||
p.def_generic(UGeneric {
|
||||
name,
|
||||
origin: self.origin,
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
mod ctx;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod lower;
|
||||
mod node;
|
||||
mod nodes;
|
||||
mod parse;
|
||||
mod token;
|
||||
mod import;
|
||||
|
||||
use crate::common::{CompilerMsg, CompilerOutput, FileSpan, FilePos};
|
||||
pub use ctx::*;
|
||||
pub use cursor::*;
|
||||
pub use node::*;
|
||||
pub use nodes::*;
|
||||
pub use parse::*;
|
||||
pub use token::*;
|
||||
pub use import::*;
|
||||
|
||||
// idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct
|
||||
// creation, etc. instead of specializing at the parsing level
|
||||
@@ -1,54 +0,0 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::ir::Origin;
|
||||
|
||||
pub struct Node<T> {
|
||||
pub inner: Option<T>,
|
||||
pub origin: Origin,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub fn new(inner: T, span: Origin) -> Self {
|
||||
Self {
|
||||
inner: Some(inner),
|
||||
origin: span,
|
||||
}
|
||||
}
|
||||
pub fn bx(self) -> Node<Box<T>> {
|
||||
Node {
|
||||
inner: self.inner.map(|v| Box::new(v)),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
pub fn map<T2, F: Fn(T) -> T2>(self, f: F) -> Node<T2> {
|
||||
Node {
|
||||
inner: self.inner.map(f),
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Node<T> {
|
||||
type Target = Option<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Node<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Node<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.inner {
|
||||
Some(v) => v.fmt(f),
|
||||
None => f.write_str("{error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, Node, PExpr, PIdent, PInstruction, Parsable, ParseResult, ParserCtx, Symbol,
|
||||
};
|
||||
|
||||
pub struct PAsmBlock {
|
||||
pub instructions: Vec<Node<PInstruction>>,
|
||||
pub args: Vec<Node<PUAsmBlockArg>>,
|
||||
}
|
||||
|
||||
pub enum PAsmBlockArg<R, V> {
|
||||
In { reg: R, var: V },
|
||||
Out { reg: R },
|
||||
}
|
||||
|
||||
pub type PUAsmBlockArg = PAsmBlockArg<Node<PIdent>, Node<PExpr>>;
|
||||
|
||||
impl Parsable for PAsmBlock {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let args = if ctx.expect_peek()?.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
parse_list(ctx, Symbol::CloseParen)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let mut instructions = Vec::new();
|
||||
while !ctx.expect_peek()?.is_symbol(Symbol::CloseCurly) {
|
||||
let res = ctx.parse();
|
||||
instructions.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_sym_on_line(Symbol::CloseCurly);
|
||||
}
|
||||
}
|
||||
ctx.expect_sym(Symbol::CloseCurly)?;
|
||||
ParseResult::Ok(Self { instructions, args })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PUAsmBlockArg {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let reg = ctx.parse::<PIdent>()?;
|
||||
ParseResult::Ok(if reg.inner.as_ref().is_some_and(|s| s.0 == "out") {
|
||||
ctx.expect_sym(Symbol::Equals)?;
|
||||
let reg = ctx.parse()?;
|
||||
Self::Out { reg }
|
||||
} else {
|
||||
ctx.expect_sym(Symbol::Equals)?;
|
||||
let var = ctx.parse()?;
|
||||
Self::In { reg, var }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PAsmBlock {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("asm {")?;
|
||||
for i in &self.instructions {
|
||||
f.write_str("\n ")?;
|
||||
i.fmt(f)?;
|
||||
}
|
||||
if !self.instructions.is_empty() {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
f.write_str("}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, PAsmBlock, PIdent, Keyword, Node, Parsable, ParseResult, ParserCtx, Symbol, PType, PVarDef,
|
||||
};
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub struct AsmFunctionHeader {
|
||||
// pub name: Node<Ident>,
|
||||
// pub args: Vec<Node<AsmVarDef>>,
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct AsmFunction {
|
||||
// pub header: Node<AsmFunctionHeader>,
|
||||
// pub body: Node<AsmBlock>,
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmFunctionHeader {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// ctx.expect_kw(Keyword::Asm)?;
|
||||
// ctx.expect_kw(Keyword::Fn)?;
|
||||
// let name = ctx.parse()?;
|
||||
// ctx.expect_sym(Symbol::OpenParen)?;
|
||||
// let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
// ParseResult::Ok(Self { name, args })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmFunction {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// let header = ctx.parse()?;
|
||||
// let body = ctx.parse()?;
|
||||
// ParseResult::Ok(Self { header, body })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub struct AsmVarDef {
|
||||
// pub reg: Node<Ident>,
|
||||
// pub name: Node<Ident>,
|
||||
// pub ty: Option<Node<Type>>,
|
||||
// }
|
||||
//
|
||||
// impl Parsable for AsmVarDef {
|
||||
// fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
// let reg = ctx.parse()?;
|
||||
// let name = ctx.parse()?;
|
||||
// if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
// ctx.next();
|
||||
// ctx.parse().map(|ty| Self { reg, name, ty: Some(ty) })
|
||||
// } else {
|
||||
// ParseResult::Ok(Self { reg, name, ty: None })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl std::fmt::Debug for AsmVarDef {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// self.name.fmt(f)?;
|
||||
// if let Some(ty) = &self.ty {
|
||||
// write!(f, ": {:?}", ty)?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
@@ -1,94 +0,0 @@
|
||||
use super::{CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol};
|
||||
|
||||
pub struct PInstruction {
|
||||
pub op: Node<PIdent>,
|
||||
pub args: Vec<Node<PAsmArg>>,
|
||||
}
|
||||
|
||||
pub enum PAsmArg {
|
||||
Value(PIdent),
|
||||
Ref(Node<PIdent>),
|
||||
}
|
||||
|
||||
impl Parsable for PInstruction {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let op = ctx.parse()?;
|
||||
let mut args = Vec::new();
|
||||
if !ctx.next_on_new_line() {
|
||||
let arg = ctx.parse()?;
|
||||
args.push(arg);
|
||||
loop {
|
||||
if ctx.next_on_new_line() {
|
||||
break;
|
||||
}
|
||||
ctx.expect_sym(Symbol::Comma)?;
|
||||
let arg = ctx.parse()?;
|
||||
args.push(arg);
|
||||
}
|
||||
}
|
||||
ParseResult::Ok(Self { op, args })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PAsmArg {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
if let Some(ident) = ctx.maybe_parse() {
|
||||
return ParseResult::Wrap(ident.map(Self::Value));
|
||||
}
|
||||
|
||||
let mut next = ctx.expect_peek()?;
|
||||
if next.is_symbol(Symbol::Minus) {
|
||||
ctx.next();
|
||||
if let Some(mut ident) = ctx.maybe_parse::<PIdent>() {
|
||||
// TODO: this is so messed up
|
||||
if let Some(i) = ident.node.as_mut() {
|
||||
i.0.insert(0, '-')
|
||||
}
|
||||
return ParseResult::Wrap(ident.map(Self::Value));
|
||||
}
|
||||
next = ctx.expect_peek()?;
|
||||
}
|
||||
|
||||
if !next.is_symbol(Symbol::OpenCurly) {
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(
|
||||
next,
|
||||
"An identifier or {identifier}",
|
||||
));
|
||||
}
|
||||
ctx.next();
|
||||
|
||||
let res = ctx.parse();
|
||||
if res.recover {
|
||||
ctx.seek_sym(Symbol::CloseCurly);
|
||||
return ParseResult::SubErr;
|
||||
}
|
||||
|
||||
ctx.expect_sym(Symbol::CloseCurly)?;
|
||||
ParseResult::Ok(Self::Ref(res.node))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PAsmArg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Value(v) => v.fmt(f),
|
||||
Self::Ref(r) => write!(f, "{{{:?}}}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PInstruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.op.fmt(f)?;
|
||||
let mut iter = self.args.iter();
|
||||
if let Some(arg) = iter.next() {
|
||||
f.write_str(" ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
for arg in iter {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use super::{
|
||||
token::Symbol, CompilerMsg, Node, NodeParsable, PStatementLike, ParseResult, ParserCtx,
|
||||
};
|
||||
use crate::{
|
||||
parser::{ParsableWith, TokenInstance},
|
||||
util::Padder,
|
||||
};
|
||||
|
||||
pub struct PBlock {
|
||||
pub statements: Vec<Node<PStatementLike>>,
|
||||
pub ret_last: bool,
|
||||
}
|
||||
|
||||
impl ParsableWith for PBlock {
|
||||
type Data = Option<Symbol>;
|
||||
fn parse(ctx: &mut ParserCtx, end: Option<Symbol>) -> ParseResult<Self> {
|
||||
let mut statements = Vec::new();
|
||||
let is_end = |t: &TokenInstance| -> bool { end.map(|e| t.is_symbol(e)).unwrap_or(false) };
|
||||
if ctx.peek().is_none_or(is_end) {
|
||||
ctx.next();
|
||||
return ParseResult::Ok(Self {
|
||||
statements,
|
||||
ret_last: false,
|
||||
});
|
||||
}
|
||||
let mut expect_semi = false;
|
||||
let mut recover = false;
|
||||
loop {
|
||||
let Some(next) = ctx.peek() else {
|
||||
if end.is_some() {
|
||||
recover = true;
|
||||
ctx.err(CompilerMsg::unexpected_end());
|
||||
}
|
||||
break;
|
||||
};
|
||||
if is_end(next) {
|
||||
ctx.next();
|
||||
break;
|
||||
}
|
||||
if next.is_symbol(Symbol::Semicolon) {
|
||||
ctx.next();
|
||||
expect_semi = false;
|
||||
continue;
|
||||
} else if expect_semi {
|
||||
ctx.err(CompilerMsg {
|
||||
msg: "expected ';'".to_string(),
|
||||
spans: vec![ctx.next_start().char_span()],
|
||||
});
|
||||
}
|
||||
let res = PStatementLike::parse_node(ctx);
|
||||
expect_semi = res
|
||||
.node
|
||||
.as_ref()
|
||||
.is_some_and(|s| matches!(s, PStatementLike::Statement(..)));
|
||||
statements.push(res.node);
|
||||
if res.recover {
|
||||
ctx.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]);
|
||||
if ctx.peek().is_none() {
|
||||
recover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseResult::from_recover(
|
||||
Self {
|
||||
statements,
|
||||
ret_last: expect_semi,
|
||||
},
|
||||
recover,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PBlock {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if !self.statements.is_empty() {
|
||||
f.write_str("{\n ")?;
|
||||
let mut padder = Padder::new(f);
|
||||
let mut end = self.statements.len();
|
||||
if self.ret_last {
|
||||
end -= 1;
|
||||
}
|
||||
for i in 0..end {
|
||||
let s = &self.statements[i];
|
||||
// they don't expose wrap_buf :grief:
|
||||
padder.write_str(&format!("{s:?};\n"))?;
|
||||
}
|
||||
if self.ret_last
|
||||
&& let Some(s) = self.statements.last()
|
||||
{
|
||||
padder.write_str(&format!("{s:?}\n"))?;
|
||||
}
|
||||
f.write_char('}')?;
|
||||
} else {
|
||||
f.write_str("{}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{
|
||||
CompilerMsg, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx,
|
||||
Symbol, Token,
|
||||
};
|
||||
|
||||
pub struct PVarDef {
|
||||
pub name: Node<PIdent>,
|
||||
pub ty: Option<Node<PType>>,
|
||||
}
|
||||
|
||||
pub struct PFieldDef {
|
||||
pub name: Node<PIdent>,
|
||||
pub val: Option<Node<PExpr>>,
|
||||
}
|
||||
|
||||
impl Parsable for PVarDef {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
ctx.next();
|
||||
Self {
|
||||
name,
|
||||
ty: Some(ctx.parse()?),
|
||||
}
|
||||
} else {
|
||||
Self { name, ty: None }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PFieldDef {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) {
|
||||
ctx.next();
|
||||
Self {
|
||||
name,
|
||||
val: Some(ctx.parse()?),
|
||||
}
|
||||
} else {
|
||||
Self { name, val: None }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelfVar {
|
||||
pub ty: SelfType,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum SelfType {
|
||||
Ref,
|
||||
Take,
|
||||
}
|
||||
|
||||
// impl Parsable for Option<SelfVar> {
|
||||
// fn maybe_parse(ctx: &mut ParserCtx) -> Result<Option<Self>, CompilerMsg> {
|
||||
// if let Some(mut next) = ctx.peek() {
|
||||
// let mut ty = SelfType::Take;
|
||||
// if next.is_symbol(Symbol::Ampersand) {
|
||||
// ctx.next();
|
||||
// ty = SelfType::Ref;
|
||||
// next = ctx.expect_peek()?;
|
||||
// }
|
||||
// if let Token::Word(name) = &next.token {
|
||||
// if name == "self" {
|
||||
// ctx.next();
|
||||
// return Ok(Some(Self { ty }));
|
||||
// }
|
||||
// }
|
||||
// if ty != SelfType::Take {
|
||||
// return Err(CompilerMsg::unexpected_token(next, "self"));
|
||||
// }
|
||||
// }
|
||||
// Ok(None)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Debug for PVarDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.name.fmt(f)?;
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, ": {:?}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PFieldDef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.name.fmt(f)?;
|
||||
if let Some(val) = &self.val {
|
||||
write!(f, ": {:?}", val)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SelfVar {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self.ty {
|
||||
SelfType::Ref => "&self",
|
||||
SelfType::Take => "self",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use crate::{common::FilePos, ir::MemberTy, parser::NodeParsableWith};
|
||||
|
||||
use super::{
|
||||
op::{InfixOp, PostfixOp},
|
||||
util::parse_list,
|
||||
CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, PType, Parsable,
|
||||
ParseResult, ParserCtx, Symbol,
|
||||
};
|
||||
|
||||
type BoxNode = Node<Box<PExpr>>;
|
||||
|
||||
pub enum PExpr {
|
||||
Lit(PLiteral),
|
||||
Ident(Node<PIdent>),
|
||||
BinaryOp(InfixOp, BoxNode, BoxNode),
|
||||
PostfixOp(BoxNode, PostfixOp),
|
||||
Block(Node<PBlock>),
|
||||
Call(BoxNode, Vec<Node<PExpr>>),
|
||||
Group(BoxNode),
|
||||
Member(BoxNode, MemberTy, Node<PIdent>),
|
||||
Generic(BoxNode, Vec<Node<PType>>),
|
||||
AsmBlock(Node<PAsmBlock>),
|
||||
Construct(BoxNode, Node<PMap>),
|
||||
If(BoxNode, BoxNode),
|
||||
Loop(BoxNode),
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl Parsable for PExpr {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let start = ctx.next_start();
|
||||
let mut e1 = Self::parse_unit_postfix(ctx)?;
|
||||
while let Some(op) = ctx
|
||||
.peek()
|
||||
.map(|next| InfixOp::from_token(&next.token))
|
||||
.flatten()
|
||||
{
|
||||
let span = start.to(ctx.prev_end());
|
||||
ctx.next();
|
||||
let n2 = ctx.parse()?.bx();
|
||||
let (n1, op, n2) = fix_precedence(Node::new(e1, span).bx(), op, n2, start);
|
||||
e1 = Self::BinaryOp(op, n1, n2);
|
||||
}
|
||||
return ParseResult::Ok(e1);
|
||||
}
|
||||
}
|
||||
|
||||
impl PExpr {
|
||||
fn parse_unit_postfix(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let start = ctx.next_start();
|
||||
// first get unit
|
||||
let mut e1 = Self::parse_unit(ctx)?;
|
||||
// then apply post ops
|
||||
loop {
|
||||
let span = start.to(ctx.prev_end());
|
||||
let Some(next) = ctx.peek() else {
|
||||
break;
|
||||
};
|
||||
if next.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
e1 = Self::Call(Node::new(e1, span).bx(), args);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
ctx.next();
|
||||
let map = ctx.parse()?;
|
||||
e1 = Self::Construct(Node::new(e1, span).bx(), map);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::Dot) {
|
||||
ctx.next();
|
||||
let field = ctx.parse()?;
|
||||
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Field, field);
|
||||
continue;
|
||||
} else if next.is_symbol(Symbol::DoubleColon) {
|
||||
ctx.next();
|
||||
if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::OpenAngle)) {
|
||||
ctx.next();
|
||||
let gargs = parse_list(ctx, Symbol::CloseAngle)?;
|
||||
e1 = Self::Generic(Node::new(e1, span).bx(), gargs);
|
||||
} else {
|
||||
let field = ctx.parse()?;
|
||||
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Member, field);
|
||||
}
|
||||
continue;
|
||||
} else if let Some(op) = PostfixOp::from_token(next) {
|
||||
ctx.next();
|
||||
e1 = Self::PostfixOp(Node::new(e1, span).bx(), op);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ParseResult::Ok(e1);
|
||||
}
|
||||
fn parse_unit(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let next = ctx.expect_peek()?;
|
||||
return ParseResult::Ok(if next.is_symbol(Symbol::OpenParen) {
|
||||
ctx.next();
|
||||
if ctx.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||
ctx.next();
|
||||
return ParseResult::Ok(PExpr::Lit(PLiteral::Unit));
|
||||
}
|
||||
let res = ctx.parse();
|
||||
if res.recover {
|
||||
ctx.seek_sym(Symbol::CloseParen);
|
||||
}
|
||||
ctx.expect_sym(Symbol::CloseParen)?;
|
||||
Self::Group(res.node.bx())
|
||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||
ctx.next();
|
||||
Self::Block(PBlock::parse_node(ctx, Some(Symbol::CloseCurly))?)
|
||||
} else if next.is_keyword(Keyword::If) {
|
||||
ctx.next();
|
||||
let cond = ctx.parse()?.bx();
|
||||
let body = ctx.parse()?.bx();
|
||||
Self::If(cond, body)
|
||||
} else if next.is_keyword(Keyword::Loop) {
|
||||
ctx.next();
|
||||
let body = ctx.parse()?.bx();
|
||||
Self::Loop(body)
|
||||
} else if next.is_keyword(Keyword::Break) {
|
||||
ctx.next();
|
||||
Self::Break
|
||||
} else if next.is_keyword(Keyword::Continue) {
|
||||
ctx.next();
|
||||
Self::Continue
|
||||
} else if next.is_keyword(Keyword::Asm) {
|
||||
ctx.next();
|
||||
Self::AsmBlock(ctx.parse()?)
|
||||
} else if let Some(res) = ctx.maybe_parse::<PLiteral>() {
|
||||
return ParseResult::Wrap(res.map(Self::Lit));
|
||||
} else {
|
||||
let res = ctx.parse();
|
||||
if res.node.is_some() {
|
||||
Self::Ident(res.node)
|
||||
} else {
|
||||
let next = ctx.expect_peek()?;
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an expression"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fix_precedence(
|
||||
mut n1: BoxNode,
|
||||
mut op: InfixOp,
|
||||
mut n2: BoxNode,
|
||||
start: FilePos,
|
||||
) -> (BoxNode, InfixOp, BoxNode) {
|
||||
if let Some(box PExpr::BinaryOp(op2, _, _)) = n2.as_ref() {
|
||||
if op.precedence() > op2.precedence() {
|
||||
let Some(box PExpr::BinaryOp(op2, n21, n22)) = n2.inner else {
|
||||
unreachable!();
|
||||
};
|
||||
let span = start.to(n21.origin.end);
|
||||
let (n11, op1, n12) = fix_precedence(n1, op, n21, start);
|
||||
n1 = Node::new(PExpr::BinaryOp(op1, n11, n12), span).bx();
|
||||
op = op2;
|
||||
n2 = n22;
|
||||
}
|
||||
}
|
||||
(n1, op, n2)
|
||||
}
|
||||
|
||||
impl Debug for PExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PExpr::Lit(c) => c.fmt(f)?,
|
||||
PExpr::Ident(n) => n.fmt(f)?,
|
||||
PExpr::Block(b) => b.fmt(f)?,
|
||||
PExpr::BinaryOp(op, e1, e2) => {
|
||||
write!(f, "({:?}", *e1)?;
|
||||
if op.pad() {
|
||||
write!(f, " {} ", op.str())?;
|
||||
} else {
|
||||
write!(f, "{}", op.str())?;
|
||||
}
|
||||
write!(f, "{:?})", *e2)?;
|
||||
}
|
||||
PExpr::Call(n, args) => {
|
||||
n.fmt(f)?;
|
||||
f.write_char('(')?;
|
||||
if let Some(a) = args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_char(')')?;
|
||||
}
|
||||
PExpr::PostfixOp(e, op) => write!(f, "({:?}{})", e, op.str())?,
|
||||
PExpr::Group(inner) => inner.fmt(f)?,
|
||||
PExpr::AsmBlock(inner) => inner.fmt(f)?,
|
||||
PExpr::Construct(node, inner) => write!(f, "{:?}{:?}", node, inner)?,
|
||||
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
|
||||
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
|
||||
PExpr::Break => write!(f, "break")?,
|
||||
PExpr::Continue => write!(f, "continue")?,
|
||||
PExpr::Member(e1, ty, name) => write!(f, "{:?}{}{:?}", e1, ty.sep(), name)?,
|
||||
PExpr::Generic(e1, gargs) => write!(f, "{:?}<{:?}>", e1, gargs)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use super::{
|
||||
util::parse_list, Node, PBlock, PGenericDef, PIdent, PType, PVarDef, Parsable, ParseResult,
|
||||
ParserCtx, Symbol,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct PFunctionHeader {
|
||||
pub name: Node<PIdent>,
|
||||
pub args: Vec<Node<PVarDef>>,
|
||||
pub gargs: Vec<Node<PGenericDef>>,
|
||||
pub ret: Option<Node<PType>>,
|
||||
}
|
||||
|
||||
pub struct PFunction {
|
||||
pub header: Node<PFunctionHeader>,
|
||||
pub body: Node<PBlock>,
|
||||
}
|
||||
|
||||
impl Parsable for PFunctionHeader {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let name = ctx.parse()?;
|
||||
let generic_args = if ctx.expect_peek()?.is_symbol(Symbol::OpenAngle) {
|
||||
ctx.next();
|
||||
parse_list(ctx, Symbol::CloseAngle)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
ctx.expect_sym(Symbol::OpenParen)?;
|
||||
// let sel = ctx.maybe_parse();
|
||||
// if sel.is_some() {
|
||||
// if let Err(err) = ctx.expect_sym(Symbol::Comma) {
|
||||
// ctx.err(err);
|
||||
// ctx.seek_syms(&[Symbol::Comma, Symbol::CloseParen]);
|
||||
// if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Comma)) {
|
||||
// ctx.next();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
let args = parse_list(ctx, Symbol::CloseParen)?;
|
||||
let ret = if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Arrow)) {
|
||||
ctx.next();
|
||||
Some(ctx.parse()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ParseResult::Ok(Self {
|
||||
name,
|
||||
args,
|
||||
gargs: generic_args,
|
||||
ret,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for PFunction {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let header = ctx.parse()?;
|
||||
ctx.expect_sym(Symbol::OpenCurly)?;
|
||||
let body = ctx.parse_with(Some(Symbol::CloseCurly))?;
|
||||
ParseResult::Ok(Self { header, body })
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PFunctionHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("fn ")?;
|
||||
self.name.fmt(f)?;
|
||||
f.write_str("(")?;
|
||||
// if let Some(s) = &self.sel {
|
||||
// s.fmt(f)?;
|
||||
// if self.args.first().is_some() {
|
||||
// f.write_str(", ")?;
|
||||
// }
|
||||
// }
|
||||
if let Some(a) = self.args.first() {
|
||||
a.fmt(f)?;
|
||||
}
|
||||
for arg in self.args.iter().skip(1) {
|
||||
f.write_str(", ")?;
|
||||
arg.fmt(f)?;
|
||||
}
|
||||
f.write_str(")")?;
|
||||
if let Some(ret) = &self.ret {
|
||||
write!(f, " -> {:?}", ret)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Debug for PFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.header.fmt(f)?;
|
||||
f.write_str(" ")?;
|
||||
self.body.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::{CompilerMsg, Parsable, ParseResult, ParserCtx, Token};
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PIdent(pub String);
|
||||
|
||||
impl Parsable for PIdent {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let next = ctx.expect_peek()?;
|
||||
let Token::Word(name) = &next.token else {
|
||||
return ParseResult::Err(CompilerMsg::unexpected_token(next, "an identifier"));
|
||||
};
|
||||
let name = name.to_string();
|
||||
ctx.next();
|
||||
ParseResult::Ok(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsable for Option<PIdent> {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let Some(next) = ctx.peek() else {
|
||||
return ParseResult::Ok(None);
|
||||
};
|
||||
let Token::Word(name) = &next.token else {
|
||||
return ParseResult::Ok(None);
|
||||
};
|
||||
let name = name.to_string();
|
||||
ctx.next();
|
||||
ParseResult::Ok(Some(PIdent(name)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PIdent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PIdent {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PIdent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use crate::parser::{Parsable, ParseResult};
|
||||
|
||||
use super::{PString, ParserCtx, Symbol, Token};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum PLiteral {
|
||||
String(String),
|
||||
Char(char),
|
||||
Number(PNumber),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct PNumber {
|
||||
pub whole: String,
|
||||
pub decimal: Option<String>,
|
||||
pub ty: Option<String>,
|
||||
}
|
||||
|
||||
impl Parsable for Option<PLiteral> {
|
||||
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
|
||||
let inst = ctx.expect_peek()?;
|
||||
ParseResult::Ok(Some(match &inst.token {
|
||||
Token::Symbol(Symbol::SingleQuote) => {
|
||||
let chars = ctx.chars();
|
||||
let c = chars.expect_next()?;
|
||||
chars.expect('\'')?;
|
||||
ctx.next();
|
||||
PLiteral::Char(c)
|
||||
}
|
||||
Token::Symbol(Symbol::DoubleQuote) => {
|
||||
ctx.next();
|
||||
let s = ctx.parse::<PString>()?;
|
||||
return match s.inner {
|
||||
Some(s) => ParseResult::Ok(Some(PLiteral::String(s.0))),
|
||||
None => ParseResult::SubErr,
|
||||
};
|
||||
}
|
||||
Token::Word(text) => {
|
||||
let first = text.chars().next().unwrap();
|
||||
if !first.is_ascii_digit() {
|
||||
return ParseResult::Ok(None);
|
||||
}
|
||||
let (whole, ty) = parse_whole_num(text);
|
||||
let mut num = PNumber {
|
||||
whole,
|
||||
decimal: None,
|
||||
ty,
|
||||
};
|
||||
ctx.next();
|
||||
if num.ty.is_none() && ctx.peek().is_some_and(|i| i.is_symbol(Symbol::Dot)) {
|
||||
ctx.next();
|
||||
if let Some(next) = ctx.peek() {
|
||||
if let Token::Word(i) = &next.token {
|
||||
if i.chars().next().unwrap().is_ascii_digit() {
|
||||
let (decimal, ty) = parse_whole_num(i);
|
||||
num.decimal = Some(decimal);
|
||||
num.ty = ty;
|
||||
ctx.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PLiteral::Number(num)
|
||||
}
|
||||
_ => return ParseResult::Ok(None),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_whole_num(text: &str) -> (String, Option<String>) {
|
||||
let mut whole = String::new();
|
||||
let mut ty = String::new();
|
||||
for c in text.chars() {
|
||||
if ty.is_empty() {
|
||||
if c.is_ascii_digit() {
|
||||
whole.push(c);
|
||||
} else if c != '_' {
|
||||
ty.push(c);
|
||||
}
|
||||
} else {
|
||||
ty.push(c);
|
||||
}
|
||||
}
|
||||
(whole, if ty.is_empty() { None } else { Some(ty) })
|
||||
}
|
||||
|
||||
impl Debug for PLiteral {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(str) => str.fmt(f),
|
||||
Self::Char(c) => c.fmt(f),
|
||||
Self::Number(n) => n.fmt(f),
|
||||
Self::Unit => f.write_str("()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PNumber {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.whole)?;
|
||||
if let Some(d) = &self.decimal {
|
||||
write!(f, ".{}", d)?;
|
||||
}
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, "_{}", ty)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user