aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-01-21 20:59:38 +0100
committerGustav Sörnäs <gustav@sornas.net>2021-01-21 20:59:38 +0100
commitca1a394cca0c88b7656d156c9ba68bd9468a6612 (patch)
treefc6ec74eabfb0950fc275676815c0e72474737ab /src
parent9e77bcebae382b5b5f712d87328b56129ccfe890 (diff)
parent4448657f8443842e3ef75353c5fa87dfebb3cffd (diff)
downloadsylt-ca1a394cca0c88b7656d156c9ba68bd9468a6612.tar.gz
Merge remote-tracking branch 'origin/wip-close' into main
Diffstat (limited to 'src')
-rw-r--r--src/compiler.rs185
-rw-r--r--src/lib.rs52
-rw-r--r--src/vm.rs251
3 files changed, 417 insertions, 71 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 1a6cfc6..70c9a51 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -1,12 +1,13 @@
use std::path::{Path, PathBuf};
use std::rc::Rc;
+use std::cell::RefCell;
use crate::error::{Error, ErrorKind};
use crate::tokenizer::{Token, TokenStream};
use crate::vm::{Value, Block, Op};
macro_rules! nextable_enum {
- ( $name:ident { $( $thing:ident ),* } ) => {
+ ( $name:ident { $( $thing:ident ),* $( , )? } ) => {
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
enum $name {
$( $thing, )*
@@ -43,7 +44,7 @@ nextable_enum!(Prec {
Bool,
Comp,
Term,
- Factor
+ Factor,
});
#[derive(Debug, Clone)]
@@ -79,13 +80,20 @@ impl From<&Value> for Type {
Value::Float(_) => Type::Float,
Value::Bool(_) => Type::Bool,
Value::String(_) => Type::String,
- Value::Function(block) => block.ty.clone(),
+ Value::Function(_, block) => block.borrow().ty.clone(),
_ => Type::Void,
}
}
}
impl Type {
+ pub fn is_unkown(&self) -> bool {
+ match self {
+ Type::UnknownType => true,
+ _ => false,
+ }
+ }
+
pub fn as_value(&self) -> Value {
match self {
Type::Void => Value::Nil,
@@ -94,24 +102,68 @@ impl Type {
Type::Float => Value::Float(1.0),
Type::Bool => Value::Bool(true),
Type::String => Value::String(Rc::new("".to_string())),
- Type::Function(_, _) => Value::Function(Rc::new(Block::from_type(self))),
+ Type::Function(_, _) => Value::Function(
+ Vec::new(),
+ Rc::new(RefCell::new(Block::from_type(self)))),
}
}
}
+#[derive(Clone)]
struct Variable {
name: String,
typ: Type,
scope: usize,
+ slot: usize,
+
+ outer_slot: usize,
+ outer_upvalue: bool,
+
active: bool,
+ upvalue: bool,
+ captured: bool,
}
struct Frame {
stack: Vec<Variable>,
+ upvalues: Vec<Variable>,
scope: usize,
variables_below: usize,
}
+impl Frame {
+ fn find_local(&self, name: &str) -> Option<Variable> {
+ for var in self.stack.iter().rev() {
+ if var.name == name && var.active {
+ return Some(var.clone());
+ }
+ }
+ None
+ }
+
+ fn find_upvalue(&self, name: &str) -> Option<Variable> {
+ for var in self.upvalues.iter().rev() {
+ if var.name == name && var.active {
+ return Some(var.clone());
+ }
+ }
+ None
+ }
+
+ fn add_upvalue(&mut self, variable: Variable) -> Variable {
+ let new_variable = Variable {
+ outer_upvalue: variable.upvalue,
+ outer_slot: variable.slot,
+ slot: self.upvalues.len(),
+ active: true,
+ upvalue: true,
+ ..variable
+ };
+ self.upvalues.push(new_variable.clone());
+ new_variable
+ }
+}
+
struct Compiler {
curr: usize,
tokens: TokenStream,
@@ -122,7 +174,7 @@ struct Compiler {
panic: bool,
errors: Vec<Error>,
- blocks: Vec<Rc<Block>>,
+ blocks: Vec<Rc<RefCell<Block>>>,
}
macro_rules! push_frame {
@@ -130,6 +182,7 @@ macro_rules! push_frame {
{
$compiler.frames.push(Frame {
stack: Vec::new(),
+ upvalues: Vec::new(),
scope: 0,
variables_below: $compiler.frame().variables_below + $compiler.stack().len(),
});
@@ -154,8 +207,13 @@ macro_rules! push_scope {
$code;
$compiler.frame_mut().scope -= 1;
- for _ in ss..$compiler.stack().len() {
- $block.add(Op::Pop, $compiler.line());
+
+ for var in $compiler.frame().stack[ss..$compiler.stack().len()].iter().rev() {
+ if var.captured {
+ $block.add(Op::PopUpvalue, $compiler.line());
+ } else {
+ $block.add(Op::Pop, $compiler.line());
+ }
}
$compiler.stack_mut().truncate(ss);
};
@@ -170,6 +228,7 @@ impl Compiler {
frames: vec![Frame {
stack: Vec::new(),
+ upvalues: Vec::new(),
scope: 0,
variables_below: 0,
}],
@@ -311,8 +370,6 @@ impl Compiler {
| Token::NotEqual
=> self.binary(block),
- Token::LeftParen => self.call(block),
-
_ => { return false; },
}
return true;
@@ -388,16 +445,36 @@ impl Compiler {
}
}
- fn find_local(&self, name: &str, _block: &Block) -> Option<(usize, Type, usize)> {
- let frame = self.frame();
- for (slot, var) in frame.stack.iter().enumerate().rev() {
- if var.name == name && var.active {
- return Some((slot, var.typ.clone(), var.scope));
+ fn find_and_capture_variable<'a, I>(name: &str, mut iterator: I) -> Option<Variable>
+ where I: Iterator<Item = &'a mut Frame> {
+ if let Some(frame) = iterator.next() {
+ if let Some(res) = frame.find_local(name) {
+ frame.stack[res.slot].captured = true;
+ return Some(res);
+ }
+ if let Some(res) = frame.find_upvalue(name) {
+ return Some(res);
+ }
+
+ if let Some(res) = Self::find_and_capture_variable(name, iterator) {
+ return Some(frame.add_upvalue(res));
}
}
None
}
+ fn find_variable(&mut self, name: &str) -> Option<Variable> {
+ if let Some(res) = self.frame().find_local(name) {
+ return Some(res);
+ }
+
+ if let Some(res) = self.frame().find_upvalue(name) {
+ return Some(res);
+ }
+
+ return Self::find_and_capture_variable(name, self.frames.iter_mut().rev());
+ }
+
fn call(&mut self, block: &mut Block) {
expect!(self, Token::LeftParen, "Expected '(' at start of function call.");
@@ -423,17 +500,15 @@ impl Compiler {
}
block.add(Op::Call(arity), self.line());
-
- for _ in 0..arity {
- block.add(Op::Pop, self.line());
- }
}
fn function(&mut self, block: &mut Block) {
expect!(self, Token::Fn, "Expected 'fn' at start of function.");
- let name = if !self.stack()[self.stack().len() - 1].active {
- &self.stack()[self.stack().len() - 1].name
+ let top = self.stack().len() - 1;
+ let name = if !self.stack()[top].active {
+ self.stack_mut()[top].active = true;
+ &self.stack()[top].name
} else {
"anonumus function"
};
@@ -441,6 +516,7 @@ impl Compiler {
let mut args = Vec::new();
let mut return_type = Type::Void;
let mut function_block = Block::new(name, &self.current_file, self.line());
+
let _ret = push_frame!(self, function_block, {
loop {
match self.peek() {
@@ -479,35 +555,60 @@ impl Compiler {
}
self.scope(&mut function_block);
+
+ for var in self.frame().upvalues.iter() {
+ function_block.ups.push((var.outer_slot, var.outer_upvalue, var.typ.clone()));
+ }
});
- if !matches!(function_block.last_op(), Some(&Op::Return)) {
+ for op in function_block.ops.iter().rev() {
+ match op {
+ Op::Pop | Op::PopUpvalue => {}
+ Op::Return => { break; } ,
+ _ => {
+ function_block.add(Op::Constant(Value::Nil), self.line());
+ function_block.add(Op::Return, self.line());
+ break;
+ }
+ }
+ }
+
+ if function_block.ops.is_empty() {
function_block.add(Op::Constant(Value::Nil), self.line());
function_block.add(Op::Return, self.line());
}
function_block.ty = Type::Function(args, Box::new(return_type));
- let function_block = Rc::new(function_block);
+ let function_block = Rc::new(RefCell::new(function_block));
+
- block.add(Op::Constant(Value::Function(Rc::clone(&function_block))), self.line());
+ let func = Op::Constant(Value::Function(Vec::new(), Rc::clone(&function_block)));
+ block.add(func, self.line());
self.blocks.push(function_block);
}
fn variable_expression(&mut self, block: &mut Block) {
let name = match self.eat() {
Token::Identifier(name) => name,
- __ => unreachable!(),
+ _ => unreachable!(),
};
- if let Some((slot, _, _)) = self.find_local(&name, block) {
- block.add(Op::ReadLocal(slot), self.line());
+ if let Some(var) = self.find_variable(&name) {
+ if var.upvalue {
+ block.add(Op::ReadUpvalue(var.slot), self.line());
+ } else {
+ block.add(Op::ReadLocal(var.slot), self.line());
+ }
+ if self.peek() == Token::LeftParen {
+ self.call(block);
+ }
} else {
error!(self, format!("Using undefined variable {}.", name));
}
}
- fn define_variable(&mut self, name: &str, typ: Type, block: &mut Block) -> Result<usize, ()> {
- if let Some((_, _, level)) = self.find_local(&name, block) {
- if level == self.frame().scope {
+ fn define_variable(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result<usize, ()> {
+ if let Some(var) = self.find_variable(&name) {
+ if var.scope == self.frame().scope {
error!(self, format!("Multiple definitions of {} in this block.", name));
return Err(());
}
@@ -517,9 +618,14 @@ impl Compiler {
let scope = self.frame().scope;
self.stack_mut().push(Variable {
name: String::from(name),
+ captured: false,
+ outer_upvalue: false,
+ outer_slot: 0,
+ slot,
typ,
scope,
- active: false
+ active: false,
+ upvalue: false,
});
Ok(slot)
}
@@ -535,9 +641,13 @@ impl Compiler {
}
fn assign(&mut self, name: &str, block: &mut Block) {
- if let Some((slot, _, _)) = self.find_local(&name, block) {
+ if let Some(var) = self.find_variable(&name) {
self.expression(block);
- block.add(Op::Assign(slot), self.line());
+ if var.upvalue {
+ block.add(Op::AssignUpvalue(var.slot), self.line());
+ } else {
+ block.add(Op::AssignLocal(var.slot), self.line());
+ }
} else {
error!(self, format!("Using undefined variable {}.", name));
}
@@ -743,12 +853,17 @@ impl Compiler {
}
- pub fn compile(&mut self, name: &str, file: &Path) -> Result<Vec<Rc<Block>>, Vec<Error>> {
+ pub fn compile(&mut self, name: &str, file: &Path) -> Result<Vec<Rc<RefCell<Block>>>, Vec<Error>> {
self.stack_mut().push(Variable {
name: String::from("/main/"),
typ: Type::Void,
+ outer_upvalue: false,
+ outer_slot: 0,
+ slot: 0,
scope: 0,
active: false,
+ captured: false,
+ upvalue: false,
});
let mut block = Block::new(name, file, 0);
@@ -760,7 +875,7 @@ impl Compiler {
block.add(Op::Return, self.line());
block.ty = Type::Function(Vec::new(), Box::new(Type::Void));
- self.blocks.insert(0, Rc::new(block));
+ self.blocks.insert(0, Rc::new(RefCell::new(block)));
if self.errors.is_empty() {
Ok(self.blocks.clone())
@@ -770,6 +885,6 @@ impl Compiler {
}
}
-pub fn compile(name: &str, file: &Path, tokens: TokenStream) -> Result<Vec<Rc<Block>>, Vec<Error>> {
+pub fn compile(name: &str, file: &Path, tokens: TokenStream) -> Result<Vec<Rc<RefCell<Block>>>, Vec<Error>> {
Compiler::new(file, tokens).compile(name, file)
}
diff --git a/src/lib.rs b/src/lib.rs
index 82905de..e83d11f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -169,7 +169,7 @@ mod tests {
simplest: "f := fn {}
f()",
param_1: "f := fn a: int {}
- f(1)",
+ f(1)",
return_1: "f := fn -> int {
ret 1
}
@@ -222,7 +222,55 @@ mod tests {
}
1 + f(2, 3) <=> 6
2 * f(2, 3) <=> 10
- f(2, 3) - (2 + 3) <=> 0"
+ f(2, 3) - (2 + 3) <=> 0",
+ factorial: "factorial : fn int -> int = fn n: int -> int {
+ if n <= 1 {
+ ret 1
+ }
+ ret n * factorial(n - 1)
+ }
+ factorial(5) <=> 120
+ factorial(6) <=> 720
+ factorial(12) <=> 479001600",
+
+ returning_closures: "
+f : fn -> fn -> int = fn -> fn -> int {
+ x : int = 0
+ f := fn -> int {
+ x = x + 1
+ ret x
+ }
+ f() <=> 1
+ ret f
+}
+
+a := f()
+b := f()
+
+a() <=> 2
+a() <=> 3
+
+b() <=> 2
+b() <=> 3
+
+a() <=> 4
+"
+
+ //TODO this tests doesn't terminate in proper time if we print blocks and ops
+ /*
+ fibonacci: "fibonacci : fn int -> int = fn n: int -> int {
+ if n == 0 {
+ ret 0
+ } else if n == 1 {
+ ret 1
+ } else if n < 0 {
+ <!>
+ }
+ ret fibonacci(n - 1) + fibonacci(n - 2)
+ }
+ fibonacci(10) <=> 55
+ fibonacci(20) <=> 6765"
+ */
);
test_file!(scoping, "tests/scoping.tdy");
diff --git a/src/vm.rs b/src/vm.rs
index 75a8eb1..5f7e0e3 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -1,8 +1,10 @@
use owo_colors::OwoColorize;
use std::collections::HashMap;
+use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::rc::Rc;
+use std::cell::RefCell;
use crate::compiler::Type;
use crate::error::{Error, ErrorKind};
@@ -22,11 +24,53 @@ pub enum Value {
Int(i64),
Bool(bool),
String(Rc<String>),
- Function(Rc<Block>),
+ Function(Vec<Rc<RefCell<UpValue>>>, Rc<RefCell<Block>>),
Unkown,
Nil,
}
+#[derive(Clone, Debug)]
+pub struct UpValue {
+ slot: usize,
+ value: Value,
+}
+
+impl UpValue {
+
+ fn new(value: usize) -> Self {
+ Self {
+ slot: value,
+ value: Value::Nil,
+ }
+ }
+
+ fn get(&self, stack: &[Value]) -> Value {
+ if self.is_closed() {
+ self.value.clone()
+ } else {
+ stack[self.slot].clone()
+ }
+ }
+
+ fn set(&mut self, stack: &mut [Value], value: Value) {
+ if self.is_closed() {
+ self.value = value;
+ } else {
+ stack[self.slot] = value;
+ }
+ }
+
+
+ fn is_closed(&self) -> bool {
+ self.slot == 0
+ }
+
+ fn close(&mut self, value: Value) {
+ self.slot = 0;
+ self.value = value;
+ }
+}
+
impl Debug for Value {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@@ -34,7 +78,7 @@ impl Debug for Value {
Value::Int(i) => write!(fmt, "(int {})", i),
Value::Bool(b) => write!(fmt, "(bool {})", b),
Value::String(s) => write!(fmt, "(string \"{}\")", s),
- Value::Function(block) => write!(fmt, "(fn {}: {:?})", block.name, block.ty),
+ Value::Function(_, block) => write!(fmt, "(fn {}: {:?})", block.borrow().name, block.borrow().ty),
Value::Unkown => write!(fmt, "(unkown)"),
Value::Nil => write!(fmt, "(nil)"),
}
@@ -57,7 +101,7 @@ impl Value {
Value::Int(_) => Type::Int,
Value::Bool(_) => Type::Bool,
Value::String(_) => Type::String,
- Value::Function(block) => block.ty.clone(),
+ Value::Function(_, block) => block.borrow().ty.clone(),
Value::Unkown => Type::UnknownType,
Value::Nil => Type::Void,
}
@@ -69,6 +113,7 @@ pub enum Op {
Illegal,
Pop,
+ PopUpvalue,
Constant(Value),
Add,
@@ -92,7 +137,10 @@ pub enum Op {
Unreachable,
ReadLocal(usize),
- Assign(usize),
+ AssignLocal(usize),
+
+ ReadUpvalue(usize),
+ AssignUpvalue(usize),
Define(Type),
@@ -105,6 +153,7 @@ pub enum Op {
#[derive(Debug)]
pub struct Block {
pub ty: Type,
+ pub ups: Vec<(usize, bool, Type)>,
pub name: String,
pub file: PathBuf,
@@ -118,6 +167,7 @@ impl Block {
pub fn new(name: &str, file: &Path, line: usize) -> Self {
Self {
ty: Type::Void,
+ ups: Vec::new(),
name: String::from(name),
file: file.to_owned(),
ops: Vec::new(),
@@ -216,12 +266,14 @@ impl Block {
#[derive(Debug)]
struct Frame {
stack_offset: usize,
- block: Rc<Block>,
+ block: Rc<RefCell<Block>>,
ip: usize,
}
#[derive(Debug)]
pub struct VM {
+ upvalues: HashMap<usize, Rc<RefCell<UpValue>>>,
+
stack: Vec<Value>,
frames: Vec<Frame>,
print_blocks: bool,
@@ -236,6 +288,7 @@ enum OpResult {
impl VM {
pub fn new() -> Self {
Self {
+ upvalues: HashMap::new(),
stack: Vec::new(),
frames: Vec::new(),
print_blocks: false,
@@ -253,6 +306,20 @@ impl VM {
self
}
+ fn drop_upvalue(&mut self, slot: usize, value: Value) {
+ if let Entry::Occupied(entry) = self.upvalues.entry(slot) {
+ entry.get().borrow_mut().close(value);
+ entry.remove();
+ } else {
+ unreachable!();
+ }
+ }
+
+ fn find_upvalue(&mut self, slot: usize) -> &mut Rc<RefCell<UpValue>> {
+ self.upvalues.entry(slot).or_insert(
+ Rc::new(RefCell::new(UpValue::new(slot))))
+ }
+
fn pop(&mut self) -> Value {
self.stack.pop().unwrap()
}
@@ -277,17 +344,17 @@ impl VM {
&mut self.frames[last]
}
- fn op(&self) -> &Op {
+ fn op(&self) -> Op {
let ip = self.frame().ip;
- &self.frame().block.ops[ip]
+ self.frame().block.borrow().ops[ip].clone()
}
fn error(&self, kind: ErrorKind, message: Option<String>) -> Error {
let frame = self.frames.last().unwrap();
Error {
kind,
- file: frame.block.file.clone(),
- line: frame.block.line(frame.ip),
+ file: frame.block.borrow().file.clone(),
+ line: frame.block.borrow().line(frame.ip),
message,
}
}
@@ -303,11 +370,38 @@ impl VM {
}
Op::Pop => {
- self.stack.pop();
+ self.stack.pop().unwrap();
+ }
+
+ Op::PopUpvalue => {
+ let value = self.stack.pop().unwrap();
+ let slot = self.stack.len();
+ self.drop_upvalue(slot, value);
}
Op::Constant(value) => {
- self.stack.push(value.clone());
+ let offset = self.frame().stack_offset;
+ let value = match value {
+ Value::Function(_, block) => {
+ let mut ups = Vec::new();
+ for (slot, is_up, _) in block.borrow().ups.iter() {
+ let up = if *is_up {
+ if let Value::Function(local_ups, _) = &self.stack[offset] {
+ Rc::clone(&local_ups[*slot])
+ } else {
+ unreachable!()
+ }
+ } else {
+ let slot = self.frame().stack_offset + slot;
+ Rc::clone(self.find_upvalue(slot))
+ };
+ ups.push(up);
+ }
+ Value::Function(ups, block)
+ },
+ _ => value.clone(),
+ };
+ self.stack.push(value);
}
Op::Neg => {
@@ -423,12 +517,33 @@ impl VM {
self.stack.push(Value::Bool(true));
}
+ Op::ReadUpvalue(slot) => {
+ let offset = self.frame().stack_offset;
+ let value = match &self.stack[offset] {
+ Value::Function(ups, _) => {
+ ups[slot].borrow().get(&self.stack)
+ }
+ _ => unreachable!(),
+ };
+ self.stack.push(value);
+ }
+
+ Op::AssignUpvalue(slot) => {
+ let offset = self.frame().stack_offset;
+ let value = self.stack.pop().unwrap();
+ let slot = match &self.stack[offset] {
+ Value::Function(ups, _) => Rc::clone(&ups[slot]),
+ _ => unreachable!(),
+ };
+ slot.borrow_mut().set(&mut self.stack, value);
+ }
+
Op::ReadLocal(slot) => {
let slot = self.frame().stack_offset + slot;
self.stack.push(self.stack[slot].clone());
}
- Op::Assign(slot) => {
+ Op::AssignLocal(slot) => {
let slot = self.frame().stack_offset + slot;
self.stack[slot] = self.stack.pop().unwrap();
}
@@ -438,16 +553,18 @@ impl VM {
Op::Call(num_args) => {
let new_base = self.stack.len() - 1 - num_args;
match &self.stack[new_base] {
- Value::Function(block) => {
- let args = block.args();
+ Value::Function(_, block) => {
+ let inner = block.borrow();
+ let args = inner.args();
if args.len() != num_args {
error!(self,
ErrorKind::InvalidProgram,
format!("Invalid number of arguments, got {} expected {}.",
num_args, args.len()));
}
+
if self.print_blocks {
- block.debug_print();
+ inner.debug_print();
}
self.frames.push(Frame {
stack_offset: new_base,
@@ -472,6 +589,13 @@ impl VM {
return Ok(OpResult::Done);
} else {
self.stack[last.stack_offset] = self.stack.pop().unwrap();
+ for slot in last.stack_offset+1..self.stack.len() {
+ if self.upvalues.contains_key(&slot) {
+ let value = self.stack[slot].clone();
+ self.drop_upvalue(slot, value);
+ }
+ }
+ self.stack.truncate(last.stack_offset + 1);
}
}
}
@@ -491,16 +615,16 @@ impl VM {
println!("]");
println!("{:5} {:05} {:?}",
- self.frame().block.line(self.frame().ip).red(),
+ self.frame().block.borrow().line(self.frame().ip).red(),
self.frame().ip.blue(),
- self.frame().block.ops[self.frame().ip]);
+ self.frame().block.borrow().ops[self.frame().ip]);
}
- pub fn run(&mut self, block: Rc<Block>) -> Result<(), Error>{
+ pub fn run(&mut self, block: Rc<RefCell<Block>>) -> Result<(), Error>{
self.stack.clear();
self.frames.clear();
- self.stack.push(Value::Function(Rc::clone(&block)));
+ self.stack.push(Value::Function(Vec::new(), Rc::clone(&block)));
self.frames.push(Frame {
stack_offset: 0,
@@ -510,7 +634,7 @@ impl VM {
if self.print_blocks {
println!("\n [[{}]]\n", "RUNNING".red());
- self.frame().block.debug_print();
+ self.frame().block.borrow().debug_print();
}
loop {
@@ -518,7 +642,7 @@ impl VM {
self.print_stack()
}
- if matches!(self.eval_op(self.op().clone())?, OpResult::Done) {
+ if matches!(self.eval_op(self.op())?, OpResult::Done) {
return Ok(());
}
}
@@ -530,9 +654,65 @@ impl VM {
Op::Jmp(_line) => {}
+ Op::Constant(ref value) => {
+ match value.clone() {
+ Value::Function(_, block) => {
+ self.stack.push(Value::Function(Vec::new(), block.clone()));
+
+ let mut types = Vec::new();
+ for (slot, is_up, _) in block.borrow().ups.iter() {
+ if *is_up {
+ types.push(Type::Void);
+ } else {
+ types.push(self.stack[*slot].as_type());
+ }
+ }
+
+ let mut block_mut = block.borrow_mut();
+ for (i, (_, is_up, ty)) in block_mut.ups.iter_mut().enumerate() {
+ if *is_up { continue; }
+
+ let suggestion = &types[i];
+ if ty.is_unkown() {
+ *ty = suggestion.clone();
+ } else {
+ if ty != suggestion {
+ error!(self,
+ ErrorKind::TypeError(op.clone(),
+ vec![ty.clone(), suggestion.clone()]),
+ "Failed to infer type.".to_string());
+ }
+ }
+ };
+ },
+ _ => {
+ self.stack.push(value.clone());
+ }
+ }
+ }
+
+ Op::PopUpvalue => {
+ self.stack.pop().unwrap();
+ }
+
+ Op::ReadUpvalue(slot) => {
+ let value = self.frame().block.borrow().ups[slot].2.as_value();
+ self.stack.push(value);
+ }
+
+ Op::AssignUpvalue(slot) => {
+ let var = self.frame().block.borrow().ups[slot].2.clone();
+ let up = self.stack.pop().unwrap().as_type();
+ if var != up {
+ error!(self, ErrorKind::TypeError(op, vec![var, up]),
+ "Incorrect type for upvalue.".to_string());
+ }
+ }
+
Op::Return => {
let a = self.stack.pop().unwrap();
- let ret = self.frame().block.ret();
+ let inner = self.frame().block.borrow();
+ let ret = inner.ret();
if a.as_type() != *ret {
error!(self, ErrorKind::TypeError(op, vec![a.as_type(),
ret.clone()]),
@@ -563,9 +743,10 @@ impl VM {
Op::Call(num_args) => {
let new_base = self.stack.len() - 1 - num_args;
- match &self.stack[new_base] {
- Value::Function(block) => {
- let args = block.args();
+ match self.stack[new_base].clone() {
+ Value::Function(_, block) => {
+ let inner = block.borrow();
+ let args = inner.args();
if args.len() != num_args {
error!(self,
ErrorKind::InvalidProgram,
@@ -582,7 +763,9 @@ impl VM {
args, stack_args));
}
- self.stack[new_base] = block.ret().as_value();
+ self.stack[new_base] = block.borrow().ret().as_value();
+
+ self.stack.truncate(new_base + 1);
},
_ => {
error!(self,
@@ -607,30 +790,30 @@ impl VM {
Ok(())
}
- fn typecheck_block(&mut self, block: Rc<Block>) -> Vec<Error> {
+ fn typecheck_block(&mut self, block: Rc<RefCell<Block>>) -> Vec<Error> {
self.stack.clear();
self.frames.clear();
- self.stack.push(Value::Function(Rc::clone(&block)));
- for arg in block.args() {
+ self.stack.push(Value::Function(Vec::new(), Rc::clone(&block)));
+ for arg in block.borrow().args() {
self.stack.push(arg.as_value());
}
self.frames.push(Frame {
stack_offset: 0,
- block: block,
+ block,
ip: 0
});
if self.print_blocks {
println!("\n [[{}]]\n", "TYPECHECK".purple());
- self.frame().block.debug_print();
+ self.frame().block.borrow().debug_print();
}
let mut errors = Vec::new();
loop {
let ip = self.frame().ip;
- if ip >= self.frame().block.ops.len() {
+ if ip >= self.frame().block.borrow().ops.len() {
break;
}
@@ -638,7 +821,7 @@ impl VM {
self.print_stack()
}
- if let Err(e) = self.check_op(self.op().clone()) {
+ if let Err(e) = self.check_op(self.op()) {
errors.push(e);
self.frame_mut().ip += 1;
}
@@ -651,7 +834,7 @@ impl VM {
errors
}
- pub fn typecheck(&mut self, blocks: &Vec<Rc<Block>>) -> Result<(), Vec<Error>> {
+ pub fn typecheck(&mut self, blocks: &Vec<Rc<RefCell<Block>>>) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();
for block in blocks.iter() {