Skip to content

Commit

Permalink
8 function array arguments (#13)
Browse files Browse the repository at this point in the history
# Pull Request

<!-- Provide a general summary of your changes in the title above -->
<!-- Optional fields can be removed if not applicable -->

## Description

The compiler can now properly handle variable reassignment using the
previous value.

## Changes Made

We now verify that the variable exists and that it will be within the
same scope before redefining it.

## Related Issue <!-- Optional -->

Fixes #8 

## Checklist

- [x] I have self-reviewed my code
- [x] Code follows project's style guidelines
- [x] Tests added and passing
- [x] Documentation updated (if needed)
  • Loading branch information
Yag000 authored Aug 11, 2023
2 parents a0bd47a + fbbdff8 commit be91040
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 1 deletion.
27 changes: 26 additions & 1 deletion crates/compiler/src/compiler.rs
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,32 @@ impl Compiler {
self.emit(Opcode::Pop, vec![]);
}
Statement::Let(s) => {
let symbol = self.symbol_table.define(s.name.value);
// This step is extremely important. If it is not done then when shadowing variables
// and using the previous value we get an error. Because we would have assigned
// a new index to the symbol and the GetGlobal instruction would get a NULL
// value instead of the previous value. (corresponds to issue #8)
let symbol = match self.symbol_table.resolve(&s.name.value) {
Some(symbol) => match symbol.scope {
SymbolScope::Global => {
// A Local variable should never replace a global one
if self.symbol_table.has_outer() {
// This means that the symbol will
// be local and not global, and thus not
// replace the global one
self.symbol_table.define(s.name.value)
} else {
symbol
}
}
SymbolScope::Local => symbol,

// We only want to do in in the case of "normal" variable assignation.
// The special cases should not be touched, since the program should not
// have access to them, only the compiler/vm
_ => self.symbol_table.define(s.name.value),
},
None => self.symbol_table.define(s.name.value),
};

self.compile_expression(s.value)?;

Expand Down
32 changes: 32 additions & 0 deletions crates/compiler/src/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,16 @@ pub mod tests {
#[test]
fn test_let_statements() {
let tests = vec![
CompilerTestCase {
input: r#"
let one = 1;"#
.to_string(),
expected_constants: vec![Object::INTEGER(1)],
expected_instructions: flatten_instructions(vec![
Opcode::Constant.make(vec![0]),
Opcode::SetGlobal.make(vec![0]),
]),
},
CompilerTestCase {
input: r#"
let one = 1;
Expand Down Expand Up @@ -1151,4 +1161,26 @@ pub mod tests {

run_compiler(tests);
}

#[test]
fn test_shadowing_with_itself() {
let tests = vec![CompilerTestCase {
input: r#"
let a = 1;
let a = a + 1;"#
.to_string(),

expected_constants: vec![Object::INTEGER(1), Object::INTEGER(1)],
expected_instructions: flatten_instructions(vec![
Opcode::Constant.make(vec![0]),
Opcode::SetGlobal.make(vec![0]),
Opcode::GetGlobal.make(vec![0]),
Opcode::Constant.make(vec![1]),
Opcode::Add.make(vec![]),
Opcode::SetGlobal.make(vec![0]),
]),
}];

run_compiler(tests);
}
}
4 changes: 4 additions & 0 deletions crates/compiler/src/symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ impl SymbolTable {
self.store.insert(symbol.name.clone(), symbol.clone());
symbol
}

pub fn has_outer(&self) -> bool{
self.outer.is_some()
}
}

#[cfg(test)]
Expand Down
271 changes: 271 additions & 0 deletions crates/vm/src/vm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,277 @@ mod tests {
expected: Object::INTEGER(610),
}];

run_vm_tests(tests);
}
#[test]
fn test_use_same_varaible_multiple_times() {
let tests = vec![
VmTestCase {
input: r#"
let a = 1;
let b = a;
let c = a + b + 1;
c"#
.to_string(),
expected: Object::INTEGER(3),
},
VmTestCase {
input: r#"
let a = 1;
let b = a;
let c = a + b + 1;
let a = 2;
let b = a;
let c = a + b + 1;
c"#
.to_string(),
expected: Object::INTEGER(5),
},
VmTestCase {
input: r#"
let a = "hello";
let b = "world";
let c = a + b;
let d = a + b + c;
d"#
.to_string(),
expected: Object::STRING("helloworldhelloworld".to_string()),
},
];

run_vm_tests(tests);
}

#[test]
fn test_array_multiple_ussage() {
let tests = vec![
VmTestCase {
input: r#"
let array = [1,2,3];
let value = array[1] + array[2];
value"#
.to_string(),
expected: Object::INTEGER(5),
},
VmTestCase {
input: r#"
let array = [1,2,3];
let array = push(array, 4);
array"#
.to_string(),
expected: Object::ARRAY(vec![
Object::INTEGER(1),
Object::INTEGER(2),
Object::INTEGER(3),
Object::INTEGER(4),
]),
},
];
run_vm_tests(tests);
}

#[test]
fn test_shadowing_same_type() {
let tests = vec![
VmTestCase {
input: r#"
let x = 4;
let x = 5;
x"#
.to_string(),
expected: Object::INTEGER(5),
},
VmTestCase {
input: r#"
let x = [1,2,3];
let x = [4,5,6];
x"#
.to_string(),
expected: Object::ARRAY(vec![
Object::INTEGER(4),
Object::INTEGER(5),
Object::INTEGER(6),
]),
},
VmTestCase {
input: r#"
let x = fn() { 1 };
let x = fn() { 2 };
x()"#
.to_string(),
expected: Object::INTEGER(2),
},
];

run_vm_tests(tests);
}

#[test]
fn test_shadowing_with_new_type() {
let tests = vec![
VmTestCase {
input: r#"
let x = 4;
let x = "string";
x"#
.to_string(),
expected: Object::STRING("string".to_string()),
},
VmTestCase {
input: r#"
let x = "string";
let x = fn() { 1 };
x()"#
.to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: r#"
let x = fn() { 1 };
let x = 5,
x"#
.to_string(),
expected: Object::INTEGER(5),
},
VmTestCase {
input: r#"
let x = 5;
let x = [1,2,3];
x"#
.to_string(),
expected: Object::ARRAY(vec![
Object::INTEGER(1),
Object::INTEGER(2),
Object::INTEGER(3),
]),
},
VmTestCase {
input: r#"
let x = [1,2,3];
let x = 5;
x"#
.to_string(),
expected: Object::INTEGER(5),
},
];

run_vm_tests(tests);
}

#[test]
fn test_shadowing_using_previous_value() {
let tests = vec![
VmTestCase {
input: r#"
let a = 1;
let b = a * a + 2
b"#
.to_string(),
expected: Object::INTEGER(3),
},
VmTestCase {
input: r#"
let a = 1;
let a = a + 1;
a"#
.to_string(),
expected: Object::INTEGER(2),
},
VmTestCase {
input: r#"
let a = fn() {
let a = 1;
a
};
a()
"#
.to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: r#"
let f = fn(a){
let a = 1;
a
};
f(1)
"#
.to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: r#"
let f = fn(){
let a = 1;
let h = fn(){
let a = 2;
a
};
h() + a
};
f()
"#
.to_string(),
expected: Object::INTEGER(3),
},
// Addition of a global variable a with 10 as its value
VmTestCase {
input: r#"
let a = 10;
let a = fn() {
let a = 1;
a
};
a()
"#
.to_string(),
expected: Object::INTEGER(1),
},
VmTestCase {
input: r#"
let a = 10;
let f = fn(a){
let a = 1;
a
};
f(1) + a
"#
.to_string(),
expected: Object::INTEGER(11),
},
VmTestCase {
input: r#"
let a = 10;
let f = fn(){
let h = fn(){
let a = 2;
a
};
h()
};
f() + a
"#
.to_string(),
expected: Object::INTEGER(12),
},
VmTestCase {
input: r#"
let a = 10;
let f = fn(){
let a = 1;
let h = fn(){
let a = 2;
a
};
h() + a
};
f() + a
"#
.to_string(),
expected: Object::INTEGER(13),
},
];

run_vm_tests(tests);
}
}

0 comments on commit be91040

Please sign in to comment.