Add a validation step back.
This commit is contained in:
4
examples/basic/test2.ngr
Normal file
4
examples/basic/test2.ngr
Normal file
@@ -0,0 +1,4 @@
|
||||
x = 5;
|
||||
x = 4*x + 3;
|
||||
print x;
|
||||
print y;
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
src/bin.rs
26
src/bin.rs
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
100
src/syntax/validate.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user