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::settings::{Configurable, SetError};
|
||||||
use cranelift_codegen::{isa, settings, CodegenError};
|
use cranelift_codegen::{isa, settings, CodegenError};
|
||||||
use cranelift_module::{default_libcall_names, ModuleCompiledFunction, ModuleError};
|
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 target_lexicon::Triple;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -36,24 +36,24 @@ pub enum BackendError {
|
|||||||
impl From<BackendError> for Diagnostic<usize> {
|
impl From<BackendError> for Diagnostic<usize> {
|
||||||
fn from(value: BackendError) -> Self {
|
fn from(value: BackendError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
BackendError::Cranelift(me) =>
|
BackendError::Cranelift(me) => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message(format!("Internal cranelift error: {}", me))
|
||||||
.with_message(format!("Internal cranelift error: {}", me)),
|
}
|
||||||
BackendError::BuiltinError(me) =>
|
BackendError::BuiltinError(me) => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message(format!("Internal runtime function error: {}", me))
|
||||||
.with_message(format!("Internal runtime function error: {}", me)),
|
}
|
||||||
BackendError::VariableLookupFailure =>
|
BackendError::VariableLookupFailure => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message("Internal variable lookup error!")
|
||||||
.with_message("Internal variable lookup error!"),
|
}
|
||||||
BackendError::CodegenError(me) =>
|
BackendError::CodegenError(me) => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message(format!("Internal codegen error: {}", me))
|
||||||
.with_message(format!("Internal codegen error: {}", me)),
|
}
|
||||||
BackendError::SetError(me) =>
|
BackendError::SetError(me) => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message(format!("Internal backend setup error: {}", me))
|
||||||
.with_message(format!("Internal backend setup error: {}", me)),
|
}
|
||||||
BackendError::LookupError(me) =>
|
BackendError::LookupError(me) => {
|
||||||
Diagnostic::error()
|
Diagnostic::error().with_message(format!("Internal error: {}", me))
|
||||||
.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::Backend(be) => be.into(),
|
||||||
MainError::ParserError(pe) => (&pe).into(),
|
MainError::ParserError(pe) => (&pe).into(),
|
||||||
MainError::IoError(e) => Diagnostic::error().with_message(format!("IO error: {}", e)),
|
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 args = CommandLineArguments::parse();
|
||||||
|
|
||||||
let syntax = Syntax::parse_file(file_database, &args.file)?;
|
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 ir = IR::from(syntax.simplify());
|
||||||
let compiled = Cranelift::new(Triple::host(), ir)?;
|
let compiled = Cranelift::new(Triple::host(), ir)?;
|
||||||
let bytes = compiled.bytes()?;
|
let bytes = compiled.bytes()?;
|
||||||
std::fs::write(
|
std::fs::write(args.output.unwrap_or_else(|| "output.o".to_string()), bytes)?;
|
||||||
args.output.unwrap_or_else(|| "output.o".to_string()),
|
|
||||||
bytes,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ lalrpop_mod!(
|
|||||||
"/syntax/parser.rs"
|
"/syntax/parser.rs"
|
||||||
);
|
);
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
mod validate;
|
||||||
|
|
||||||
pub use crate::syntax::ast::*;
|
pub use crate::syntax::ast::*;
|
||||||
pub use crate::syntax::location::Location;
|
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 {
|
impl Program {
|
||||||
pub fn simplify(mut self) -> Self {
|
pub fn simplify(mut self) -> Self {
|
||||||
@@ -40,10 +40,14 @@ impl Expression {
|
|||||||
|
|
||||||
let new_name = format!("<{}:{}>", base_name, *gensym_index);
|
let new_name = format!("<{}:{}>", base_name, *gensym_index);
|
||||||
*gensym_index += 1;
|
*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))
|
(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