Add a validation step back.

This commit is contained in:
2023-01-27 21:41:06 -08:00
parent afcf3c65cd
commit 3333dffa1e
6 changed files with 152 additions and 27 deletions

4
examples/basic/test2.ngr Normal file
View File

@@ -0,0 +1,4 @@
x = 5;
x = 4*x + 3;
print x;
print y;

View File

@@ -8,7 +8,7 @@ use cranelift_codegen::isa::LookupError;
use cranelift_codegen::settings::{Configurable, SetError};
use cranelift_codegen::{isa, settings, CodegenError};
use cranelift_module::{default_libcall_names, ModuleCompiledFunction, ModuleError};
use cranelift_object::{ObjectBuilder, ObjectModule, object};
use cranelift_object::{object, ObjectBuilder, ObjectModule};
use target_lexicon::Triple;
use thiserror::Error;
@@ -36,24 +36,24 @@ pub enum BackendError {
impl From<BackendError> for Diagnostic<usize> {
fn from(value: BackendError) -> Self {
match value {
BackendError::Cranelift(me) =>
Diagnostic::error()
.with_message(format!("Internal cranelift error: {}", me)),
BackendError::BuiltinError(me) =>
Diagnostic::error()
.with_message(format!("Internal runtime function error: {}", me)),
BackendError::VariableLookupFailure =>
Diagnostic::error()
.with_message("Internal variable lookup error!"),
BackendError::CodegenError(me) =>
Diagnostic::error()
.with_message(format!("Internal codegen error: {}", me)),
BackendError::SetError(me) =>
Diagnostic::error()
.with_message(format!("Internal backend setup error: {}", me)),
BackendError::LookupError(me) =>
Diagnostic::error()
.with_message(format!("Internal error: {}", me)),
BackendError::Cranelift(me) => {
Diagnostic::error().with_message(format!("Internal cranelift error: {}", me))
}
BackendError::BuiltinError(me) => {
Diagnostic::error().with_message(format!("Internal runtime function error: {}", me))
}
BackendError::VariableLookupFailure => {
Diagnostic::error().with_message("Internal variable lookup error!")
}
BackendError::CodegenError(me) => {
Diagnostic::error().with_message(format!("Internal codegen error: {}", me))
}
BackendError::SetError(me) => {
Diagnostic::error().with_message(format!("Internal backend setup error: {}", me))
}
BackendError::LookupError(me) => {
Diagnostic::error().with_message(format!("Internal error: {}", me))
}
}
}
}

View File

@@ -41,7 +41,9 @@ impl From<MainError> for Diagnostic<usize> {
MainError::Backend(be) => be.into(),
MainError::ParserError(pe) => (&pe).into(),
MainError::IoError(e) => Diagnostic::error().with_message(format!("IO error: {}", e)),
MainError::WriteError(e) => Diagnostic::error().with_message(format!("Module write error: {}", e)),
MainError::WriteError(e) => {
Diagnostic::error().with_message(format!("Module write error: {}", e))
}
}
}
}
@@ -50,13 +52,27 @@ fn compile(file_database: &mut SimpleFiles<String, String>) -> Result<(), MainEr
let args = CommandLineArguments::parse();
let syntax = Syntax::parse_file(file_database, &args.file)?;
let (mut errors, mut warnings) = syntax.validate();
let stop = !errors.is_empty();
let messages = errors
.drain(..)
.map(Into::into)
.chain(warnings.drain(..).map(Into::into));
let writer = StandardStream::stderr(ColorChoice::Auto);
let config = codespan_reporting::term::Config::default();
for message in messages {
term::emit(&mut writer.lock(), &config, file_database, &message).unwrap();
}
if stop {
return Ok(());
}
let ir = IR::from(syntax.simplify());
let compiled = Cranelift::new(Triple::host(), ir)?;
let bytes = compiled.bytes()?;
std::fs::write(
args.output.unwrap_or_else(|| "output.o".to_string()),
bytes,
)?;
std::fs::write(args.output.unwrap_or_else(|| "output.o".to_string()), bytes)?;
Ok(())
}

View File

@@ -11,6 +11,7 @@ lalrpop_mod!(
"/syntax/parser.rs"
);
pub mod ast;
mod validate;
pub use crate::syntax::ast::*;
pub use crate::syntax::location::Location;

View File

@@ -1,4 +1,4 @@
use crate::syntax::ast::{Program, Statement, Expression};
use crate::syntax::ast::{Expression, Program, Statement};
impl Program {
pub fn simplify(mut self) -> Self {
@@ -40,7 +40,11 @@ impl Expression {
let new_name = format!("<{}:{}>", base_name, *gensym_index);
*gensym_index += 1;
prereqs.push(Statement::Binding(loc.clone(), new_name.clone(), Expression::Primitive(loc.clone(), prim, new_exprs)));
prereqs.push(Statement::Binding(
loc.clone(),
new_name.clone(),
Expression::Primitive(loc.clone(), prim, new_exprs),
));
(prereqs, Expression::Reference(loc, new_name))
}

100
src/syntax/validate.rs Normal file
View File

@@ -0,0 +1,100 @@
use crate::syntax::{Expression, Location, Program, Statement};
use codespan_reporting::diagnostic::Diagnostic;
use std::collections::HashMap;
pub enum Error {
UnboundVariable(Location, String),
}
impl From<Error> for Diagnostic<usize> {
fn from(x: Error) -> Self {
match &x {
Error::UnboundVariable(location, name) => location
.labelled_error("unbound here")
.with_message(format!("Unbound variable '{}'", name)),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Warning {
ShadowedVariable(Location, Location, String),
}
impl From<Warning> for Diagnostic<usize> {
fn from(x: Warning) -> Self {
match &x {
Warning::ShadowedVariable(original, new, name) => Diagnostic::warning()
.with_labels(vec![
new.primary_label().with_message("variable rebound here"),
original
.secondary_label()
.with_message("original binding site"),
])
.with_message(format!("Variable '{}' is rebound", name)),
}
}
}
impl Program {
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
let mut errors = vec![];
let mut warnings = vec![];
let mut bound_variables = HashMap::new();
for stmt in self.statements.iter() {
match stmt {
Statement::Binding(loc, var, val) => {
// we're going to make the decision that a variable is not bound in the right
// hand side of its binding, which makes a lot of things easier. So we'll just
// immediately check the expression, and go from there.
let (mut exp_errors, mut exp_warnings) = val.validate(&bound_variables);
errors.append(&mut exp_errors);
warnings.append(&mut exp_warnings);
if let Some(original_binding_site) = bound_variables.get(var) {
warnings.push(Warning::ShadowedVariable(
original_binding_site.clone(),
loc.clone(),
var.clone(),
));
} else {
bound_variables.insert(var.clone(), loc.clone());
}
}
Statement::Print(_, var) if bound_variables.contains_key(var) => {}
Statement::Print(loc, var) => {
errors.push(Error::UnboundVariable(loc.clone(), var.clone()))
}
}
}
(errors, warnings)
}
}
impl Expression {
fn validate(&self, variable_map: &HashMap<String, Location>) -> (Vec<Error>, Vec<Warning>) {
match self {
Expression::Value(_, _) => (vec![], vec![]),
Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]),
Expression::Reference(loc, var) => (
vec![Error::UnboundVariable(loc.clone(), var.clone())],
vec![],
),
Expression::Primitive(_, _, args) => {
let mut errors = vec![];
let mut warnings = vec![];
for expr in args.iter() {
let (mut err, mut warn) = expr.validate(variable_map);
errors.append(&mut err);
warnings.append(&mut warn);
}
(errors, warnings)
}
}
}
}