aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler.rs26
-rw-r--r--src/lib.rs198
-rw-r--r--src/tokenizer.rs8
-rw-r--r--src/vm.rs11
4 files changed, 210 insertions, 33 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 2caa4e9..d12ee4b 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -161,6 +161,7 @@ macro_rules! push_scope {
};
}
+/// Helper function for adding operations to the given block.
fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize {
block.add(op, compiler.line())
}
@@ -207,6 +208,7 @@ impl Compiler {
&mut self.frame_mut().stack
}
+ /// Used to recover from a panic so the rest of the code can be parsed.
fn clear_panic(&mut self) {
if self.panic {
self.panic = false;
@@ -255,6 +257,15 @@ impl Compiler {
t
}
+ /// The line of the current token.
+ fn line(&self) -> usize {
+ if self.curr < self.tokens.len() {
+ self.tokens[self.curr].1
+ } else {
+ self.tokens[self.tokens.len() - 1].1
+ }
+ }
+
fn precedence(&self, token: Token) -> Prec {
match token {
Token::Star | Token::Slash => Prec::Factor,
@@ -277,14 +288,6 @@ impl Compiler {
}
}
- fn line(&self) -> usize {
- if self.curr < self.tokens.len() {
- self.tokens[self.curr].1
- } else {
- self.tokens[self.tokens.len() - 1].1
- }
- }
-
fn prefix(&mut self, token: Token, block: &mut Block) -> bool {
match token {
Token::Identifier(_) => self.variable_expression(block),
@@ -303,7 +306,6 @@ impl Compiler {
return true;
}
-
fn infix(&mut self, token: Token, block: &mut Block) -> bool {
match token {
Token::Minus
@@ -428,6 +430,7 @@ impl Compiler {
block.add_from(op, self.line());
}
+ /// Entry point for all expression parsing.
fn expression(&mut self, block: &mut Block) {
match self.peek_four() {
(Token::Fn, ..) => self.function(block),
@@ -514,6 +517,7 @@ impl Compiler {
add_op(self, block, Op::Call(arity));
}
+ // TODO(ed): de-complexify
fn function(&mut self, block: &mut Block) {
expect!(self, Token::Fn, "Expected 'fn' at start of function.");
@@ -807,6 +811,7 @@ impl Compiler {
fn parse_type(&mut self) -> Result<Type, ()> {
match self.peek() {
+
Token::Fn => {
self.eat();
let mut params = Vec::new();
@@ -839,6 +844,7 @@ impl Compiler {
let f = Type::Function(params, Box::new(return_type));
Ok(f)
}
+
Token::LeftParen => {
self.eat();
let mut element = Vec::new();
@@ -855,6 +861,7 @@ impl Compiler {
}
}
}
+
Token::Identifier(x) => {
self.eat();
match x.as_str() {
@@ -866,7 +873,6 @@ impl Compiler {
}
}
_ => Err(()),
-
}
}
diff --git a/src/lib.rs b/src/lib.rs
index bdafa03..cca1a58 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -281,69 +281,232 @@ impl Blob {
}
}
+///
+/// Ops are opperations that the virtual
+/// machine carries out when running the
+/// "byte-code".
+///
#[derive(Debug, Clone)]
pub enum Op {
+ /// This instruction should never be run.
+ /// Finding it in a program is a critical error.
Illegal,
+ /// Pops one value from the stack.
+ ///
+ /// {A, B} - Pop - {A}
Pop,
+ /// Assumes the value on the top of the
+ /// stack has an upvalue, and closes that
+ /// upvalue.
+ ///
+ /// {A, B} - Pop - {A}
PopUpvalue,
+ /// Copies the value on the top of the stack
+ /// and puts it on top of the stack.
+ ///
+ /// {A, B} - Copy - {A, B, B}
Copy,
+ /// Adds the given value to the top of the stack.
+ /// Also links upvalues if the value is a function.
+ ///
+ /// {A} - Constant(B) - {A, B}
Constant(Value),
+ /// Creates a new [Tuple] with the given size and place it on the top
+ /// of the stack.
+ ///
+ /// {A, B, C} - Tuple(3) - {D(A, B, C)}
Tuple(usize),
+ /// Indexes something indexable, currently only Tuples,
+ /// and adds that element to the stack.
+ ///
+ /// {T, I} - Index - {T[I]}
Index,
+ /// Looks up a field by the given name
+ /// and replaces the parent with it.
+ /// Currently only expects [Value::Blob].
+ ///
+ /// {O} - Get(F) - {O.F}
Get(String),
+ /// Looks up a field by the given name
+ /// and replaces the current value in the object.
+ /// Currently only expects [Value::Blob].
+ ///
+ /// {O} - Set(F) - {}
Set(String),
+ /// Adds the two top elements on the stack,
+ /// using the function [op::add]. The result
+ /// is the pushed.
+ ///
+ /// {A, B} - Add - {A + B}
Add,
+ /// Sub the two top elements on the stack,
+ /// using the function [op::sub]. The result
+ /// is the pushed.
+ ///
+ /// {A, B} - Sub - {A - B}
Sub,
+ /// Multiples the two top elements on the stack,
+ /// using the function [op::mul]. The result
+ /// is the pushed.
+ ///
+ /// {A, B} - Mul - {A - B}
Mul,
+ /// Divides the two top elements on the stack,
+ /// using the function [op::div]. The result
+ /// is the pushed.
+ ///
+ /// {A, B} - Div - {A / B}
Div,
+ /// Negates the top element on the stack.
+ ///
+ /// {A} - Neg - {-A}
Neg,
+ /// Performs a boolean and on the
+ /// top 2 stack elements using [op::and].
+ ///
+ /// {A, B} - And - {A && B}
And,
+ /// Performs a boolean or on the
+ /// top 2 stack elements using [op::or].
+ ///
+ /// {A, B} - Or - {A || B}
Or,
+ /// Performs a boolean not on the
+ /// top stack element using [op::not].
+ ///
+ /// {A} - Not - {!A}
Not,
+ /// Sets the instruction pointer
+ /// to the given value.
+ ///
+ /// Does not affect the stack.
Jmp(usize),
+ /// Sets the instruction pointer
+ /// to the given value, if the
+ /// topmost value is false, also
+ /// pops this value.
+ ///
+ /// {A} - JmpFalse(n) - {}
JmpFalse(usize),
- Equal, // ==
- Less, // <
- Greater, // >
-
+ /// Compares the two topmost elements
+ /// on the stack for equality, and pushes
+ /// the result. Compares using [op::eq].
+ ///
+ /// {A, B} - Equal - {A == B}
+ Equal,
+ /// Compares the two topmost elements
+ /// on the stack for order, and pushes the result.
+ /// Compares using [op::less].
+ ///
+ /// {A, B} - Less - {A < B}
+ Less,
+ /// Compares the two topmost elements
+ /// on the stack for order, and pushes the result.
+ /// Compares using [op::less].
+ ///
+ /// {A, B} - Greater - {B < A}
+ Greater,
+
+ /// Pops the top value of the stack, and
+ /// crashes the program if it is false.
+ ///
+ /// {A} - Assert - {}
Assert,
+ /// This instruction should not be executed.
+ /// If it is the program crashes.
+ ///
+ /// Does not affect the stack.
Unreachable,
+ /// Reads the value counted from the
+ /// bottom of the stack and adds it
+ /// to the top.
+ ///
+ /// {A, B} - ReadLocal(0) - {A, B, A}
ReadLocal(usize),
+ /// Sets the value at the given index
+ /// of the stack, to the topmost value.
+ /// Pops the topsmost element.
+ ///
+ /// {A, B} - AssignLocal(0) - {B}
AssignLocal(usize),
+ /// Reads the upvalue, and adds it
+ /// to the top of the stack.
+ ///
+ /// {} - ReadUpvalue(0) - {A}
ReadUpvalue(usize),
+ /// Sets the given upvalue, and pops
+ /// the topmost element.
+ ///
+ /// {A} - AssignUpvalue(0) - {}
AssignUpvalue(usize),
+ /// A helper instruction for the typechecker,
+ /// makes sure the top value on the stack
+ /// is of the given type, and is ment to signal
+ /// that the "variable" is added.
+ ///
+ /// Does not affect the stack.
Define(Type),
+ /// Calls "something" with the given number
+ /// of arguments. The callable value is
+ /// then replaced with the result.
+ ///
+ /// Callable things are: [Value::Blob], [Value::Function],
+ /// and [Value::ExternFunction].
+ ///
+ /// {F, A, B} - Call(2) - {F(A, B)}
Call(usize),
+ /// Prints and pops the top value on the stack.
+ ///
+ /// {A} - Print - {}
Print,
+ /// Pops the current stackframe and replaces
+ /// slot 0 with the top value. Also pops
+ /// upvalues.
+ ///
+ /// {F, A, B} - Return - {..., B}
Return,
+
+ /// Temporarily stops execution and returns
+ /// to the call site.
+ ///
+ /// Does not affect the stack.
Yield,
}
+///
+/// Module with all the operators that can be applied
+/// to values.
+///
+/// Broken out because they need to be recursive.
mod op {
use super::Value;
use std::rc::Rc;
- fn tuple_op(a: &Rc<Vec<Value>>, b: &Rc<Vec<Value>>, f: fn (&Value, &Value) -> Value) -> Value {
+ fn tuple_bin_op(a: &Rc<Vec<Value>>, b: &Rc<Vec<Value>>, f: fn (&Value, &Value) -> Value) -> Value {
Value::Tuple(Rc::new(a.iter().zip(b.iter()).map(|(a, b)| f(a, b)).collect()))
}
+ fn tuple_un_op(a: &Rc<Vec<Value>>, f: fn (&Value) -> Value) -> Value {
+ Value::Tuple(Rc::new(a.iter().map(f).collect()))
+ }
+
pub fn neg(value: &Value) -> Value {
match value {
Value::Float(a) => Value::Float(-*a),
Value::Int(a) => Value::Int(-*a),
- Value::Tuple(a) => Value::Tuple(Rc::new(a.iter().map(neg).collect())),
+ Value::Tuple(a) => tuple_un_op(a, neg),
_ => Value::Nil,
}
}
@@ -351,7 +514,7 @@ mod op {
pub fn not(value: &Value) -> Value {
match value {
Value::Bool(a) => Value::Bool(!*a),
- Value::Tuple(a) => Value::Tuple(Rc::new(a.iter().map(not).collect())),
+ Value::Tuple(a) => tuple_un_op(a, not),
_ => Value::Nil,
}
}
@@ -362,7 +525,7 @@ mod op {
(Value::Float(a), Value::Float(b)) => Value::Float(a + b),
(Value::Int(a), Value::Int(b)) => Value::Int(a + b),
(Value::String(a), Value::String(b)) => Value::String(Rc::from(format!("{}{}", a, b))),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, add),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, add),
_ => Value::Nil,
}
}
@@ -375,7 +538,7 @@ mod op {
match (a, b) {
(Value::Float(a), Value::Float(b)) => Value::Float(a * b),
(Value::Int(a), Value::Int(b)) => Value::Int(a * b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, mul),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, mul),
_ => Value::Nil,
}
}
@@ -384,7 +547,7 @@ mod op {
match (a, b) {
(Value::Float(a), Value::Float(b)) => Value::Float(a / b),
(Value::Int(a), Value::Int(b)) => Value::Int(a / b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, div),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, div),
_ => Value::Nil,
}
}
@@ -395,7 +558,7 @@ mod op {
(Value::Int(a), Value::Int(b)) => Value::Bool(a == b),
(Value::String(a), Value::String(b)) => Value::Bool(a == b),
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, eq),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, eq),
_ => Value::Nil,
}
}
@@ -406,7 +569,7 @@ mod op {
(Value::Int(a), Value::Int(b)) => Value::Bool(a < b),
(Value::String(a), Value::String(b)) => Value::Bool(a < b),
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a < b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, less),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, less),
_ => Value::Nil,
}
}
@@ -418,7 +581,7 @@ mod op {
pub fn and(a: &Value, b: &Value) -> Value {
match (a, b) {
(Value::Bool(a), Value::Bool(b)) => Value::Bool(*a && *b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, and),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, and),
_ => Value::Nil,
}
}
@@ -426,7 +589,7 @@ mod op {
pub fn or(a: &Value, b: &Value) -> Value {
match (a, b) {
(Value::Bool(a), Value::Bool(b)) => Value::Bool(*a || *b),
- (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_op(a, b, or),
+ (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, or),
_ => Value::Nil,
}
}
@@ -459,6 +622,7 @@ impl Block {
}
}
+ // Used to create empty functions.
fn empty_with_type(ty: &Type) -> Self {
let mut block = Block::new("/empty/", Path::new(""), 0);
block.ty = ty.clone();
@@ -836,4 +1000,10 @@ a.a <=> 1
a.a -= 1
a.a <=> 0"
);
+
+ 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",
+ );
}
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index 28172a3..9574af1 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -123,7 +123,7 @@ pub enum Token {
#[token("\n")]
Newline,
- #[regex(r"//[^\n]*\n", logos::skip)]
+ #[regex(r"//[^\n]*", logos::skip)]
Comment,
#[regex(r"[ \t\r]", logos::skip)]
@@ -229,8 +229,8 @@ mod tests {
#[test]
fn comment() {
- lex_once("// a\n1");
- assert_eq!(lex("1// a\n2").len(), 2);
- assert_eq!(lex("1\n// a\n2").len(), 3); // newline is also a token
+ assert_eq!(lex("// a\n1").len(), 2);
+ assert_eq!(lex("1// a\n2").len(), 3);
+ assert_eq!(lex("1\n// a\n2").len(), 4); // newline is also a token
}
}
diff --git a/src/vm.rs b/src/vm.rs
index 5c42805..5a7b471 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -122,10 +122,6 @@ impl VM {
(b, a) // this matches the order they were on the stack
}
- fn _peek_up(&self, amount: usize) -> Option<&Value> {
- self.stack.get(self.stack.len() - amount)
- }
-
fn frame(&self) -> &Frame {
let last = self.frames.len() - 1;
&self.frames[last]
@@ -141,8 +137,8 @@ impl VM {
self.frame().block.borrow().ops[ip].clone()
}
+ /// Stop the program, violently
fn crash_and_burn(&self) -> ! {
- println!("\n\n !!!POPPING EMPTY STACK - DUMPING EVERYTHING!!!\n");
self.print_stack();
println!("\n");
self.frame().block.borrow().debug_print();
@@ -162,6 +158,7 @@ impl VM {
}
}
+ /// Runs a single operation on the VM
fn eval_op(&mut self, op: Op) -> Result<OpResult, Error> {
match op {
Op::Illegal => {
@@ -427,6 +424,7 @@ impl VM {
self.frame().block.borrow().ops[self.frame().ip]);
}
+ // Initalizes the VM for running. Run cannot be called before this.
pub(crate) fn init(&mut self, prog: &Prog) {
let block = Rc::clone(&prog.blocks[0]);
self.blobs = prog.blobs.clone();
@@ -443,6 +441,7 @@ impl VM {
});
}
+ /// Simulates the program.
pub fn run(&mut self) -> Result<OpResult, Error> {
if self.print_blocks {
println!("\n [[{}]]\n", "RUNNING".red());
@@ -461,6 +460,7 @@ impl VM {
}
}
+ /// Checks the current operation for type errors.
fn check_op(&mut self, op: Op) -> Result<(), Error> {
match op {
Op::Unreachable => {}
@@ -702,6 +702,7 @@ impl VM {
errors
}
+ // Checks the program for type errors.
pub(crate) fn typecheck(&mut self, prog: &Prog) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();