aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler.rs')
-rw-r--r--src/compiler.rs339
1 files changed, 250 insertions, 89 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index c59de4d..18599ae 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -47,7 +47,7 @@ nextable_enum!(Prec {
Factor
});
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
enum Type {
NoType,
UnkownType,
@@ -74,7 +74,14 @@ impl TryFrom<&str> for Type {
struct Variable {
name: String,
typ: Type,
- level: usize,
+ scope: usize,
+ active: bool,
+}
+
+struct Frame {
+ stack: Vec<Variable>,
+ scope: usize,
+ variables_below: usize,
}
struct Compiler {
@@ -82,13 +89,48 @@ struct Compiler {
tokens: TokenStream,
current_file: PathBuf,
- level: usize,
- stack: Vec<Variable>,
+ frames: Vec<Frame>,
panic: bool,
errors: Vec<Error>,
}
+macro_rules! push_frame {
+ ($compiler:expr, $block:expr, $code:tt) => {
+ {
+ $compiler.frames.push(Frame {
+ stack: Vec::new(),
+ scope: 0,
+ variables_below: $compiler.frame().variables_below + $compiler.stack().len(),
+ });
+
+ // Return value stored as a variable
+ $compiler.define_variable("", Type::UnkownType, &mut $block).unwrap();
+ $code
+
+ $compiler.frames.pop().unwrap();
+ // The 0th slot is the return value, which is passed out
+ // from functions, and should not be popped.
+ 0
+ }
+ };
+}
+
+macro_rules! push_scope {
+ ($compiler:expr, $block:expr, $code:tt) => {
+ let ss = $compiler.stack().len();
+ $compiler.frame_mut().scope += 1;
+
+ $code;
+
+ $compiler.frame_mut().scope -= 1;
+ for _ in ss..$compiler.stack().len() {
+ $block.add(Op::Pop, $compiler.line());
+ }
+ $compiler.stack_mut().truncate(ss);
+ };
+}
+
impl Compiler {
pub fn new(current_file: &Path, tokens: TokenStream) -> Self {
Self {
@@ -96,14 +138,35 @@ impl Compiler {
tokens,
current_file: PathBuf::from(current_file),
- level: 0,
- stack: vec![],
+ frames: vec![Frame {
+ stack: Vec::new(),
+ scope: 0,
+ variables_below: 0,
+ }],
panic: false,
errors: vec![],
}
}
+ fn frame(&self) -> &Frame {
+ let last = self.frames.len() - 1;
+ &self.frames[last]
+ }
+
+ fn frame_mut(&mut self) -> &mut Frame {
+ let last = self.frames.len() - 1;
+ &mut self.frames[last]
+ }
+
+ fn stack(&self) -> &[Variable] {
+ &self.frame().stack.as_ref()
+ }
+
+ fn stack_mut(&mut self) -> &mut Vec<Variable> {
+ &mut self.frame_mut().stack
+ }
+
fn clear_panic(&mut self) {
if self.panic {
self.panic = false;
@@ -122,10 +185,10 @@ impl Compiler {
if self.panic { return }
self.panic = true;
self.errors.push(Error {
- kind: kind,
+ kind,
file: self.current_file.clone(),
line: self.line(),
- message: message,
+ message,
});
}
@@ -216,6 +279,8 @@ impl Compiler {
| Token::NotEqual
=> self.binary(block),
+ Token::LeftParen => self.call(block),
+
_ => { return false; },
}
return true;
@@ -260,7 +325,7 @@ impl Compiler {
Token::Minus => &[Op::Sub],
Token::Star => &[Op::Mul],
Token::Slash => &[Op::Div],
- Token::AssertEqual => &[Op::AssertEqual],
+ Token::AssertEqual => &[Op::Equal, Op::Assert],
Token::EqualEqual => &[Op::Equal],
Token::Less => &[Op::Less],
Token::Greater => &[Op::Greater],
@@ -273,7 +338,10 @@ impl Compiler {
}
fn expression(&mut self, block: &mut Block) {
- self.parse_precedence(block, Prec::No);
+ match self.peek_four() {
+ (Token::Fn, ..) => self.function(block),
+ _ => self.parse_precedence(block, Prec::No),
+ }
}
fn parse_precedence(&mut self, block: &mut Block, precedence: Prec) {
@@ -288,11 +356,105 @@ impl Compiler {
}
}
- fn find_local(&self, name: &str, block: &Block) -> Option<(usize, Type, usize)> {
- self.stack.iter()
- .enumerate()
- .rev()
- .find_map(|x| if x.1.name == name { Some((x.0, x.1.typ, x.1.level)) } else { None} )
+ 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, var.scope));
+ }
+ }
+ None
+ }
+
+ 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;
+ }
+ _ => {
+ self.expression(block);
+ arity += 1;
+ if !matches!(self.peek(), Token::RightParen) {
+ expect!(self, Token::Comma, "Expected ',' after argument.");
+ }
+ }
+ }
+ }
+
+ 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
+ } else {
+ "anonumus function"
+ };
+
+ let mut _return_type = None;
+ let mut function_block = Block::new(name, &self.current_file);
+ let arity;
+ let _ret = push_frame!(self, function_block, {
+ loop {
+ match self.peek() {
+ Token::Identifier(name) => {
+ self.eat();
+ expect!(self, Token::Colon, "Expected ':' after parameter name.");
+ if let Ok(typ) = self.type_ident() {
+ if let Ok(slot) = self.define_variable(&name, typ, &mut function_block) {
+ self.stack_mut()[slot].active = true;
+ }
+ } else {
+ error!(self, "Failed to parse parameter type.");
+ }
+ if !matches!(self.peek(), Token::Arrow | Token::LeftBrace) {
+ expect!(self, Token::Comma, "Expected ',' after parameter.");
+ }
+ }
+ Token::LeftBrace => {
+ _return_type = Some(Type::NoType);
+ break;
+ }
+ Token::Arrow => {
+ self.eat();
+ if let Ok(typ) = self.type_ident() {
+ _return_type = Some(typ);
+ } else {
+ error!(self, "Failed to parse return type.");
+ }
+ break;
+ }
+ _ => {
+ error!(self, "Expected '->' or more paramters in function definition.");
+ break;
+ }
+ }
+ }
+ arity = self.frame().stack.len() - 1;
+
+ self.scope(&mut function_block);
+ });
+
+
+ function_block.add(Op::Constant(Value::Bool(true)), self.line());
+ function_block.add(Op::Return, self.line());
+
+ block.add(Op::Constant(Value::Function(arity, Rc::new(function_block))), self.line());
}
fn variable_expression(&mut self, block: &mut Block) {
@@ -307,17 +469,32 @@ impl Compiler {
}
}
- fn define_variable(&mut self, name: &str, typ: Type, block: &mut Block) {
+ 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.level {
+ if level == self.frame().scope {
error!(self, format!("Multiple definitions of {} in this block.", name));
- return;
+ return Err(());
}
}
+ let slot = self.stack().len();
+ let scope = self.frame().scope;
+ self.stack_mut().push(Variable {
+ name: String::from(name),
+ typ,
+ scope,
+ active: false
+ });
+ Ok(slot)
+ }
+
+ fn definition_statement(&mut self, name: &str, typ: Type, block: &mut Block) {
+ let slot = self.define_variable(name, typ, block);
self.expression(block);
- self.stack.push(Variable { name: String::from(name), level: self.level, typ });
+ if let Ok(slot) = slot {
+ self.stack_mut()[slot].active = true;
+ }
}
fn assign(&mut self, name: &str, block: &mut Block) {
@@ -334,25 +511,16 @@ impl Compiler {
return;
}
- self.level += 1;
- let h = self.stack.len();
-
- while !matches!(self.peek(), Token::RightBrace | Token::EOF) {
- self.statement(block);
- match self.peek() {
- Token::Newline => { self.eat(); },
- Token::RightBrace => { break; },
- _ => { error!(self, "Expect newline after statement."); },
+ push_scope!(self, block, {
+ while !matches!(self.peek(), Token::RightBrace | Token::EOF) {
+ self.statement(block);
+ match self.peek() {
+ Token::Newline => { self.eat(); },
+ Token::RightBrace => { break; },
+ _ => { error!(self, "Expect newline after statement."); },
+ }
}
- }
-
- self.level -= 1;
-
- for _ in h..self.stack.len() {
- block.add(Op::Pop, self.line());
- }
-
- self.stack.truncate(h);
+ });
expect!(self, Token::RightBrace, "Expected '}' at end of block.");
}
@@ -384,66 +552,53 @@ impl Compiler {
fn for_loop(&mut self, block: &mut Block) {
expect!(self, Token::For, "Expected 'for' at start of for-loop.");
- // push outer scope for loop variable
- self.level += 1;
- let h = self.stack.len();
-
- // Definition
- match self.peek_four() {
- (Token::Identifier(name), Token::Identifier(typ), Token::ColonEqual, ..) => {
- self.eat();
- self.eat();
- self.eat();
- if let Ok(typ) = Type::try_from(typ.as_ref()) {
- self.define_variable(&name, typ, block);
- } else {
- error!(self, format!("Failed to parse type '{}'.", typ));
+ push_scope!(self, block, {
+ // Definition
+ match self.peek_four() {
+ (Token::Identifier(name), Token::Identifier(typ), Token::ColonEqual, ..) => {
+ self.eat();
+ self.eat();
+ self.eat();
+ if let Ok(typ) = Type::try_from(typ.as_ref()) {
+ self.definition_statement(&name, typ, block);
+ } else {
+ error!(self, format!("Failed to parse type '{}'.", typ));
+ }
}
- }
- (Token::Identifier(name), Token::ColonEqual, ..) => {
- self.eat();
- self.eat();
- self.define_variable(&name, Type::UnkownType, block);
- }
+ (Token::Identifier(name), Token::ColonEqual, ..) => {
+ self.eat();
+ self.eat();
+ self.definition_statement(&name, Type::UnkownType, block);
+ }
- (Token::Comma, ..) => {}
+ (Token::Comma, ..) => {}
- _ => { error!(self, "Expected definition at start of for-loop."); }
- }
+ _ => { error!(self, "Expected definition at start of for-loop."); }
+ }
- expect!(self, Token::Comma, "Expect ',' between initalizer and loop expression.");
+ expect!(self, Token::Comma, "Expect ',' between initalizer and loop expression.");
- let cond = block.curr();
- self.expression(block);
- let cond_out = block.add(Op::Illegal, self.line());
- let cond_cont = block.add(Op::Illegal, self.line());
- expect!(self, Token::Comma, "Expect ',' between initalizer and loop expression.");
+ let cond = block.curr();
+ self.expression(block);
+ let cond_out = block.add(Op::Illegal, self.line());
+ let cond_cont = block.add(Op::Illegal, self.line());
+ expect!(self, Token::Comma, "Expect ',' between initalizer and loop expression.");
- let inc = block.curr();
- {
- let h = self.stack.len();
- self.statement(block);
- for _ in h..self.stack.len() {
- block.add(Op::Pop, self.line());
- }
- self.stack.truncate(h);
- }
- block.add(Op::Jmp(cond), self.line());
+ let inc = block.curr();
+ push_scope!(self, block, {
+ self.statement(block);
+ });
+ block.add(Op::Jmp(cond), self.line());
- // patch_jmp!(Op::Jmp, cond_cont => block.curr());
- block.patch(Op::Jmp(block.curr()), cond_cont);
- self.scope(block);
- block.add(Op::Jmp(inc), self.line());
+ // patch_jmp!(Op::Jmp, cond_cont => block.curr());
+ block.patch(Op::Jmp(block.curr()), cond_cont);
+ self.scope(block);
+ block.add(Op::Jmp(inc), self.line());
- block.patch(Op::JmpFalse(block.curr()), cond_out);
+ block.patch(Op::JmpFalse(block.curr()), cond_out);
- // pop outer scope
- self.level -= 1;
- for _ in h..self.stack.len() {
- block.add(Op::Pop, self.line());
- }
- self.stack.truncate(h);
+ });
}
fn type_ident(&mut self) -> Result<Type, ()> {
@@ -476,7 +631,7 @@ impl Compiler {
self.eat();
if let Ok(typ) = self.type_ident() {
expect!(self, Token::Equal, "Expected assignment.");
- self.define_variable(&name, typ, block);
+ self.definition_statement(&name, typ, block);
} else {
error!(self, format!("Expected type found '{:?}'.", self.peek()));
}
@@ -485,7 +640,7 @@ impl Compiler {
tokens!(Token::Identifier(name), Token::ColonEqual) => {
self.eat();
self.eat();
- self.define_variable(&name, Type::UnkownType, block);
+ self.definition_statement(&name, Type::UnkownType, block);
}
tokens!(Token::Identifier(name), Token::Equal) => {
@@ -502,6 +657,12 @@ impl Compiler {
self.for_loop(block);
}
+ tokens!(Token::Ret) => {
+ self.eat();
+ self.expression(block);
+ block.add(Op::Return, self.line());
+ }
+
tokens!(Token::Unreachable) => {
self.eat();
block.add(Op::Unreachable, self.line());