Add variable resolution; the error behavior is pretty bad here.
This commit is contained in:
1
src/asts.rs
Normal file
1
src/asts.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod hil;
|
||||||
173
src/asts/hil.rs
Normal file
173
src/asts/hil.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
use crate::variable_map::{Variable, VariableMap};
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Program<Annotation> {
|
||||||
|
pub statements: Vec<Statement<Annotation>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Annotation> Program<Annotation> {
|
||||||
|
pub fn pretty<'a, D, A>(
|
||||||
|
&self,
|
||||||
|
variable_map: &VariableMap,
|
||||||
|
allocator: &'a D,
|
||||||
|
) -> DocBuilder<'a, D, A>
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
let mut result = allocator.nil();
|
||||||
|
|
||||||
|
for stmt in self.statements.iter() {
|
||||||
|
result = result
|
||||||
|
.append(stmt.pretty(variable_map, allocator))
|
||||||
|
.append(allocator.text(";"))
|
||||||
|
.append(allocator.hardline());
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Statement<Annotation> {
|
||||||
|
Binding(Annotation, Variable, Expression<Annotation>),
|
||||||
|
Print(Annotation, Variable),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Annotation> Statement<Annotation> {
|
||||||
|
pub fn pretty<'a, D, A>(
|
||||||
|
&self,
|
||||||
|
variable_map: &VariableMap,
|
||||||
|
allocator: &'a D,
|
||||||
|
) -> DocBuilder<'a, D, A>
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Statement::Binding(_, var, expr) => {
|
||||||
|
let name = variable_map.get_name(*var).unwrap_or("<unknown>");
|
||||||
|
|
||||||
|
allocator
|
||||||
|
.text(name.to_string())
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(allocator.text("="))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(expr.pretty(variable_map, allocator))
|
||||||
|
}
|
||||||
|
Statement::Print(_, var) => {
|
||||||
|
let name = variable_map.get_name(*var).unwrap_or("<unknown>");
|
||||||
|
|
||||||
|
allocator
|
||||||
|
.text("print")
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(allocator.text(name.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Expression<Annotation> {
|
||||||
|
Value(Annotation, Value),
|
||||||
|
Reference(Annotation, Variable),
|
||||||
|
Primitive(Annotation, Primitive, Vec<Expression<Annotation>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Primitive {
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Times,
|
||||||
|
Divide,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Annotation> Expression<Annotation> {
|
||||||
|
fn pretty<'a, A, D>(&self, variable_map: &VariableMap, allocator: &'a D) -> DocBuilder<'a, D, A>
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Expression::Value(_, val) => val.pretty(allocator),
|
||||||
|
Expression::Reference(_, var) => {
|
||||||
|
let name = variable_map.get_name(*var).unwrap_or("<unknown>");
|
||||||
|
|
||||||
|
allocator.text(name.to_string())
|
||||||
|
}
|
||||||
|
Expression::Primitive(_, op, exprs) if exprs.len() == 1 => op
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(exprs[0].pretty(variable_map, allocator)),
|
||||||
|
Expression::Primitive(_, op, exprs) if exprs.len() == 2 => {
|
||||||
|
let left = exprs[0].pretty(variable_map, allocator);
|
||||||
|
let right = exprs[1].pretty(variable_map, allocator);
|
||||||
|
|
||||||
|
left.append(allocator.space())
|
||||||
|
.append(op.pretty(allocator))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(right)
|
||||||
|
.parens()
|
||||||
|
}
|
||||||
|
Expression::Primitive(_, op, exprs) => {
|
||||||
|
allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
|
||||||
|
match self {
|
||||||
|
Primitive::Plus => allocator.text("+"),
|
||||||
|
Primitive::Minus => allocator.text("-"),
|
||||||
|
Primitive::Times => allocator.text("*"),
|
||||||
|
Primitive::Divide => allocator.text("/"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Value {
|
||||||
|
Number(Option<u8>, i128),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
|
||||||
|
match self {
|
||||||
|
Value::Number(opt_base, value) => {
|
||||||
|
let value_str = match opt_base {
|
||||||
|
None => format!("{}", value),
|
||||||
|
Some(2) => format!("0b{:b}", value),
|
||||||
|
Some(8) => format!("0o{:o}", value),
|
||||||
|
Some(10) => format!("0d{}", value),
|
||||||
|
Some(16) => format!("0x{:x}", value),
|
||||||
|
Some(_) => format!("!!{:x}!!", value),
|
||||||
|
};
|
||||||
|
|
||||||
|
allocator.text(value_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct CommaSep {}
|
||||||
|
|
||||||
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for CommaSep
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
|
||||||
|
allocator.text(",").append(allocator.space())
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/bin.rs
41
src/bin.rs
@@ -3,10 +3,8 @@ use codespan_reporting::diagnostic::Diagnostic;
|
|||||||
use codespan_reporting::files::SimpleFiles;
|
use codespan_reporting::files::SimpleFiles;
|
||||||
use codespan_reporting::term;
|
use codespan_reporting::term;
|
||||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||||
use ngr::error::Error;
|
use ngr::passes::run_front_end;
|
||||||
use ngr::syntax::Program;
|
use pretty::Arena;
|
||||||
use pretty::{Arena, Pretty};
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
@@ -19,36 +17,27 @@ struct CommandLineArguments {
|
|||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_file(
|
|
||||||
file_database: &mut SimpleFiles<String, String>,
|
|
||||||
initial_file_name: &str,
|
|
||||||
) -> Result<Program, Error> {
|
|
||||||
let initial_file_contents = fs::read_to_string(initial_file_name)?;
|
|
||||||
let initial_file = file_database.add(initial_file_name.to_string(), initial_file_contents);
|
|
||||||
let db_version = file_database.get(initial_file)?;
|
|
||||||
let db_version_source = db_version.source();
|
|
||||||
Ok(Program::parse(initial_file, db_version_source)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = CommandLineArguments::parse();
|
let args = CommandLineArguments::parse();
|
||||||
let mut file_database = SimpleFiles::new();
|
let mut file_database = SimpleFiles::new();
|
||||||
let initial_file_name = &args.file;
|
let initial_file_name = &args.file;
|
||||||
|
let mut hil_conversion_result = run_front_end(&mut file_database, initial_file_name);
|
||||||
|
|
||||||
match compile_file(&mut file_database, initial_file_name) {
|
let writer = StandardStream::stderr(ColorChoice::Auto);
|
||||||
Ok(p) => {
|
let config = codespan_reporting::term::Config::default();
|
||||||
|
|
||||||
|
for error in hil_conversion_result.errors.drain(..) {
|
||||||
|
term::emit(&mut writer.lock(), &config, &file_database, &Diagnostic::from(error)).unwrap();
|
||||||
|
}
|
||||||
|
for warning in hil_conversion_result.warnings.drain(..) {
|
||||||
|
term::emit(&mut writer.lock(), &config, &file_database, &Diagnostic::from(warning)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((tree, variable_map)) = hil_conversion_result.result {
|
||||||
let arena = Arena::new();
|
let arena = Arena::new();
|
||||||
p.pretty(&arena)
|
tree.pretty(&variable_map, &arena)
|
||||||
.into_doc()
|
.into_doc()
|
||||||
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
|
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
let diagnostic = Diagnostic::from(e);
|
|
||||||
let writer = StandardStream::stderr(ColorChoice::Auto);
|
|
||||||
let config = codespan_reporting::term::Config::default();
|
|
||||||
|
|
||||||
term::emit(&mut writer.lock(), &config, &file_database, &diagnostic).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("Error in parser: {0}")]
|
#[error("Error in parser: {0}")]
|
||||||
ParserError(#[from] ParseError<Location, Token, LexerError>),
|
ParserError(#[from] ParseError<Location, Token, LexerError>),
|
||||||
|
|
||||||
|
#[error("Internal error: Couldn't deal with bound variable with no bindiing site ({0})")]
|
||||||
|
BindingSiteFailure(Location, String),
|
||||||
|
|
||||||
|
#[error("Unbound variable '{0}'")]
|
||||||
|
UnboundVariable(Location, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locations_to_labels(start: &Location, end: &Location) -> Vec<Label<usize>> {
|
fn locations_to_labels(start: &Location, end: &Location) -> Vec<Label<usize>> {
|
||||||
@@ -136,6 +142,34 @@ impl From<Error> for Diagnostic<usize> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Error::BindingSiteFailure(location, name) => match location {
|
||||||
|
Location::Manufactured => Diagnostic::error().with_message(format!(
|
||||||
|
"Internal Error: Lost binding site for bound variable {}",
|
||||||
|
name
|
||||||
|
)),
|
||||||
|
Location::InFile(file_id, offset) => Diagnostic::error()
|
||||||
|
.with_labels(vec![
|
||||||
|
Label::primary(*file_id, *offset..*offset).with_message("discovered here")
|
||||||
|
])
|
||||||
|
.with_message(format!(
|
||||||
|
"Internal Error: Lost binding site for bound variable {}",
|
||||||
|
name
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
|
||||||
|
Error::UnboundVariable(location, name) => match location {
|
||||||
|
Location::Manufactured => Diagnostic::error().with_message(format!(
|
||||||
|
"Unbound variable '{}'",
|
||||||
|
name
|
||||||
|
)),
|
||||||
|
Location::InFile(file_id, offset) => Diagnostic::error()
|
||||||
|
.with_labels(vec![
|
||||||
|
Label::primary(*file_id, *offset..*offset).with_message("unbound here")
|
||||||
|
])
|
||||||
|
.with_message(format!("Unbound variable '{}'", name)),
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
pub mod error;
|
pub mod asts;
|
||||||
|
pub mod errors;
|
||||||
|
pub mod passes;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod variable_map;
|
||||||
|
pub mod warnings;
|
||||||
|
|||||||
64
src/passes.rs
Normal file
64
src/passes.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use codespan_reporting::files::SimpleFiles;
|
||||||
|
use crate::asts::hil;
|
||||||
|
use crate::errors::Error;
|
||||||
|
use crate::syntax;
|
||||||
|
use crate::syntax::Location;
|
||||||
|
use crate::variable_map::VariableMap;
|
||||||
|
use crate::warnings::Warning;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
mod syntax_to_hil;
|
||||||
|
|
||||||
|
pub struct PassResult<T> {
|
||||||
|
pub result: T,
|
||||||
|
pub warnings: Vec<Warning>,
|
||||||
|
pub errors: Vec<Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T,E> From<E> for PassResult<Option<T>>
|
||||||
|
where
|
||||||
|
Error: From<E>
|
||||||
|
{
|
||||||
|
fn from(x: E) -> Self {
|
||||||
|
PassResult {
|
||||||
|
result: None,
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![Error::from(x)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_front_end(
|
||||||
|
file_database: &mut SimpleFiles<String, String>,
|
||||||
|
initial_file_name: &str,
|
||||||
|
) -> PassResult<Option<(hil::Program<Location>, VariableMap)>> {
|
||||||
|
let initial_file_contents = match fs::read_to_string(initial_file_name) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return PassResult::from(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let initial_file = file_database.add(initial_file_name.to_string(), initial_file_contents);
|
||||||
|
let db_version = match file_database.get(initial_file) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return PassResult::from(e),
|
||||||
|
};
|
||||||
|
let db_version_source = db_version.source();
|
||||||
|
let raw_syntax = match syntax::Program::parse(initial_file, db_version_source) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return PassResult::from(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut variable_map = VariableMap::empty();
|
||||||
|
let conversion_result = hil::Program::convert(raw_syntax, &mut variable_map);
|
||||||
|
let result = if conversion_result.errors.is_empty() {
|
||||||
|
Some((conversion_result.result, variable_map))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
PassResult {
|
||||||
|
result,
|
||||||
|
warnings: conversion_result.warnings,
|
||||||
|
errors: conversion_result.errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/passes/syntax_to_hil.rs
Normal file
152
src/passes/syntax_to_hil.rs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
use crate::asts::hil::{self, Primitive};
|
||||||
|
use crate::errors::Error;
|
||||||
|
use crate::passes::PassResult;
|
||||||
|
use crate::syntax;
|
||||||
|
use crate::syntax::Location;
|
||||||
|
use crate::variable_map::VariableMap;
|
||||||
|
use crate::warnings::Warning;
|
||||||
|
|
||||||
|
impl hil::Program<Location> {
|
||||||
|
pub fn convert(
|
||||||
|
mut input: syntax::Program,
|
||||||
|
var_map: &mut VariableMap,
|
||||||
|
) -> PassResult<hil::Program<Location>> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
let mut warnings = Vec::new();
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for syntax_stmt in input.statements.drain(..) {
|
||||||
|
let mut result = hil::Statement::convert(syntax_stmt, var_map);
|
||||||
|
|
||||||
|
statements.push(result.result);
|
||||||
|
warnings.append(&mut result.warnings);
|
||||||
|
errors.append(&mut result.errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
PassResult {
|
||||||
|
result: hil::Program { statements },
|
||||||
|
warnings,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl hil::Statement<Location> {
|
||||||
|
fn convert(
|
||||||
|
input: syntax::Statement,
|
||||||
|
var_map: &mut VariableMap,
|
||||||
|
) -> PassResult<hil::Statement<Location>> {
|
||||||
|
match input {
|
||||||
|
syntax::Statement::Binding(loc, variable_name, expr) => {
|
||||||
|
let mut expr_result = hil::Expression::convert(expr, var_map);
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut warnings = Vec::new();
|
||||||
|
|
||||||
|
if let Some(var) = var_map.get_variable(&variable_name) {
|
||||||
|
if let Some(orig_loc) = var_map.get_binding_site(var) {
|
||||||
|
warnings.push(Warning::ShadowedVariable(orig_loc, loc.clone(), variable_name.clone()));
|
||||||
|
} else {
|
||||||
|
errors.push(Error::BindingSiteFailure(
|
||||||
|
loc.clone(),
|
||||||
|
variable_name.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let variable = var_map.add(variable_name, loc.clone());
|
||||||
|
let result = hil::Statement::Binding(loc, variable, expr_result.result);
|
||||||
|
warnings.append(&mut expr_result.warnings);
|
||||||
|
errors.append(&mut expr_result.errors);
|
||||||
|
|
||||||
|
PassResult {
|
||||||
|
result,
|
||||||
|
warnings,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syntax::Statement::Print(variable_loc, variable_name) => match var_map.get_variable(&variable_name) {
|
||||||
|
None => PassResult {
|
||||||
|
result: hil::Statement::Print(Location::Manufactured, 0),
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![Error::UnboundVariable(variable_loc, variable_name)],
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(variable) => PassResult {
|
||||||
|
result: hil::Statement::Print(variable_loc, variable),
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl hil::Expression<Location> {
|
||||||
|
fn convert(
|
||||||
|
input: syntax::Expression,
|
||||||
|
var_map: &mut VariableMap,
|
||||||
|
) -> PassResult<hil::Expression<Location>> {
|
||||||
|
match input {
|
||||||
|
syntax::Expression::Value(location, value) => PassResult {
|
||||||
|
result: hil::Expression::Value(location, hil::Value::from(value)),
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![],
|
||||||
|
},
|
||||||
|
|
||||||
|
syntax::Expression::Reference(location, name) => match var_map.get_variable(&name) {
|
||||||
|
None => PassResult {
|
||||||
|
result: hil::Expression::Reference(Location::Manufactured, 0),
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![Error::UnboundVariable(location, name)],
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(variable) => PassResult {
|
||||||
|
result: hil::Expression::Reference(location, variable),
|
||||||
|
warnings: vec![],
|
||||||
|
errors: vec![],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
syntax::Expression::Primitive(location, name, mut exprs) => {
|
||||||
|
let mut args = vec![];
|
||||||
|
let mut warnings = vec![];
|
||||||
|
let mut errors = vec![];
|
||||||
|
|
||||||
|
let op = match name.as_ref() {
|
||||||
|
"+" => Primitive::Plus,
|
||||||
|
"-" => Primitive::Minus,
|
||||||
|
"*" => Primitive::Times,
|
||||||
|
"/" => Primitive::Divide,
|
||||||
|
other => {
|
||||||
|
errors.push(Error::UnboundVariable(location.clone(), other.to_string()));
|
||||||
|
Primitive::Plus
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for orig_expr in exprs.drain(..) {
|
||||||
|
let mut sub_result = hil::Expression::convert(orig_expr, var_map);
|
||||||
|
|
||||||
|
args.push(sub_result.result);
|
||||||
|
warnings.append(&mut sub_result.warnings);
|
||||||
|
errors.append(&mut sub_result.errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
PassResult {
|
||||||
|
result: hil::Expression::Primitive(location, op, args),
|
||||||
|
warnings,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<syntax::Value> for hil::Value {
|
||||||
|
fn from(x: syntax::Value) -> hil::Value {
|
||||||
|
match x {
|
||||||
|
syntax::Value::Number(base, value) =>
|
||||||
|
hil::Value::Number(base, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,13 +38,14 @@ impl FromStr for Program {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn order_of_operations() {
|
fn order_of_operations() {
|
||||||
let muladd1 = "1 + 2 * 3;";
|
let muladd1 = "x = 1 + 2 * 3;";
|
||||||
let testfile = 0;
|
let testfile = 0;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Program::from_str(muladd1).unwrap(),
|
Program::from_str(muladd1).unwrap(),
|
||||||
Program {
|
Program {
|
||||||
statements: vec![Statement::Expr(
|
statements: vec![Statement::Binding(
|
||||||
Location::InFile(testfile, 0),
|
Location::InFile(testfile, 0),
|
||||||
|
"x".to_string(),
|
||||||
Expression::Primitive(
|
Expression::Primitive(
|
||||||
Location::InFile(testfile, 2),
|
Location::InFile(testfile, 2),
|
||||||
"+".to_string(),
|
"+".to_string(),
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ where
|
|||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Binding(Location, String, Expression),
|
Binding(Location, String, Expression),
|
||||||
Print(Location, String),
|
Print(Location, String),
|
||||||
Expr(Location, Expression),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement
|
||||||
@@ -51,7 +50,6 @@ where
|
|||||||
.text("print")
|
.text("print")
|
||||||
.append(allocator.space())
|
.append(allocator.space())
|
||||||
.append(allocator.text(var.to_string())),
|
.append(allocator.text(var.to_string())),
|
||||||
Statement::Expr(_, expr) => expr.pretty(allocator),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ Statements: Vec<Statement> = {
|
|||||||
|
|
||||||
Statement: Statement = {
|
Statement: Statement = {
|
||||||
<l:@L> <v:"<var>"> "=" <e:Expression> ";" => Statement::Binding(l, v.to_string(), e),
|
<l:@L> <v:"<var>"> "=" <e:Expression> ";" => Statement::Binding(l, v.to_string(), e),
|
||||||
<l:@L> "print" <v:"<var>"> ";" => Statement::Print(l, v.to_string()),
|
"print" <l:@L> <v:"<var>"> ";" => Statement::Print(l, v.to_string()),
|
||||||
<l:@L> <e:Expression> ";" => Statement::Expr(l, e),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression: Expression = {
|
Expression: Expression = {
|
||||||
|
|||||||
57
src/variable_map.rs
Normal file
57
src/variable_map.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use crate::syntax::Location;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type Variable = usize;
|
||||||
|
pub struct VariableMap {
|
||||||
|
map: HashMap<Variable, VariableInfo>,
|
||||||
|
next_index: Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VariableInfo {
|
||||||
|
name: String,
|
||||||
|
binding_location: Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableInfo {
|
||||||
|
fn new(name: String, binding_location: Location) -> Self {
|
||||||
|
VariableInfo {
|
||||||
|
name,
|
||||||
|
binding_location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableMap {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
VariableMap {
|
||||||
|
map: HashMap::new(),
|
||||||
|
next_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, name: String, loc: Location) -> Variable {
|
||||||
|
let result = self.next_index;
|
||||||
|
|
||||||
|
self.next_index += 1;
|
||||||
|
self.map.insert(result, VariableInfo::new(name, loc));
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_name(&self, var: Variable) -> Option<&str> {
|
||||||
|
self.map.get(&var).map(|x| x.name.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_binding_site(&self, var: Variable) -> Option<Location> {
|
||||||
|
self.map.get(&var).map(|x| x.binding_location.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_variable(&self, name: &str) -> Option<usize> {
|
||||||
|
for (num, info) in self.map.iter() {
|
||||||
|
if info.name == name {
|
||||||
|
return Some(*num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/warnings.rs
Normal file
41
src/warnings.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use crate::syntax::Location;
|
||||||
|
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
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) => match original {
|
||||||
|
Location::Manufactured => match new {
|
||||||
|
Location::Manufactured => Diagnostic::warning()
|
||||||
|
.with_message(format!("Variable '{}' is rebound", name)),
|
||||||
|
Location::InFile(file_id, offset) => Diagnostic::warning()
|
||||||
|
.with_labels(vec![Label::primary(*file_id, *offset..*offset)
|
||||||
|
.with_message("variable rebound here")])
|
||||||
|
.with_message(format!("Variable '{}' is rebound", name)),
|
||||||
|
},
|
||||||
|
Location::InFile(orig_file_id, orig_offset) => match new {
|
||||||
|
Location::Manufactured => Diagnostic::warning()
|
||||||
|
.with_labels(vec![Label::primary(
|
||||||
|
*orig_file_id,
|
||||||
|
*orig_offset..*orig_offset,
|
||||||
|
)
|
||||||
|
.with_message("original binding site")])
|
||||||
|
.with_message(format!("Variable '{}' is rebound", name)),
|
||||||
|
Location::InFile(new_file_id, new_offset) => Diagnostic::warning()
|
||||||
|
.with_labels(vec![
|
||||||
|
Label::primary(*new_file_id, *new_offset..*new_offset)
|
||||||
|
.with_message("variable rebound here"),
|
||||||
|
Label::secondary(*orig_file_id, *orig_offset..*orig_offset)
|
||||||
|
.with_message("original binding site"),
|
||||||
|
])
|
||||||
|
.with_message(format!("Variable '{}' is rebound", name)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user