Skip to content

Instantly share code, notes, and snippets.

@linuskmr
Created January 23, 2022 11:53
Show Gist options
  • Save linuskmr/bb20194205080a3f692497f982011b1b to your computer and use it in GitHub Desktop.
Save linuskmr/bb20194205080a3f692497f982011b1b to your computer and use it in GitHub Desktop.
Demonstration of a checker for the forty two languages. Its goal is to verify the usage of variables, i.e. creating, accessing and dropping values, for example on the stack via RAII.
//! This is a demonstration of how a checker for the forty two languages could look like. Its goal is to verify the
//! usage of variables, i.e. creating, accessing and dropping values, for example on the stack via RAII.
use std::collections::{HashSet};
use thiserror::Error;
/// Instructions that a checker has to validate, i.e. creating, accessing and dropping variables.
enum Instruction {
/// Create a new variable
Create(String),
/// Access a variable for getting or setting
Access(String),
/// Removes the variable
Drop(String),
}
/// This checker validates source code or an AST by verifying that all variable creations and accesses are valid.
///
/// This means that no two variables with the same name can exist and that a not declared variable may not be
/// accessed. This concept might be expanded to checking if functions with the specified names and types exists.
#[derive(Debug, Eq, PartialEq)]
struct Checker<Instructions: Iterator<Item=Instruction>> {
instructions: Instructions,
/// All currently defined variables
variables: HashSet<String>,
}
/// Indicates that by the checking the code, an error occurred.
///
/// This it either because a variable with the same name is already defined ([CheckError::AlreadyDefined]) or a
/// unknown variables is accessed ([CheckError::UnknownVariable]).
#[derive(Error, Debug, Eq, PartialEq)]
enum CheckError {
/// Another variable with the same name exists.
#[error("Another variable with the name `{0}` exists")]
AlreadyDefined(String),
/// Access to variable that is not declared yet.
#[error("Access to variable `{0}` which is not declared")]
UnknownVariable(String),
}
impl<Instructions: Iterator<Item=Instruction>> Iterator for Checker<Instructions> {
type Item = Result<(), CheckError>;
fn next(&mut self) -> Option<Self::Item> {
// Get the next instruction from the code. If the code is drained, nothing has to be done and we will return
// immediately
let instruction: Instruction = self.instructions.next()?;
// Depending on what the next instruction is, either add a variable to the set or remove one or check if a
// variable is in the set.
Some(match instruction {
Instruction::Create(new_var) => {
match self.variables.insert(new_var.clone()) {
// Variable not already in set
true => Ok(()),
// Variable already in set
false => Err(CheckError::AlreadyDefined(new_var)),
}
},
Instruction::Access(get_var) => {
match self.variables.contains(&get_var) {
true => Ok(()),
// No variable with this name
false => Err(CheckError::UnknownVariable(get_var))
}
},
Instruction::Drop(drop_var) => {
match self.variables.remove(&drop_var) {
// Variable was present in set
true => Ok(()),
// Variable not present in set
false => Err(CheckError::UnknownVariable(drop_var)),
}
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
/// This test verifies that a basic flow of instructions work do not lead to any error.
#[test]
fn basic_ok() {
let checker = Checker {
instructions: vec![
Instruction::Create("name".into()),
Instruction::Create("age".into()),
Instruction::Access("name".into()),
Instruction::Drop("age".into()),
].into_iter(),
variables: HashSet::new(),
};
let check_result: Result<(), CheckError> = checker.collect();
assert!(check_result.is_ok());
}
/// An already dropped variable will be accessed, which is expected to lead to an [CheckError::UnknownVariable].
#[test]
fn already_dropped_var() {
let checker = Checker {
instructions: vec![
Instruction::Create("name".into()),
Instruction::Create("age".into()),
Instruction::Access("name".into()),
Instruction::Drop("age".into()),
Instruction::Access("age".into()),
].into_iter(),
variables: HashSet::new(),
};
let check_result: Result<(), CheckError> = checker.collect();
assert!(matches!(check_result, Err(CheckError::UnknownVariable(_))));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment