diff options
Diffstat (limited to 'src/compiler.rs')
| -rw-r--r-- | src/compiler.rs | 348 |
1 files changed, 234 insertions, 114 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index ec3a52c..680d1f1 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,11 +1,11 @@ -use std::{borrow::Cow, path::{Path, PathBuf}}; +use std::{path::{Path, PathBuf}}; use std::cell::RefCell; use std::collections::{HashMap, hash_map::Entry}; use std::rc::Rc; use crate::{Blob, Block, Op, Prog, RustFunction, Type, Value}; use crate::error::{Error, ErrorKind}; -use crate::tokenizer::{Token, TokenStream}; +use crate::tokenizer::{Token, PlacedToken, TokenStream}; macro_rules! nextable_enum { ( $name:ident { $( $thing:ident ),* $( , )? } ) => { @@ -46,7 +46,7 @@ macro_rules! parse_branch { ($compiler:expr, $block:expr, [ $( $call:expr ),* ]) => { { let block_length = $block.ops.len(); - let token_length = $compiler.curr; + let token_length = $compiler.current_token; let num_errors = $compiler.errors.len(); let mut stored_errors = Vec::new(); @@ -63,7 +63,7 @@ macro_rules! parse_branch { return true; } $compiler.panic = false; - $compiler.curr = token_length; + $compiler.current_token = token_length; let thrown_errors = $compiler.errors.len() - num_errors - 1; stored_errors.extend($compiler.errors.split_off(thrown_errors)); $block.ops.truncate(block_length); @@ -82,7 +82,7 @@ macro_rules! parse_branch { ($compiler:expr, $block:expr, $call:expr) => { { let block_length = $block.ops.len(); - let token_length = $compiler.curr; + let token_length = $compiler.current_token; let num_errors = $compiler.errors.len(); let mut stored_errors = Vec::new(); // Closures for early return on success. @@ -97,7 +97,7 @@ macro_rules! parse_branch { return true; } $compiler.panic = false; - $compiler.curr = token_length; + $compiler.current_token = token_length; let thrown_errors = $compiler.errors.len() - num_errors - 1; stored_errors.extend($compiler.errors.split_off(thrown_errors)); $block.ops.truncate(block_length); @@ -107,6 +107,77 @@ macro_rules! parse_branch { }; } +macro_rules! push_frame { + ($compiler:expr, $block:expr, $code:tt) => { + { + $compiler.current_context_mut().push(Frame::new()); + + // Return value stored as a variable + let var = Variable::new("", true, Type::Unknown); + $compiler.define(var).unwrap(); + + $code + + let frame = $compiler.current_context_mut().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!("Unused 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 + } + }; +} + +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; + + 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); + }; +} + nextable_enum!(Prec { No, Assert, @@ -243,6 +314,22 @@ impl Frame { } } +type CompilerContext = Vec<Frame>; + +struct Section<'a> { + path: PathBuf, + tokens: &'a [PlacedToken], +} + +impl<'a> Section<'a> { + fn new(path: PathBuf, tokens: &'a [PlacedToken]) -> Self { + Section { + path, + tokens + } + } +} + #[derive(Debug)] enum Name { Slot(usize, usize), @@ -250,12 +337,12 @@ enum Name { Space(HashMap<String, usize>), } -pub(crate) struct Compiler { - curr: usize, - tokens: TokenStream, - current_file: PathBuf, +pub(crate) struct Compiler<'a> { + current_token: usize, + current_section: usize, + sections: Vec<Section<'a>>, - frames: Vec<Frame>, + contextes: HashMap<PathBuf, CompilerContext>, panic: bool, errors: Vec<Error>, @@ -272,90 +359,85 @@ pub(crate) struct Compiler { names: HashMap<String, Name>, } -macro_rules! push_frame { - ($compiler:expr, $block:expr, $code:tt) => { - { - $compiler.frames.push(Frame::new()); - - // Return value stored as a variable - let var = Variable::new("", true, Type::Unknown); - $compiler.define(var).unwrap(); - - $code - - 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!("Unused 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 - } - }; +/// 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()) } -macro_rules! push_scope { - ($compiler:expr, $block:expr, $code:tt) => { - let ss = $compiler.stack().len(); - $compiler.frame_mut().scope += 1; +fn split_sections<'a>(file_name: PathBuf, tokens: &'a TokenStream) -> Vec<Section> { + let mut sections = Vec::new(); - $code; + let mut last = 0; + let mut curr = 0; + while curr < tokens.len() { + if match (tokens.get(curr + 0), tokens.get(curr + 1), tokens.get(curr + 2)) { + (Some((Token::LeftBrace, _)), ..) + => { + let mut blocks = 0; + loop { + curr += 1; + match tokens.get(curr) { + Some((Token::LeftBrace, _)) => { + blocks += 1; + } - $compiler.frame_mut().scope -= 1; + Some((Token::RightBrace, _)) => { + curr += 1; + blocks -= 1; + if blocks <= 0 { + break; + } + } - 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!("Variable is unused: '{}'.", var.name),) - ); - } - if var.captured { - add_op($compiler, $block, Op::PopUpvalue); - } else { - add_op($compiler, $block, Op::Pop); - } - } + None => { + break; + } - for (e, l, m) in errors.iter() { - $compiler.error_on_line(e.clone(), *l, Some(m.clone())); - $compiler.panic = false; + _ => {} + } + } + false + }, + + (Some((Token::Identifier(_), _)), + Some((Token::ColonColon, _)), + Some((Token::Fn, _))) + => true, + + (Some((Token::Identifier(_), _)), + Some((Token::ColonColon, _)), + Some(_)) + => true, + + (Some((Token::Identifier(_), _)), + Some((Token::ColonEqual, _)), + Some(_)) + => true, + + _ => false, + } { + sections.push(Section::new(file_name.clone(), &tokens[last..curr])); + last = curr; } - $compiler.stack_mut().truncate(ss); - }; + curr += 1; + } + sections.push(Section::new(file_name, &tokens[last..curr])); + sections } -/// 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()) -} +impl<'a> Compiler<'a> { + pub(crate) fn new(current_file: &Path, tokens: &'a TokenStream) -> Self { + let current_file = current_file.to_path_buf(); + let sections = split_sections(current_file.clone(), tokens); -impl Compiler { - pub(crate) fn new(current_file: &Path, tokens: TokenStream) -> Self { + let mut contextes = HashMap::new(); + contextes.insert(current_file, vec![Frame::new()]); Self { - curr: 0, - tokens, - current_file: PathBuf::from(current_file), + current_token: 0, + current_section: 0, + sections, - frames: vec![Frame::new()], + contextes, panic: false, errors: vec![], @@ -385,7 +467,8 @@ impl Compiler { | Value::Bool(_) | Value::String(_) | Value::Tuple(_) - | Value::Nil) { + | Value::Nil) + { let entry = self.values.entry(value.clone()); if let Entry::Occupied(entry) = entry { *entry.get() @@ -406,14 +489,31 @@ impl Compiler { self.strings.len() - 1 } + fn section(&self) -> &Section { + &self.sections[self.current_section] + } + + fn current_file(&self) -> &Path { + &self.section().path + } + + fn current_context(&self) -> &CompilerContext { + self.contextes.get(self.current_file()).unwrap() + } + + fn current_context_mut(&mut self) -> &mut CompilerContext { + let file = self.current_file().to_path_buf(); + self.contextes.get_mut(&file).unwrap() + } + fn frame(&self) -> &Frame { - let last = self.frames.len() - 1; - &self.frames[last] + let last = self.current_context().len() - 1; + &self.current_context()[last] } fn frame_mut(&mut self) -> &mut Frame { - let last = self.frames.len() - 1; - &mut self.frames[last] + let last = self.current_context().len() - 1; + &mut self.current_context_mut()[last] } /// Marks a variable as read. Also marks upvalues. @@ -424,15 +524,15 @@ impl Compiler { } - if if let Some(up) = self.frames[frame_id].upvalues.get(var.slot) { + if if let Some(up) = self.current_context()[frame_id].upvalues.get(var.slot) { up.name == var.name } else { false } { - let mut inner_var = self.frames[frame_id].upvalues[var.slot].clone(); + let mut inner_var = self.current_context()[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; + self.current_context_mut()[frame_id].upvalues[var.slot].read = true; } else { - self.frames[frame_id].stack[var.slot].read = true; + self.current_context_mut()[frame_id].stack[var.slot].read = true; } } @@ -468,22 +568,26 @@ impl Compiler { self.panic = true; self.errors.push(Error { kind, - file: self.current_file.clone(), + file: self.current_file().to_path_buf(), line, message, }); } + fn init_section(&mut self, section: usize) { + self.current_token = 0; + self.current_section = section; + } fn peek(&self) -> Token { self.peek_at(0) } fn peek_at(&self, at: usize) -> Token { - if self.tokens.len() <= self.curr + at { + if self.section().tokens.len() <= self.current_token + at { crate::tokenizer::Token::EOF } else { - self.tokens[self.curr + at].0.clone() + self.section().tokens[self.current_token + at].0.clone() } } @@ -494,16 +598,28 @@ impl Compiler { fn eat(&mut self) -> Token { let t = self.peek(); - self.curr += 1; + self.current_token += 1; + match t { + Token::GitConflictBegin => { + self.current_token -= 1; + let start = self.line(); + self.current_token += 1; + while !matches!(self.eat(), Token::GitConflictEnd) {} + self.panic = false; + self.error_on_line(ErrorKind::GitConflictError(start, self.line()), start, None); + self.panic = true; + } + _ => {} + } t } /// The line of the current token. fn line(&self) -> usize { - if self.curr < self.tokens.len() { - self.tokens[self.curr].1 + if self.section().tokens.len() == 0 { + 0xCAFEBABE } else { - self.tokens[self.tokens.len() - 1].1 + self.section().tokens[std::cmp::min(self.current_token, self.section().tokens.len() - 1)].1 } } @@ -689,8 +805,8 @@ impl Compiler { } } - fn find_and_capture_variable<'a, I>(name: &str, mut iterator: I) -> Option<Variable> - where I: Iterator<Item = &'a mut Frame> { + fn find_and_capture_variable<'b, I>(name: &str, mut iterator: I) -> Option<Variable> + where I: Iterator<Item = &'b mut Frame> { if let Some(frame) = iterator.next() { if let Some(res) = frame.find_local(name) { frame.stack[res.slot].captured = true; @@ -720,7 +836,7 @@ impl Compiler { return Some(res); } - Self::find_and_capture_variable(name, self.frames.iter_mut().rev()) + Self::find_and_capture_variable(name, self.current_context_mut().iter_mut().rev()) } fn find_constant(&mut self, name: &str) -> usize { @@ -846,15 +962,15 @@ impl Compiler { self.stack_mut()[top].active = true; self.stack()[top].name.clone() } else { - format!("λ {}@{:03}", self.current_file.display(), self.line()) + format!("λ {}@{:03}", self.current_file().display(), self.line()) }; let mut args = Vec::new(); let mut return_type = Type::Void; - let mut function_block = Block::new(&name, &self.current_file); + let mut function_block = Block::new(&name, self.current_file()); let block_id = self.blocks.len(); - let temp_block = Block::new(&name, &self.current_file); + let temp_block = Block::new(&name, self.current_file()); self.blocks.push(Rc::new(RefCell::new(temp_block))); let _ret = push_frame!(self, function_block, { @@ -953,7 +1069,7 @@ impl Compiler { // Variables if let Some(var) = self.find_variable(&name) { - self.mark_read(self.frames.len() - 1, &var); + self.mark_read(self.current_context().len() - 1, &var); if var.upvalue { add_op(self, block, Op::ReadUpvalue(var.slot)); } else { @@ -1017,7 +1133,7 @@ impl Compiler { fn constant_statement(&mut self, name: &str, typ: Type, block: &mut Block) { // Magical global constants - if self.frames.len() <= 1 && self.peek() == Token::Fn { + if self.current_context().len() <= 1 && self.peek() == Token::Fn { self.function(block, Some(name)); // Remove the function, since it's a constant and we already // added it. @@ -1305,7 +1421,7 @@ impl Compiler { _ => unreachable!(), }; if let Some(var) = self.find_variable(&name) { - self.mark_read(self.frames.len() - 1, &var); + self.mark_read(self.current_context().len() - 1, &var); if var.upvalue { add_op(self, block, Op::ReadUpvalue(var.slot)); } else { @@ -1473,7 +1589,7 @@ impl Compiler { } - pub(crate) fn compile(&mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> { + pub(crate) fn compile(&'a mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> { self.functions = functions .to_vec() .into_iter() @@ -1484,10 +1600,14 @@ impl Compiler { 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."); + for section in 0..self.sections.len() { + self.init_section(section); + while self.peek() != Token::EOF { + println!("compiling {} -- statement -- {:?}", section, self.line()); + self.statement(&mut block); + expect!(self, Token::Newline | Token::EOF, + "Expect newline or EOF after expression."); + } } let tmp = self.add_constant(Value::Unknown); add_op(self, &mut block, Op::Constant(tmp)); @@ -1509,7 +1629,7 @@ impl Compiler { } } - for var in self.frames.pop().unwrap().stack.iter().skip(1) { + for var in self.current_context_mut().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); |
