🤷 The initial version of the compiler, both static and JIT.
This implements a full compiler, with both static compilation and JIT support, for the world's simplest and silliest programming language. You can do math, and print variables. That's it. On the bright side, it implements every part of the compiler, from the lexer and parser; through analysis and simplification; and into a reasonable code generator. This should be a good jumping off point for adding more advanced features. Tests, including proptests, are included to help avoid regressions.
This commit is contained in:
116
src/syntax/validate.rs
Normal file
116
src/syntax/validate.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
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() {
|
||||
let (mut new_errors, mut new_warnings) = stmt.validate(&mut bound_variables);
|
||||
errors.append(&mut new_errors);
|
||||
warnings.append(&mut new_warnings);
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
}
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn validate(
|
||||
&self,
|
||||
bound_variables: &mut HashMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
|
||||
match self {
|
||||
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