Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement block scoped variable declarations #173

Merged
merged 3 commits into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 119 additions & 40 deletions src/lib/environment/lexical_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ pub enum EnvironmentType {
Object,
}

/// The scope of a given variable
#[derive(Debug, Clone, Copy)]
pub enum VariableScope {
/// The variable declaration is scoped to the current block (`let` and `const`)
Block,
/// The variable declaration is scoped to the current function (`var`)
Function,
}

#[derive(Debug)]
pub struct LexicalEnvironment {
environment_stack: VecDeque<Environment>,
Expand Down Expand Up @@ -98,25 +107,78 @@ impl LexicalEnvironment {
.get_global_object()
}

pub fn create_mutable_binding(&mut self, name: String, deletion: bool) {
self.get_current_environment()
.borrow_mut()
.create_mutable_binding(name, deletion)
pub fn create_mutable_binding(&mut self, name: String, deletion: bool, scope: VariableScope) {
match scope {
VariableScope::Block => self
.get_current_environment()
.borrow_mut()
.create_mutable_binding(name, deletion),
VariableScope::Function => {
// Find the first function or global environment (from the top of the stack)
let env = self
.environment_stack
barskern marked this conversation as resolved.
Show resolved Hide resolved
.iter_mut()
.rev()
.find(|env| match env.borrow().get_environment_type() {
EnvironmentType::Function | EnvironmentType::Global => true,
_ => false,
})
.expect("No function or global environment");

env.borrow_mut().create_mutable_binding(name, deletion)
}
}
}

pub fn create_immutable_binding(&mut self, name: String, deletion: bool) -> bool {
self.get_current_environment()
.borrow_mut()
.create_immutable_binding(name, deletion)
pub fn create_immutable_binding(
&mut self,
name: String,
deletion: bool,
scope: VariableScope,
) -> bool {
match scope {
VariableScope::Block => self
.get_current_environment()
.borrow_mut()
.create_immutable_binding(name, deletion),
VariableScope::Function => {
// Find the first function or global environment (from the top of the stack)
let env = self
.environment_stack
.iter_mut()
.rev()
.find(|env| match env.borrow().get_environment_type() {
EnvironmentType::Function | EnvironmentType::Global => true,
_ => false,
})
.expect("No function or global environment");

env.borrow_mut().create_immutable_binding(name, deletion)
}
}
}

pub fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) {
let env = self.get_current_environment();
// Find the first environment which has the given binding
let env = self
.environment_stack
.iter_mut()
.rev()
.find(|env| env.borrow().has_binding(name))
.expect("Binding does not exists"); // TODO graceful error handling

env.borrow_mut().set_mutable_binding(name, value, strict);
}

pub fn initialize_binding(&mut self, name: &str, value: Value) {
let env = self.get_current_environment();
// Find the first environment which has the given binding
let env = self
.environment_stack
.iter_mut()
.rev()
.find(|env| env.borrow().has_binding(name))
.expect("Binding does not exists"); // TODO graceful error handling

env.borrow_mut().initialize_binding(name, value);
}

Expand All @@ -139,36 +201,12 @@ impl LexicalEnvironment {
}

pub fn get_binding_value(&mut self, name: &str) -> Value {
let env: Environment = self.get_current_environment().clone();
let borrowed_env = env.borrow();
let result = borrowed_env.has_binding(name);
if result {
return borrowed_env.get_binding_value(name, false);
}

// Check outer scope
if borrowed_env.get_outer_environment().is_some() {
let mut outer: Option<Environment> = borrowed_env.get_outer_environment();
while outer.is_some() {
if outer
.as_ref()
.expect("Could not get outer as reference")
.borrow()
.has_binding(&name)
{
return outer
.expect("Outer was None")
.borrow()
.get_binding_value(name, false);
}
outer = outer
.expect("Outer was None")
.borrow()
.get_outer_environment();
}
}

Gc::new(ValueData::Undefined)
self.environment_stack
.iter()
.rev()
.find(|env| env.borrow().has_binding(name))
.map(|env| env.borrow().get_binding_value(name, false))
.unwrap_or_else(|| Gc::new(ValueData::Undefined))
}
}

Expand Down Expand Up @@ -236,3 +274,44 @@ pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
var_names: HashSet::new(),
})))
}

