aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/compiler.rs47
-rw-r--r--src/lib.rs23
2 files changed, 69 insertions, 1 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 128b4a8..eb6058a 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -126,6 +126,7 @@ struct Variable {
active: bool,
upvalue: bool,
captured: bool,
+ mutable: bool,
}
struct Frame {
@@ -798,6 +799,32 @@ impl Compiler {
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) {
+ 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: false,
});
Ok(slot)
}
@@ -813,6 +840,15 @@ impl Compiler {
}
}
+ fn constant_statement(&mut self, name: &str, typ: Type, block: &mut Block) {
+ let slot = self.define_constant(name, typ.clone(), block);
+ self.expression(block);
+
+ if let Ok(slot) = slot {
+ self.stack_mut()[slot].active = true;
+ }
+ }
+
fn assign(&mut self, block: &mut Block) {
let name = match self.eat() {
Token::Identifier(name) => name,
@@ -837,6 +873,10 @@ impl Compiler {
};
if let Some(var) = self.find_variable(&name) {
+ if !var.mutable {
+ // TODO(ed): Maybe a better error than "SyntaxError".
+ error!(self, format!("Cannot assign to constant '{}'", var.name));
+ }
if let Some(op) = op {
if var.upvalue {
add_op(self, block, Op::ReadUpvalue(var.slot));
@@ -1165,6 +1205,12 @@ impl Compiler {
self.definition_statement(&name, Type::Unknown, block);
}
+ (Token::Identifier(name), Token::ColonColon, ..) => {
+ self.eat();
+ self.eat();
+ self.constant_statement(&name, Type::Unknown, block);
+ }
+
(Token::Blob, Token::Identifier(_), ..) => {
self.blob_statement(block);
}
@@ -1219,6 +1265,7 @@ impl Compiler {
active: false,
captured: false,
upvalue: false,
+ mutable: true,
});
let mut block = Block::new(name, file, 0);
diff --git a/src/lib.rs b/src/lib.rs
index 862e9fa..22b6713 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -805,6 +805,16 @@ mod tests {
assert_errs!(run_string("<!>\n", true, Vec::new()), [ErrorKind::Unreachable]);
}
+ #[test]
+ fn assign_to_constant() {
+ assert_errs!(run_string("a :: 2\na = 2", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]);
+ }
+
+ #[test]
+ fn assign_to_constant_upvalue() {
+ assert_errs!(run_string("a :: 2\nq :: fn { a = 2 }\n", true, Vec::new()), [ErrorKind::SyntaxError(_, _)]);
+ }
+
macro_rules! test_multiple {
($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => {
mod $mod {
@@ -848,7 +858,7 @@ mod tests {
test_multiple!(
if_,
compare_constants_equality: "if 1 == 2 {
- <!>
+ <!>
}",
compare_constants_unequality: "if 1 != 1 {
<!>
@@ -1060,6 +1070,17 @@ a.a <=> 0"
);
test_multiple!(
+ read_constants,
+ simple: "
+a :: 1
+a <=> 1
+b := 2
+{
+ a <=> 1
+}",
+ );
+
+ test_multiple!(
assignment_op_regression,
simple_add: "
a := 0