Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sezna committed Jul 21, 2024
1 parent a898209 commit e834445
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 40 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion pete/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
};

use clap::Parser as ClapParser;
use error::PeteError;
use petr_api::*;
use petr_pkg::BuildPlan;
use petr_resolve::Dependency;
Expand All @@ -19,6 +20,8 @@ pub mod error {
TomlSeriatlize(#[from] toml::ser::Error),
#[error(transparent)]
Pkg(#[from] petr_pkg::error::PkgError),
#[error("Failed to lower code")]
FailedToLower,
}
}

Expand Down Expand Up @@ -222,7 +225,13 @@ pub fn compile(
timings.end("type check");

timings.start("lowering");
let lowerer: Lowerer = Lowerer::new(type_checker);
let lowerer: Lowerer = match Lowerer::new(type_checker) {
Ok(l) => l,
Err(e) => {
eprintln!("Failed to lower: {:?}", e);
return Err(PeteError::FailedToLower);
},
};
timings.end("lowering");

render_errors(parse_errs, &source_map);
Expand Down
2 changes: 1 addition & 1 deletion petr-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
};

pub use petr_fmt::{format_sources, Formattable, FormatterConfig, FormatterContext};
pub use petr_ir::Lowerer;
pub use petr_ir::{Lowerer, LoweringError};
pub use petr_parse::Parser;
#[cfg(not(feature = "no_std"))]
pub use petr_pkg::{manifest::find_manifest, BuildPlan};
Expand Down
8 changes: 7 additions & 1 deletion petr-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
petr-utils = { path = "../petr-utils" }
petr-utils = { path = "../petr-utils", optional = true }
petr-typecheck = { path = "../petr-typecheck" }
miette = { version = "5.10", features = ["fancy"] }
thiserror = "1.0.61"

[dev-dependencies]
petr-parse = { path = "../petr-parse" }
expect-test = "1.5.0"
petr-resolve = { path = "../petr-resolve" }

[features]
debug = ["petr-utils/debug"]
default = ["dep:petr-utils"]
10 changes: 8 additions & 2 deletions petr-ir/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
#[derive(Debug)]
pub struct LoweringError;
use miette::Diagnostic;
use thiserror::Error;

#[derive(Debug, Error, Diagnostic, Clone)]
pub enum LoweringError {
#[error("Internal compiler error: {0}")]
Internal(String),
}
54 changes: 32 additions & 22 deletions petr-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
use std::{collections::BTreeMap, rc::Rc};

use petr_typecheck::{FunctionId, TypeChecker, TypeVariable, TypedExpr, TypedExprKind};
use petr_utils::{idx_map_key, Identifier, IndexMap, SymbolId};
use petr_utils::{idx_map_key, Identifier, IndexMap, SpannedItem, SymbolId};

mod error;
mod opcodes;

use error::*;
pub use error::LoweringError;
use opcodes::*;
pub use opcodes::{DataLabel, Intrinsic, IrOpcode, Reg, ReservedRegister};

pub fn lower(checker: TypeChecker) -> Result<(DataSection, Vec<IrOpcode>), LoweringError> {
let lowerer = Lowerer::new(checker);
pub fn lower(checker: TypeChecker) -> Result<(DataSection, Vec<IrOpcode>)> {
let lowerer = Lowerer::new(checker)?;
Ok(lowerer.finalize())
}

Expand All @@ -37,6 +37,8 @@ pub struct FunctionSignature {

idx_map_key!(MonomorphizedFunctionId);

pub type Result<T> = std::result::Result<T, SpannedItem<LoweringError>>;

pub type DataSection = IndexMap<DataLabel, DataSectionEntry>;
/// Lowers typed nodes into an IR suitable for code generation.
pub struct Lowerer {
Expand All @@ -56,7 +58,7 @@ pub enum DataSectionEntry {
}

impl Lowerer {
pub fn new(type_checker: TypeChecker) -> Self {
pub fn new(type_checker: TypeChecker) -> Result<Self> {
// if there is an entry point, set that
// set entry point to func named main
let entry_point = type_checker
Expand All @@ -72,15 +74,16 @@ impl Lowerer {
monomorphized_functions: Default::default(),
};

let monomorphized_entry_point_id = entry_point.map(|(id, _func)| {
lowerer
.monomorphize_function(MonomorphizedFunction { id, params: vec![] })
.expect("handle errors better")
});
let monomorphized_entry_point_id = match entry_point {
None => None,
Some((id, _func)) => {
let monomorphized_entry_point_id = lowerer.monomorphize_function(MonomorphizedFunction { id, params: vec![] })?;
Some(monomorphized_entry_point_id)
},
};

lowerer.entry_point = monomorphized_entry_point_id;
// lowerer.lower_entry_point().expect("handle errors better");
lowerer
Ok(lowerer)
}

pub fn finalize(self) -> (DataSection, Vec<IrOpcode>) {
Expand All @@ -107,15 +110,15 @@ impl Lowerer {
fn monomorphize_function(
&mut self,
func: MonomorphizedFunction,
) -> Result<MonomorphizedFunctionId, LoweringError> {
) -> Result<MonomorphizedFunctionId> {
if let Some(previously_monomorphized_definition) = self.monomorphized_functions.iter().find(|(_id, (sig, _))| *sig == func.signature()) {
return Ok(previously_monomorphized_definition.0);
}

let function_definition_body = self.type_checker.get_function(&func.id).body.clone();

let mut buf = vec![];
self.with_variable_context(|ctx| -> Result<_, _> {
self.with_variable_context(|ctx| -> Result<_> {
// Pop parameters off the stack in reverse order -- the last parameter for the function
// will be the first thing popped off the stack
// When we lower a function call, we push them onto the stack from first to last. Since
Expand Down Expand Up @@ -170,7 +173,7 @@ impl Lowerer {
&mut self,
body: &TypedExpr,
return_destination: ReturnDestination,
) -> Result<Vec<IrOpcode>, LoweringError> {
) -> Result<Vec<IrOpcode>> {
use TypedExprKind::*;

match &body.kind {
Expand Down Expand Up @@ -247,8 +250,8 @@ impl Lowerer {
})
},
Intrinsic { ty: _ty, intrinsic } => self.lower_intrinsic(intrinsic, return_destination),
ErrorRecovery => Err(LoweringError),
ExprWithBindings { bindings, expression } => self.with_variable_context(|ctx| -> Result<_, _> {
ErrorRecovery(span) => Err(span.with_item(LoweringError::Internal("Lowering should not be performed on an AST with errors".into()))),
ExprWithBindings { bindings, expression } => self.with_variable_context(|ctx| -> Result<_> {
let mut buf = vec![];
for (name, expr) in bindings {
let reg = ctx.fresh_reg();
Expand Down Expand Up @@ -335,7 +338,7 @@ impl Lowerer {
&mut self,
intrinsic: &petr_typecheck::Intrinsic,
return_destination: ReturnDestination,
) -> Result<Vec<IrOpcode>, LoweringError> {
) -> Result<Vec<IrOpcode>> {
let mut buf = vec![];
use petr_typecheck::Intrinsic::*;
match intrinsic {
Expand Down Expand Up @@ -379,7 +382,7 @@ impl Lowerer {
rhs: &TypedExpr,
return_destination: ReturnDestination,
op: fn(Reg, Reg, Reg) -> IrOpcode,
) -> Result<Vec<IrOpcode>, LoweringError> {
) -> Result<Vec<IrOpcode>> {
let mut buf = vec![];
let lhs_reg = self.fresh_reg();
let rhs_reg = self.fresh_reg();
Expand All @@ -406,9 +409,9 @@ impl Lowerer {
fn with_variable_context<F, T>(
&mut self,
func: F,
) -> Result<T, LoweringError>
) -> Result<T>
where
F: FnOnce(&mut Self) -> Result<T, LoweringError>,
F: FnOnce(&mut Self) -> Result<T>,
{
self.variables_in_scope.push(Default::default());
let res = func(self);
Expand Down Expand Up @@ -515,7 +518,14 @@ mod tests {
panic!("ir gen failed: code didn't typecheck");
}

let lowerer = Lowerer::new(type_checker);
let lowerer = match Lowerer::new(type_checker) {
Ok(lowerer) => lowerer,
Err(err) => {
eprintln!("{:?}", err);
panic!("ir gen failed: code didn't lower");
},
};

let res = lowerer.pretty_print();

expect.assert_eq(&res);
Expand Down
3 changes: 3 additions & 0 deletions petr-playground/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ crate-type = ["cdylib"]
wasm-bindgen = "0.2.92"
petr-api = { path = "../petr-api", default-features = false, features = ["no_std"] }
stdlib = { path = "../stdlib" }

[dev-dependencies]
expect-test = "1.5.0"
20 changes: 14 additions & 6 deletions petr-playground/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! Nothing fancy at all, could definitely be improved over time to support better error reporting,
//! etc
use expect_test::expect;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
Expand Down Expand Up @@ -52,7 +53,7 @@ fn run_snippet_inner<F>(
#[test]
fn test_run_snippet() {
fn retrieve_output_content(s: &str) {
assert_eq!(s, "Logs:<br>\tHello, World!\nResult: <br>\tUnit");
expect![""].assert_eq(s);
}

run_snippet_inner(
Expand All @@ -68,7 +69,7 @@ function main() returns 'unit
#[test]
fn repro_of_wasm_panic() {
fn retrieve_output_content(s: &str) {
assert_eq!(s, "Logs:<br>\tHello, World!\nResult: <br>\tUnit");
expect![""].assert_eq(s);
}
run_snippet_inner(
r#"
Expand Down Expand Up @@ -109,17 +110,24 @@ fn compile_snippet(code: String) -> Result<Lowerer, Vec<String>> {

let (resolution_errs, resolved) = resolve_symbols(ast, interner, dependencies);
let (type_errs, type_checker) = type_check(resolved);
let lowerer = Lowerer::new(type_checker);

errs.extend(parse_errs.into_iter().map(|e| format!("{:?}", render_error(&source_map, e))));
errs.extend(type_errs.into_iter().map(|e| format!("{:?}", render_error(&source_map, e))));
errs.extend(resolution_errs.into_iter().map(|e| format!("{:?}", render_error(&source_map, e))));

if !errs.is_empty() {
Err(errs)
} else {
Ok(lowerer)
return Err(errs);
}

let lowerer = match Lowerer::new(type_checker) {
Ok(l) => l,
Err(e) => {
errs.push(format!("{:?}", e));
return Err(errs);
},
};

Ok(lowerer)
}

#[wasm_bindgen]
Expand Down
20 changes: 13 additions & 7 deletions petr-typecheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ impl TypeChecker {
Unit => self.unit(),
Variable { ty, .. } => *ty,
Intrinsic { ty, .. } => *ty,
ErrorRecovery => self.error_recovery(),
ErrorRecovery(..) => self.ctx.error_recovery,
ExprWithBindings { expression, .. } => self.expr_ty(expression),
TypeConstructor { ty, .. } => *ty,
}
Expand Down Expand Up @@ -553,7 +553,14 @@ impl TypeChecker {
self.ctx.int_ty
}

pub fn error_recovery(&self) -> TypeVariable {
/// To reference an error recovery type, you must provide an error.
/// This holds the invariant that error recovery types are only generated when
/// an error occurs.
pub fn error_recovery(
&mut self,
err: TypeError,
) -> TypeVariable {
self.push_error(err);
self.ctx.error_recovery
}

Expand Down Expand Up @@ -624,8 +631,7 @@ pub enum TypedExprKind {
ty: TypeVariable,
intrinsic: Intrinsic,
},
// TODO put a span here?
ErrorRecovery,
ErrorRecovery(Span),
ExprWithBindings {
bindings: Vec<(Identifier, TypedExpr)>,
expression: Box<TypedExpr>,
Expand Down Expand Up @@ -661,7 +667,7 @@ impl std::fmt::Debug for TypedExpr {
Unit => write!(f, "unit"),
Variable { name, .. } => write!(f, "variable: {}", name.id),
Intrinsic { intrinsic, .. } => write!(f, "intrinsic: {:?}", intrinsic),
ErrorRecovery => write!(f, "error recovery"),
ErrorRecovery(..) => write!(f, "error recovery"),
ExprWithBindings { bindings, expression } => {
write!(f, "bindings: ")?;
for (name, expr) in bindings {
Expand Down Expand Up @@ -715,7 +721,7 @@ impl TypeCheck for Expr {
function: ctx.realize_symbol(func_decl.name.id).to_string(),
}));
return TypedExpr {
kind: TypedExprKind::ErrorRecovery,
kind: TypedExprKind::ErrorRecovery(self.span),
span: self.span,
};
}
Expand All @@ -737,7 +743,7 @@ impl TypeCheck for Expr {
}
},
ExprKind::Unit => TypedExprKind::Unit,
ExprKind::ErrorRecovery => TypedExprKind::ErrorRecovery,
ExprKind::ErrorRecovery => TypedExprKind::ErrorRecovery(self.span),
ExprKind::Variable { name, ty } => {
// look up variable in scope
// find its expr return type
Expand Down
Binary file modified petr-vm/src/lib.rs
Binary file not shown.

0 comments on commit e834445

Please sign in to comment.