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())
|
||||
}
|
||||
}
|
||||
47
src/bin.rs
47
src/bin.rs
@@ -3,10 +3,8 @@ use codespan_reporting::diagnostic::Diagnostic;
|
||||
use codespan_reporting::files::SimpleFiles;
|
||||
use codespan_reporting::term;
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
use ngr::error::Error;
|
||||
use ngr::syntax::Program;
|
||||
use pretty::{Arena, Pretty};
|
||||
use std::fs;
|
||||
use ngr::passes::run_front_end;
|
||||
use pretty::Arena;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
@@ -19,36 +17,27 @@ struct CommandLineArguments {
|
||||
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() {
|
||||
let args = CommandLineArguments::parse();
|
||||
let mut file_database = SimpleFiles::new();
|
||||
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) {
|
||||
Ok(p) => {
|
||||
let arena = Arena::new();
|
||||
p.pretty(&arena)
|
||||
.into_doc()
|
||||
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
|
||||
.unwrap()
|
||||
}
|
||||
Err(e) => {
|
||||
let diagnostic = Diagnostic::from(e);
|
||||
let writer = StandardStream::stderr(ColorChoice::Auto);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
let writer = StandardStream::stderr(ColorChoice::Auto);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
|
||||
term::emit(&mut writer.lock(), &config, &file_database, &diagnostic).unwrap();
|
||||
}
|
||||
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();
|
||||
tree.pretty(&variable_map, &arena)
|
||||
.into_doc()
|
||||
.render_colored(72, StandardStream::stdout(ColorChoice::Auto))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,12 @@ pub enum Error {
|
||||
|
||||
#[error("Error in parser: {0}")]
|
||||
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>> {
|
||||
@@ -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 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]
|
||||
fn order_of_operations() {
|
||||
let muladd1 = "1 + 2 * 3;";
|
||||
let muladd1 = "x = 1 + 2 * 3;";
|
||||
let testfile = 0;
|
||||
assert_eq!(
|
||||
Program::from_str(muladd1).unwrap(),
|
||||
Program {
|
||||
statements: vec![Statement::Expr(
|
||||
statements: vec![Statement::Binding(
|
||||
Location::InFile(testfile, 0),
|
||||
"x".to_string(),
|
||||
Expression::Primitive(
|
||||
Location::InFile(testfile, 2),
|
||||
"+".to_string(),
|
||||
|
||||
@@ -31,7 +31,6 @@ where
|
||||
pub enum Statement {
|
||||
Binding(Location, String, Expression),
|
||||
Print(Location, String),
|
||||
Expr(Location, Expression),
|
||||
}
|
||||
|
||||
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement
|
||||
@@ -51,7 +50,6 @@ where
|
||||
.text("print")
|
||||
.append(allocator.space())
|
||||
.append(allocator.text(var.to_string())),
|
||||
Statement::Expr(_, expr) => expr.pretty(allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,7 @@ Statements: Vec<Statement> = {
|
||||
|
||||
Statement: Statement = {
|
||||
<l:@L> <v:"<var>"> "=" <e:Expression> ";" => Statement::Binding(l, v.to_string(), e),
|
||||
<l:@L> "print" <v:"<var>"> ";" => Statement::Print(l, v.to_string()),
|
||||
<l:@L> <e:Expression> ";" => Statement::Expr(l, e),
|
||||
"print" <l:@L> <v:"<var>"> ";" => Statement::Print(l, v.to_string()),
|
||||
}
|
||||
|
||||
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