M src/base.rs => src/base.rs +231 -0
@@ 1,5 1,6 @@
use crate::prelude::*;
use std::collections::HashSet;
+use std::fmt::{Display, Formatter, Result as FResult, Write as FWrite};
#[derive(Clone, Debug)]
pub enum GlobDef {
@@ 117,6 118,16 @@ pub enum Const {
F64(f64),
}
+impl Body {
+ pub fn get_stm(&self, StmRef(i): StmRef) -> &Stm {
+ &self.stm_arena[i as usize]
+ }
+
+ pub fn get_block(&self, BlockRef(i): BlockRef) -> &Block {
+ &self.block_arena[i as usize]
+ }
+}
+
macro_rules! impl_const_from {
($t:ty, $c:expr) => {
impl From<$t> for Const {
@@ 169,3 180,223 @@ impl_const_arithm_binop!(std::ops::Sub, sub);
impl_const_arithm_binop!(std::ops::Mul, mul);
impl_const_arithm_binop!(std::ops::Div, div);
impl_const_arithm_binop!(std::ops::Rem, rem);
+
+impl Display for BlockRef {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ write!(f, "b{}", self.0)
+ }
+}
+
+impl Display for StmRef {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ write!(f, "s{}", self.0)
+ }
+}
+
+impl Display for ParamRef {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ write!(f, "p{}", self.0)
+ }
+}
+
+impl Display for Binop {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ use Binop::*;
+ write!(
+ f,
+ "{}",
+ match *self {
+ Add => "+",
+ Sub => "-",
+ Mul => "*",
+ Quot => "quot",
+ Rem => "rem",
+ Lt => "<",
+ Gt => ">",
+ Leq => "<=",
+ Geq => ">=",
+ Eq => "=",
+ Neq => "/=",
+ }
+ )
+ }
+}
+
+impl Display for Operand {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ match *self {
+ Operand::Const(x) => write!(f, "{}", x),
+ Operand::Var(x) => write!(f, "{}", x),
+ Operand::Param(x) => write!(f, "{}", x),
+ Operand::Global(x) => write!(f, "g{}", x),
+ }
+ }
+}
+
+impl Display for Const {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ use Const::*;
+ match *self {
+ Bool(x) => write!(f, "{}", x),
+ I8(x) => write!(f, "{}i8", x),
+ I16(x) => write!(f, "{}i16", x),
+ I32(x) => write!(f, "{}i32", x),
+ I64(x) => write!(f, "{}i64", x),
+ Int(x) => write!(f, "{}i", x),
+ N8(x) => write!(f, "{}n8", x),
+ N16(x) => write!(f, "{}n16", x),
+ N32(x) => write!(f, "{}n32", x),
+ N64(x) => write!(f, "{}n64", x),
+ Nat(x) => write!(f, "{}n", x),
+ F64(x) => write!(f, "{}f64", x),
+ }
+ }
+}
+
+impl Pretty for (DefId, &GlobDef) {
+ type LocalCtx = ();
+ fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
+ writeln!(pr, ";; {}", pr.cache.get_def_name(self.0))?;
+ match self.1 {
+ GlobDef::FunDef(d) => (self.0, d).pretty(pr),
+ GlobDef::VarDef(d) => (self.0, d).pretty(pr),
+ }
+ }
+}
+
+impl Pretty for (DefId, &FunDef) {
+ type LocalCtx = ();
+ fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
+ let id = self.0;
+ let def = self.1;
+ write!(pr, "(defun g{id} [")?;
+ pr.write_sep(def.params.iter().map(|_| "_"), " ")?;
+ pr.write_str("]")?;
+ pr.newline_indent()?;
+ pr.add_indent(2, |pr| {
+ write!(pr, ":children [")?;
+ pr.write_sep(&self.1.children, " ")?;
+ write!(pr, "]")?;
+ pr.newline_indent()?;
+ self.1.body.pretty(pr)?;
+ pr.write_char(')')
+ })
+ }
+}
+
+impl Pretty for (DefId, &GVarDef) {
+ type LocalCtx = ();
+ fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
+ let id = self.0;
+ let def = self.1;
+ write!(pr, "(define g{id}")?;
+ pr.newline_indent()?;
+ pr.add_indent(2, |pr| {
+ write!(pr, ":children [")?;
+ pr.write_sep(&def.children, " ")?;
+ write!(pr, "]")?;
+ pr.newline_indent()?;
+ def.body.pretty(pr)?;
+ pr.write_char(')')
+ })
+ }
+}
+
+impl Pretty for Body {
+ type LocalCtx = ();
+ fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
+ write!(pr, ":root {}", self.root)?;
+ pr.newline_indent()?;
+
+ pr.with_local_ctx(self, |pr| {
+ let mut it = self.block_arena.iter().enumerate();
+ if let Some((i, b)) = it.next() {
+ (BlockRef(i as u32), b).pretty(pr)?;
+ for (i, b) in it {
+ pr.newline_indent()?;
+ (BlockRef(i as u32), b).pretty(pr)?;
+ }
+ }
+ Ok(())
+ })
+ }
+}
+
+impl Pretty for (BlockRef, &Block) {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ write!(pr, "(block {}", self.0)?;
+ pr.newline_indent()?;
+ pr.add_indent(2, |pr| {
+ for sref in self.1.stms.iter().cloned() {
+ (sref, pr.local_ctx.get_stm(sref)).pretty(pr)?;
+ pr.newline_indent()?;
+ }
+ self.1.tail.pretty(pr)?;
+ pr.write_char(')')
+ })
+ }
+}
+
+impl Pretty for (StmRef, &Stm) {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ write!(pr, "[{} ", self.0)?;
+ self.1.pretty(pr)?;
+ pr.write_char(']')
+ }
+}
+
+impl Pretty for Stm {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ match self {
+ Stm::Let { rhs } => {
+ pr.write_str("(let ")?;
+ rhs.pretty(pr)?;
+ pr.write_char(')')
+ }
+ }
+ }
+}
+
+impl Pretty for Flow {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ match self {
+ Flow::Produce(ration) => ration.pretty(pr),
+ Flow::Diverge(div) => div.pretty(pr),
+ }
+ }
+}
+
+impl Pretty for Operation {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ match self {
+ Operation::Binop(op, a, b) => write!(pr, "({op} {a} {b})"),
+ Operation::Call(f, args) => {
+ write!(pr, "(call {f} [")?;
+ pr.write_sep(args, " ")?;
+ pr.write_str("])")
+ }
+ Operation::Wrap(rand) => rand.pretty(pr),
+ }
+ }
+}
+
+impl Pretty for Operand {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ write!(pr, "{}", self)
+ }
+}
+
+impl Pretty for Diverge {
+ type LocalCtx = Body;
+ fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
+ match self {
+ Diverge::If(pred, conseq, alt) => write!(pr, "(if {pred} {conseq} {alt})"),
+ }
+ }
+}
M src/cache.rs => src/cache.rs +11 -7
@@ 149,7 149,7 @@ impl Cache {
} else {
let module_id =
*self.parent_modules.get(&def_id).expect("ice: if there's a def id, there should be a parent module");
- let def_ident = self.fetch_def_name(def_id).last().clone();
+ let def_ident = self.get_def_name(def_id).last().clone();
let module = self.fetch_parsed_module(module_id)?;
let parsed = module[&def_ident].clone();
let resolved = resolve::resolve_def(self, module_id, &parsed.1)?;
@@ 266,7 266,7 @@ impl Cache {
fn gen_def_resolution(&mut self, def_ident: String, module_id: ModuleId) -> DefId {
debug_assert!(self.fetch_parsed_module(module_id).unwrap().contains_key(&def_ident));
- let def_name = self.fetch_module_name(module_id).clone().with(def_ident);
+ let def_name = self.get_module_name(module_id).clone().with(def_ident);
match self.resolved_names.get(&def_name) {
Some(Res::Def(def_id)) => *def_id,
Some(res) => panic!("ice: fetching/generating definition resolution for name {def_name}, but that name already resolves to {res:?}"),
@@ 287,11 287,11 @@ impl Cache {
self.n_defs - 1
}
- pub fn fetch_def_name(&self, def_id: DefId) -> &FullName<String> {
+ pub fn get_def_name(&self, def_id: DefId) -> &FullName<String> {
self.resolved_names_rev.get(&Res::Def(def_id)).expect("ice: if we have a def id, there should be a reverse")
}
- pub fn fetch_module_name(&self, module_id: ModuleId) -> &FullName<String> {
+ pub fn get_module_name(&self, module_id: ModuleId) -> &FullName<String> {
self.resolved_names_rev
.get(&Res::Module(module_id))
.expect("ice: if we have a module id, there should be a reverse")
@@ 321,20 321,24 @@ impl Cache {
pub fn fetch_source(&mut self, file_id: FileId) -> Result<&str> {
if !self.sources.contains_key(&file_id) {
- let path = self.fetch_path(file_id);
+ let path = self.get_path(file_id);
let src = std::fs::read_to_string(path).unwrap();
self.sources.insert(file_id, src);
}
Ok(&self.sources[&file_id])
}
- pub fn fetch_module_file(&self, module_id: ModuleId) -> FileId {
+ pub fn get_module_file(&self, module_id: ModuleId) -> FileId {
*self.module_files.get(&module_id).expect("ice: we shouldn't have a module id without an associated file id")
}
- pub fn fetch_path(&self, file_id: FileId) -> &Path {
+ pub fn get_path(&self, file_id: FileId) -> &Path {
self.file_paths.get(&file_id).expect("ice: we shouldn't have a file id without an associated file path")
}
+
+ pub fn get_abaseds(&self) -> &HashMap<DefId, base::GlobDef> {
+ &self.abaseds
+ }
}
#[derive(Debug, Clone)]
M src/diag.rs => src/diag.rs +116 -13
@@ 2,7 2,7 @@ use crate::prelude::*;
use fem::Type as FType;
use resolve::Type as RType;
use std::collections::HashSet;
-use std::io::*;
+use std::fmt::{Debug, Display, Error as FError, Formatter, Result as FResult, Write as FWrite};
use std::path::{Path, PathBuf};
pub type Result<T> = std::result::Result<T, Error>;
@@ 99,7 99,7 @@ impl ResolveErr {
use ResolveErr::*;
match self {
UndefInMod(def, mid) => {
- let mname = cache.fetch_module_name(*mid);
+ let mname = cache.get_module_name(*mid);
def.loc.error(cache, format!("Couldn't find definition `{def}` in module `{mname}`."))
}
InvalidRootNamespace(ns) =>
@@ 109,7 109,7 @@ impl ResolveErr {
.error(cache, format!("Failed to resolve the name `{name}` to any existing module or definition.")),
ParentIsNotModule(loc, parent) =>
loc.error(cache, format!("The parent `{}` does not resolve to a module.", parent)),
- DupDef(loc) => loc.error(cache, format!("Duplicate definition of this name in this module.")),
+ DupDef(loc) => loc.error(cache, "Duplicate definition of this name in this module."),
}
}
}
@@ 123,7 123,6 @@ pub struct ParseErr {
impl ParseErr {
pub fn render(&self, cache: &mut Cache) -> String {
- use std::fmt::Write;
self.loc.error(
cache,
if self.expecteds.len() == 1 {
@@ 161,8 160,8 @@ impl LexErr {
}
}
-impl std::fmt::Debug for LexErr {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+impl Debug for LexErr {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
write!(f, "loc={:?}: expected {}, found {}", self.loc, self.expected, self.found)
}
}
@@ 175,8 174,8 @@ pub enum LexThing {
Anon,
}
-impl std::fmt::Display for LexThing {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+impl Display for LexThing {
+ fn fmt(&self, f: &mut Formatter) -> FResult {
match self {
LexThing::Char(c) => write!(f, "char {:?}", c),
LexThing::Descr(desc) => write!(f, "{}", desc),
@@ 205,19 204,19 @@ impl Loc {
pub fn with_offset(&self, offset: u32) -> Self {
use Loc::*;
match self {
- File { file, .. } | FileGeneral { file } => File { file: file.clone(), offset },
+ &File { file, .. } | &FileGeneral { file } => File { file, offset },
Anon { .. } | AnonGeneral => Anon { offset },
}
}
- pub fn error<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
+ pub fn error<M: Display>(&self, cache: &mut Cache, msg: M) -> String {
self.contextualize(cache, format!("error: {msg}"))
}
- pub fn contextualize<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
+ pub fn contextualize<M: Display>(&self, cache: &mut Cache, msg: M) -> String {
match self {
Loc::File { file, offset } => {
- let path = cache.fetch_path(*file).to_owned();
+ let path = cache.get_path(*file).to_owned();
let src = cache.fetch_source(*file).unwrap();
let (line_i, column_i, line_s) = line_and_column_at(src, *offset);
let gutter = " ".repeat(column_i.max(1).ilog10() as usize + 1);
@@ 226,7 225,7 @@ impl Loc {
format!("{path}:{line_i}:{column_i}: {msg}\n{gutter} |\n{column_i} | {line_s}\n{gutter} | {marker}^",)
}
Loc::FileGeneral { file } => {
- let path = cache.fetch_path(*file);
+ let path = cache.get_path(*file);
format!("{}: {msg}", path.to_string_lossy())
}
Loc::Anon { offset } => {
@@ 260,3 259,107 @@ fn line_and_column_at(mut s: &str, mut offset: u32) -> (u32, u32, &str) {
offset -= i as u32 + 1;
}
}
+
+pub struct DisplayPretty<'ca, 'ct, T, LocalCtx> {
+ pub this: T,
+ pub cache: &'ca Cache,
+ pub column: usize,
+ pub local_ctx: &'ct LocalCtx,
+}
+
+impl<'ca, 'ct, P, LocalCtx> Display for DisplayPretty<'ca, 'ct, P, LocalCtx>
+where
+ P: Pretty<LocalCtx = LocalCtx>,
+{
+ fn fmt(&self, f: &mut Formatter) -> FResult {
+ self.this.pretty(&mut Prettier { out: f, cache: self.cache, column0: self.column, local_ctx: self.local_ctx })
+ }
+}
+
+pub trait Pretty {
+ type LocalCtx;
+ fn pretty(&self, st: &mut Prettier<Self::LocalCtx>) -> FResult;
+}
+
+impl<P: Pretty> Pretty for &P {
+ type LocalCtx = P::LocalCtx;
+ fn pretty(&self, st: &mut Prettier<P::LocalCtx>) -> FResult {
+ (**self).pretty(st)
+ }
+}
+
+pub struct Prettier<'w, 'ca, 'ct, LocalCtx> {
+ pub out: &'w mut dyn FWrite,
+ pub cache: &'ca Cache,
+ column0: usize,
+ pub local_ctx: &'ct LocalCtx,
+}
+
+impl<'w, 'ca, 'ct, LocalCtx> Prettier<'w, 'ca, 'ct, LocalCtx> {
+ pub fn with_local_ctx<'ct2, LocalCtx2, A>(
+ &mut self,
+ ctx: &'ct2 LocalCtx2,
+ f: impl FnOnce(&mut Prettier<LocalCtx2>) -> std::result::Result<A, FError>,
+ ) -> std::result::Result<A, FError> {
+ f(&mut Prettier { out: self.out, cache: self.cache, column0: self.column0, local_ctx: ctx })
+ }
+
+ pub fn add_indent<A>(
+ &mut self,
+ columns: usize,
+ f: impl FnOnce(&mut Prettier<LocalCtx>) -> std::result::Result<A, FError>,
+ ) -> std::result::Result<A, FError> {
+ for _ in 0..columns {
+ self.write_char(' ')?;
+ }
+ self.at_added_indent(columns, f)
+ }
+
+ pub fn at_added_indent<A>(
+ &mut self,
+ columns: usize,
+ f: impl FnOnce(&mut Prettier<LocalCtx>) -> std::result::Result<A, FError>,
+ ) -> std::result::Result<A, FError> {
+ self.column0 += columns;
+ let y = f(self)?;
+ self.column0 -= columns;
+ Ok(y)
+ }
+
+ pub fn indent(&mut self) -> FResult {
+ for _ in 0..self.column0 {
+ self.write_char(' ')?;
+ }
+ Ok(())
+ }
+
+ pub fn newline_indent(&mut self) -> FResult {
+ self.write_char('\n')?;
+ self.indent()
+ }
+
+ pub fn write_sep(&mut self, xs: impl IntoIterator<Item = impl Display>, sep: &str) -> FResult {
+ let mut xs = xs.into_iter();
+ if let Some(x) = xs.next() {
+ write!(self, "{x}")?;
+ for x in xs {
+ write!(self, "{sep}{x}")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<'w, 'ca, 'ct, LocalCtx> FWrite for Prettier<'w, 'ca, 'ct, LocalCtx> {
+ fn write_str(&mut self, s: &str) -> FResult {
+ self.out.write_str(s)
+ }
+}
+
+/// Estimate how wide the gived string rendered as glyphs would be in monospace columns.
+/// This will never be 100% accurate for all systems, as each font can display a "character" however widely
+/// it wishes, and we don't limit users to e.g. the subset of latin characters & symbols.
+pub fn estim_display_width(s: &str) -> usize {
+ // TODO: Use library which looks at grapheme clusters & stuff to get more accurate estimation
+ s.chars().count()
+}
M src/eval.rs => src/eval.rs +4 -4
@@ 28,8 28,8 @@ impl<'c, 'd, 'a> EvalDef<'c, 'd, 'a> {
self.eval_block_ref(self.body.root)
}
- fn eval_block_ref(&mut self, BlockRef(i): BlockRef) -> Result<Operand> {
- self.eval_block(&self.body.block_arena[i as usize])
+ fn eval_block_ref(&mut self, bref: BlockRef) -> Result<Operand> {
+ self.eval_block(&self.body.get_block(bref))
}
fn eval_block(&mut self, block: &Block) -> Result<Operand> {
@@ 44,8 44,8 @@ impl<'c, 'd, 'a> EvalDef<'c, 'd, 'a> {
Ok(())
}
- fn eval_stm_ref(&mut self, lhs @ StmRef(i): StmRef) -> Result<()> {
- let stm = &self.body.stm_arena[i as usize];
+ fn eval_stm_ref(&mut self, lhs: StmRef) -> Result<()> {
+ let stm = &self.body.get_stm(lhs);
match stm {
Stm::Let { rhs } => {
let val = self.eval_div(rhs)?;
M src/main.rs => src/main.rs +7 -0
@@ 34,6 34,13 @@ fn main() {
if let Err(msg) = with_pretty_errors(&mut cache, |cache| {
let id = cache.fetch_global_resolved_name(&main_name)?.as_def().unwrap();
println!("result: {:?}", cache.fetch_evaluated(id)?);
+ let mut f = std::fs::File::create(".dbg.base").unwrap();
+ use std::io::Write;
+ let mut abaseds = cache.get_abaseds().iter().collect::<Vec<_>>();
+ abaseds.sort_by_key(|(k, _)| *k);
+ for def in abaseds.into_iter().map(|(k, v)| (*k, v)) {
+ writeln!(f, "{}\n", DisplayPretty { this: def, cache, column: 0, local_ctx: &() }).unwrap()
+ }
Ok(())
}) {
eprintln!("{msg}")
M src/resolve.rs => src/resolve.rs +1 -1
@@ 44,7 44,7 @@ struct Resolver<'c> {
impl<'c> Resolver<'c> {
fn new(cache: &'c mut Cache, module: ModuleId) -> Result<Self> {
let module_top_level = cache.fetch_module_local_resolved_names(module)?.clone();
- let file = cache.fetch_module_file(module);
+ let file = cache.get_module_file(module);
Ok(Self { cache, local_count: 0, scopes: vec![module_top_level], module, file })
}