aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler.rs149
-rw-r--r--src/lib.rs59
-rw-r--r--src/tokenizer.rs2
-rw-r--r--src/vm.rs22
4 files changed, 199 insertions, 33 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index e0b65c1..f2daa6e 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -126,6 +126,7 @@ struct Variable {
active: bool,
upvalue: bool,
captured: bool,
+ mutable: bool,
}
enum LoopOp {
@@ -437,7 +438,7 @@ impl Compiler {
Token::Bool(_) => self.value(block),
Token::String(_) => self.value(block),
- Token::Not => self.unary(block),
+ Token::Bang => self.unary(block),
_ => { return false; },
}
@@ -537,7 +538,7 @@ impl Compiler {
fn unary(&mut self, block: &mut Block) {
let op = match self.eat() {
Token::Minus => Op::Neg,
- Token::Not => Op::Not,
+ Token::Bang => Op::Not,
_ => { error!(self, "Invalid unary operator"); Op::Neg },
};
self.parse_precedence(block, Prec::Factor);
@@ -627,27 +628,67 @@ impl Compiler {
}
fn call(&mut self, block: &mut Block) {
- expect!(self, Token::LeftParen, "Expected '(' at start of function call.");
-
let mut arity = 0;
- loop {
- match self.peek() {
- Token::EOF => {
- error!(self, "Unexpected EOF in function call.");
- break;
- }
- Token::RightParen => {
- self.eat();
- break;
+ match self.peek() {
+ Token::LeftParen => {
+ self.eat();
+ loop {
+ match self.peek() {
+ Token::EOF => {
+ error!(self, "Unexpected EOF in function call.");
+ break;
+ }
+ Token::RightParen => {
+ self.eat();
+ break;
+ }
+ _ => {
+ self.expression(block);
+ arity += 1;
+ if !matches!(self.peek(), Token::RightParen) {
+ expect!(self, Token::Comma, "Expected ',' after argument.");
+ }
+ }
+ }
+ if self.panic {
+ break;
+ }
}
- _ => {
- self.expression(block);
- arity += 1;
- if !matches!(self.peek(), Token::RightParen) {
- expect!(self, Token::Comma, "Expected ',' after argument.");
+ },
+
+ Token::Bang => {
+ self.eat();
+ loop {
+ match self.peek() {
+ Token::EOF => {
+ error!(self, "Unexpected EOF in function call.");
+ break;
+ }
+ Token::Newline => {
+ break;
+ }
+ _ => {
+ if !parse_branch!(self, block, self.expression(block)) {
+ break;
+ }
+ arity += 1;
+ if matches!(self.peek(), Token::Comma) {
+ self.eat();
+ }
+ }
}
+ if self.panic {
+ break;
+ }
+ }
+ if !self.panic {
+ println!("LINE {} -- ", self.line());
}
}
+
+ _ => {
+ error!(self, "Invalid function call. Expected '!' or '('.");
+ }
}
add_op(self, block, Op::Call(arity));
@@ -766,18 +807,17 @@ impl Compiler {
break;
}
}
- Token::LeftParen => {
- self.call(block);
+ _ => {
+ if !parse_branch!(self, block, self.call(block)) {
+ break
+ }
}
- _ => { break }
}
}
} else if let Some(blob) = self.find_blob(&name) {
let string = self.add_constant(Value::Blob(blob));
add_op(self, block, Op::Constant(string));
- if self.peek() == Token::LeftParen {
- self.call(block);
- }
+ parse_branch!(self, block, self.call(block));
} else if let Some(slot) = self.find_extern_function(&name) {
let string = self.add_constant(Value::ExternFunction(slot));
add_op(self, block, Op::Constant(string));
@@ -807,6 +847,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)
}
@@ -822,6 +888,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,
@@ -846,8 +921,16 @@ 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 {
- add_op(self, block, Op::Copy);
+ if var.upvalue {
+ add_op(self, block, Op::ReadUpvalue(var.slot));
+ } else {
+ add_op(self, block, Op::ReadLocal(var.slot));
+ }
self.expression(block);
add_op(self, block, op);
} else {
@@ -1110,15 +1193,14 @@ impl Compiler {
add_op(self, block, Op::Set(field));
return;
}
- Token::LeftParen => {
- self.call(block);
- }
Token::Newline => {
return;
}
_ => {
- error!(self, "Unexpected token when parsing blob-field.");
- return;
+ if !parse_branch!(self, block, self.call(block)) {
+ error!(self, "Unexpected token when parsing blob-field.");
+ return;
+ }
}
}
}
@@ -1174,6 +1256,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);
}
@@ -1246,6 +1334,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 eec05d0..ee176e5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -813,6 +813,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 {
@@ -856,7 +866,7 @@ mod tests {
test_multiple!(
if_,
compare_constants_equality: "if 1 == 2 {
- <!>
+ <!>
}",
compare_constants_unequality: "if 1 != 1 {
<!>
@@ -1054,6 +1064,14 @@ a.a <=> 0"
);
test_multiple!(
+ fancy_call,
+ not: "f := fn {}\n f!\n",
+ one_arg: "f := fn a:int { a <=> 1 }\n f! 1\n",
+ two_arg: "f := fn a:int, b:int { b <=> 3 }\n f! 1, 1 + 2\n",
+ three_arg: "f := fn a:int, b:int, c:int { c <=> 13 }\n f! 1, 1 + 2, 1 + 4 * 3\n",
+ );
+
+ test_multiple!(
newline_regression,
simple: "a := 1 // blargh \na += 1 // blargh \n a <=> 2 // HARGH",
expressions: "1 + 1 // blargh \n 2 // blargh \n // HARGH \n",
@@ -1114,6 +1132,45 @@ for i := 0, i < 4, i += 1 {
}
a <=> 3
",
+ );
+
+ test_multiple!(
+ read_constants,
+ simple: "
+a :: 1
+a <=> 1
+b := 2
+{
+ a <=> 1
+}",
+ );
+ test_multiple!(
+ assignment_op_regression,
+ simple_add: "
+a := 0
+b := 99999
+a += 1
+a <=> 1
+",
+
+ simple_sub: "
+a := 0
+b := 99999
+a -= 1
+a <=> -1
+",
+
+ strange: "
+a := 0
+{
+ b := 99999
+ {
+ a := 99999
+ }
+ a -= 1
+}
+a <=> -1
+",
);
}
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index d7b28aa..b54e194 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -116,7 +116,7 @@ pub enum Token {
#[token("||")]
Or,
#[token("!")]
- Not,
+ Bang,
#[token(",")]
Comma,
diff --git a/src/vm.rs b/src/vm.rs
index 057fdba..c4d72a1 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -63,9 +63,10 @@ pub struct VM {
pub print_blocks: bool,
pub print_ops: bool,
+ runtime: bool,
- extern_functions: Vec<RustFunction>,
+ extern_functions: Vec<RustFunction>,
}
#[derive(Eq, PartialEq)]
@@ -92,6 +93,7 @@ impl VM {
print_blocks: false,
print_ops: false,
+ runtime: false,
extern_functions: Vec::new()
}
@@ -158,10 +160,25 @@ impl VM {
self.frame().block.borrow().ops[ip]
}
+ fn print_stacktrace(&self) {
+ if !self.runtime { return; }
+
+ println!("\n<{}>", "STACK".red());
+ for (i, frame) in self.frames.iter().enumerate() {
+ println!(" {:>3}. {}:{:<4} in {:10}",
+ i,
+ frame.block.borrow().file.display(),
+ frame.block.borrow().line(self.frame().ip),
+ frame.block.borrow().name.blue());
+ }
+ println!("")
+ }
+
/// Stop the program, violently
fn crash_and_burn(&self) -> ! {
self.print_stack();
println!("\n");
+ self.print_stacktrace();
self.frame().block.borrow().debug_print();
println!(" ip: {}, line: {}\n",
self.frame().ip.blue(),
@@ -171,6 +188,7 @@ impl VM {
fn error(&self, kind: ErrorKind, message: Option<String>) -> Error {
let frame = self.frames.last().unwrap();
+ self.print_stacktrace();
Error {
kind,
file: frame.block.borrow().file.clone(),
@@ -472,6 +490,7 @@ impl VM {
self.extern_functions = prog.functions.clone();
self.stack.clear();
self.frames.clear();
+ self.runtime = true;
self.push(Value::Function(Vec::new(), Rc::clone(&block)));
@@ -755,6 +774,7 @@ impl VM {
self.blobs = prog.blobs.clone();
self.constants = prog.constants.clone();
self.strings = prog.strings.clone();
+ self.runtime = false;
self.extern_functions = prog.functions.clone();
for block in prog.blocks.iter() {