aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler.rs
diff options
context:
space:
mode:
authorEdvard Thörnros <edvard.thornros@gmail.com>2021-02-18 19:51:30 +0100
committerEdvard Thörnros <edvard.thornros@gmail.com>2021-02-18 19:51:30 +0100
commit75b2027c7b545b9827607f17ea7444f67622999d (patch)
treed6cc2e077c96ce0cfb683c0e3e0c2ab3fe4b997c /src/compiler.rs
parent090dd8c52e4ae60742fe8bad7b74e18bb808ba0d (diff)
parent6bb72c7bbe2bbb1b627809d4b52f44a77bdb4b33 (diff)
downloadsylt-75b2027c7b545b9827607f17ea7444f67622999d.tar.gz
Merge branch 'unusued-variables' into fix-constant-bug
Diffstat (limited to 'src/compiler.rs')
-rw-r--r--src/compiler.rs171
1 files changed, 110 insertions, 61 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index c70640d..7b9fd9a 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -113,12 +113,13 @@ nextable_enum!(Prec {
Factor,
});
-#[derive(Clone)]
+#[derive(Clone, Debug)]
struct Variable {
name: String,
typ: Type,
scope: usize,
slot: usize,
+ line: usize,
outer_slot: usize,
outer_upvalue: bool,
@@ -127,6 +128,28 @@ struct Variable {
upvalue: bool,
captured: bool,
mutable: bool,
+ read: bool,
+}
+
+impl Variable {
+ fn new(name: &str, mutable: bool, typ: Type) -> Self {
+ Self {
+ name: String::from(name),
+ typ,
+ scope: 0,
+ slot: 0,
+ line: 0,
+
+ outer_slot: 0,
+ outer_upvalue: false,
+
+ active: false,
+ upvalue: false,
+ captured: false,
+ mutable,
+ read: false,
+ }
+ }
}
enum LoopOp {
@@ -242,10 +265,27 @@ macro_rules! push_frame {
$compiler.frames.push(Frame::new());
// Return value stored as a variable
- $compiler.define_variable("", Type::Unknown, &mut $block).unwrap();
+ let var = Variable::new("", true, Type::Unknown);
+ $compiler.define(var).unwrap();
+
$code
- $compiler.frames.pop().unwrap();
+ let frame = $compiler.frames.pop().unwrap();
+ // 0-th slot is the function itself.
+ for var in frame.stack.iter().skip(1) {
+ if !(var.read || var.upvalue) {
+ let e = ErrorKind::SyntaxError(
+ var.line,
+ Token::Identifier(var.name.clone()
+ ));
+ $compiler.error_on_line(
+ e,
+ var.line,
+ Some(format!("Usage of undefined value: '{}'.", var.name))
+ );
+ }
+ $compiler.panic = false;
+ }
// The 0th slot is the return value, which is passed out
// from functions, and should not be popped.
0
@@ -262,13 +302,30 @@ macro_rules! push_scope {
$compiler.frame_mut().scope -= 1;
- for var in $compiler.frame().stack[ss..$compiler.stack().len()].iter().rev() {
+ let mut errors = Vec::new();
+ for var in $compiler.frame().stack.iter().skip(ss).rev() {
+ if !(var.read || var.upvalue) {
+ let e = ErrorKind::SyntaxError(
+ var.line,
+ Token::Identifier(var.name.clone()
+ ));
+ errors.push((
+ e,
+ var.line,
+ format!("Usage of undefined value: '{}'.", var.name),)
+ );
+ }
if var.captured {
add_op($compiler, $block, Op::PopUpvalue);
} else {
add_op($compiler, $block, Op::Pop);
}
}
+
+ for (e, l, m) in errors.iter() {
+ $compiler.error_on_line(e.clone(), *l, Some(m.clone()));
+ $compiler.panic = false;
+ }
$compiler.stack_mut().truncate(ss);
};
}
@@ -337,6 +394,26 @@ impl Compiler {
&mut self.frames[last]
}
+ /// Marks a variable as read, also marks upvalues.
+ fn mark_read(&mut self, frame_id: usize, var: &Variable) {
+ // Early out
+ if var.read {
+ return;
+ }
+
+
+ if if let Some(up) = self.frames[frame_id].upvalues.get(var.slot) {
+ up.name == var.name
+ } else { false } {
+ let mut inner_var = self.frames[frame_id].upvalues[var.slot].clone();
+ inner_var.slot = inner_var.outer_slot;
+ self.mark_read(frame_id - 1, &inner_var);
+ self.frames[frame_id].upvalues[var.slot].read = true;
+ } else {
+ self.frames[frame_id].stack[var.slot].read = true;
+ }
+ }
+
fn stack(&self) -> &[Variable] {
&self.frame().stack.as_ref()
}
@@ -733,7 +810,8 @@ impl Compiler {
expect!(self, Token::Colon, "Expected ':' after parameter name.");
if let Ok(typ) = self.parse_type() {
args.push(typ.clone());
- if let Ok(slot) = self.define_variable(&name, typ, &mut function_block) {
+ let var = Variable::new(&name, true, typ);
+ if let Ok(slot) = self.define(var) {
self.stack_mut()[slot].active = true;
}
} else {
@@ -814,6 +892,7 @@ impl Compiler {
// Variables
if let Some(var) = self.find_variable(&name) {
+ self.mark_read(self.frames.len() - 1, &var);
if var.upvalue {
add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
@@ -846,58 +925,26 @@ impl Compiler {
parse_branch!(self, block, self.call(block));
}
- 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(());
- }
- }
-
- let slot = self.stack().len();
- 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,
- upvalue: false,
- mutable: true,
- });
- Ok(slot)
- }
-
- fn define_constant(&mut self, name: &str, typ: Type, _block: &mut Block) -> Result<usize, ()> {
- if let Some(var) = self.find_variable(&name) {
+ fn define(&mut self, mut var: Variable) -> Result<usize, ()> {
+ if let Some(var) = self.find_variable(&var.name) {
if var.scope == self.frame().scope {
- error!(self, format!("Multiple definitions of {} in this block.", name));
+ error!(self, format!("Multiple definitions of '{}' in this block.",
+ var.name));
return Err(());
}
}
let slot = self.stack().len();
- 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,
- upvalue: false,
- mutable: false,
- });
+ var.slot = slot;
+ var.scope = self.frame().scope;
+ var.line = self.line();
+ self.stack_mut().push(var);
Ok(slot)
}
fn definition_statement(&mut self, name: &str, typ: Type, block: &mut Block) {
- let slot = self.define_variable(name, typ.clone(), block);
+ let var = Variable::new(name, true, typ.clone());
+ let slot = self.define(var);
self.expression(block);
let constant = self.add_constant(Value::Ty(typ));
add_op(self, block, Op::Define(constant));
@@ -931,7 +978,8 @@ impl Compiler {
}
}
- let slot = self.define_constant(name, typ.clone(), block);
+ let var = Variable::new(name, false, typ);
+ let slot = self.define(var);
self.expression(block);
if let Ok(slot) = slot {
@@ -1208,6 +1256,7 @@ impl Compiler {
_ => unreachable!(),
};
if let Some(var) = self.find_variable(&name) {
+ self.mark_read(self.frames.len() - 1, &var);
if var.upvalue {
add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
@@ -1382,23 +1431,14 @@ impl Compiler {
.enumerate()
.map(|(i, (s, f))| (s, (i, f)))
.collect();
- 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,
- mutable: true,
- });
+ let main = Variable::new("/main/", false, Type::Void);
+ let _ = self.define(main);
let mut block = Block::new(name, file);
while self.peek() != Token::EOF {
self.statement(&mut block);
- expect!(self, Token::Newline | Token::EOF, "Expect newline or EOF after expression.");
+ expect!(self, Token::Newline | Token::EOF,
+ "Expect newline or EOF after expression.");
}
add_op(self, &mut block, Op::Constant(self.nil_value()));
add_op(self, &mut block, Op::Return);
@@ -1416,6 +1456,15 @@ impl Compiler {
}
}
+ for var in self.frames.pop().unwrap().stack.iter().skip(1) {
+ if !(var.read || var.upvalue) {
+ let e = ErrorKind::SyntaxError(var.line, Token::Identifier(var.name.clone()));
+ let m = format!("Unused value '{}'.", var.name);
+ self.error_on_line(e, var.line, Some(m));
+ }
+ self.panic = false;
+ }
+
self.blocks.insert(0, Rc::new(RefCell::new(block)));
if self.errors.is_empty() {