#[cfg(test)]
mod tests {
use crate::exec;

#[test]
fn let_is_blockscoped() {
let scenario = r#"
{
let bar = "bar";
}
bar == undefined;
"#;

assert_eq!(&exec(scenario), "true");
}

#[test]
fn const_is_blockscoped() {
let scenario = r#"
{
const bar = "bar";
}
bar == undefined;
"#;

assert_eq!(&exec(scenario), "true");
}

#[test]
fn var_not_blockscoped() {
let scenario = r#"
{
var bar = "bar";
}
bar == "bar";
"#;

assert_eq!(&exec(scenario), "true");
}
}
75 changes: 52 additions & 23 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::{
environment::lexical_environment::new_function_environment,
environment::lexical_environment::{
new_declarative_environment, new_function_environment, VariableScope,
},
js::{
function::{create_unmapped_arguments_object, Function, RegularFunction},
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
Expand Down Expand Up @@ -71,6 +73,13 @@ impl Executor for Interpreter {
ExprDef::Const(Const::String(ref str)) => Ok(to_value(str.to_owned())),
ExprDef::Const(Const::Bool(val)) => Ok(to_value(val)),
ExprDef::Block(ref es) => {
{
let env = &mut self.realm.environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
}

let mut obj = to_value(None::<()>);
for e in es.iter() {
let val = self.run(e)?;
Expand All @@ -84,6 +93,8 @@ impl Executor for Interpreter {
obj = val;
}
}

self.realm.environment.pop();
Ok(obj)
}
ExprDef::Local(ref name) => {
Expand Down Expand Up @@ -215,9 +226,11 @@ impl Executor for Interpreter {
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function))));
if name.is_some() {
self.realm
.environment
.create_mutable_binding(name.clone().expect("No name was supplied"), false);
self.realm.environment.create_mutable_binding(
name.clone().expect("No name was supplied"),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(
name.as_ref().expect("Could not get name as reference"),
val.clone(),
Expand Down Expand Up @@ -355,7 +368,11 @@ impl Executor for Interpreter {
for i in 0..data.args.len() {
let name = data.args.get(i).expect("Could not get data argument");
let expr = v_args.get(i).expect("Could not get argument");
env.create_mutable_binding(name.clone(), false);
env.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
env.initialize_binding(name, expr.to_owned());
}
let result = self.run(&data.expr);
Expand Down Expand Up @@ -387,9 +404,11 @@ impl Executor for Interpreter {
.environment
.set_mutable_binding(&name, val.clone(), true);
} else {
self.realm
.environment
.create_mutable_binding(name.clone(), true);
self.realm.environment.create_mutable_binding(
name.clone(),
true,
VariableScope::Function,
);
self.realm.environment.initialize_binding(name, val.clone());
}
}
Expand All @@ -408,9 +427,11 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Undefined),
};
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
Expand All @@ -422,18 +443,22 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Undefined),
};
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Block,
);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
}
ExprDef::ConstDecl(ref vars) => {
for (name, value) in vars.iter() {
self.realm
.environment
.create_immutable_binding(name.clone(), false);
self.realm.environment.create_immutable_binding(
name.clone(),
false,
VariableScope::Block,
);
let val = self.run(&value)?;
self.realm.environment.initialize_binding(&name, val);
}
Expand Down Expand Up @@ -485,19 +510,23 @@ impl Interpreter {
for i in 0..data.args.len() {
let name = data.args.get(i).expect("Could not get data argument");
let expr: &Value = arguments_list.get(i).expect("Could not get argument");
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding(name, expr.clone());
}

// Add arguments object
let arguments_obj = create_unmapped_arguments_object(arguments_list);
self.realm
.environment
.create_mutable_binding("arguments".to_string(), false);
self.realm.environment.create_mutable_binding(
"arguments".to_string(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding("arguments", arguments_obj);
Expand Down
Loading