aboutsummaryrefslogtreecommitdiffstats
path: root/src/compiler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler.rs')
-rw-r--r--src/compiler.rs102
1 files changed, 98 insertions, 4 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 345088f..6074660 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -1,4 +1,5 @@
use std::path::{Path, PathBuf};
+use std::convert::TryFrom;
use crate::tokenizer::{Token, TokenStream};
use crate::vm::{Value, Block, Op};
@@ -45,10 +46,38 @@ nextable_enum!(Prec {
Factor
});
+enum Type {
+ UnkownType,
+ Int,
+ Float,
+ Bool,
+}
+
+impl TryFrom<&str> for Type {
+ type Error = ();
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ match value {
+ "int" => Ok(Type::Int),
+ "float" => Ok(Type::Float),
+ "bool" => Ok(Type::Bool),
+ _ => Err(()),
+ }
+ }
+}
+
+struct Variable {
+ name: String,
+ typ: Type,
+}
+
struct Compiler {
curr: usize,
tokens: TokenStream,
current_file: PathBuf,
+
+ stack: Vec<Variable>,
+
panic: bool,
errors: Vec<Error>,
}
@@ -59,6 +88,9 @@ impl Compiler {
curr: 0,
tokens,
current_file: PathBuf::from(current_file),
+
+ stack: vec![],
+
panic: false,
errors: vec![],
}
@@ -90,13 +122,22 @@ impl Compiler {
}
fn peek(&self) -> Token {
- if self.tokens.len() <= self.curr {
+ self.peek_at(0)
+ }
+
+ fn peek_at(&self, at: usize) -> Token {
+ if self.tokens.len() <= self.curr + at {
crate::tokenizer::Token::EOF
} else {
- self.tokens[self.curr].0.clone()
+ self.tokens[self.curr + at].0.clone()
}
}
+ // TODO(ed): Const generics
+ fn peek_four(&self) -> (Token, Token, Token, Token) {
+ (self.peek_at(0), self.peek_at(1), self.peek_at(2), self.peek_at(3))
+ }
+
fn eat(&mut self) -> Token {
let t = self.peek();
self.curr += 1;
@@ -135,6 +176,7 @@ impl Compiler {
fn prefix(&mut self, token: Token, block: &mut Block) -> bool {
match token {
+ Token::Identifier(_) => self.variable_expression(block),
Token::LeftParen => self.grouping(block),
Token::Minus => self.unary(block),
@@ -236,17 +278,69 @@ impl Compiler {
}
}
+ fn find_local(&self, name: &str, block: &Block) -> Option<usize> {
+ self.stack.iter()
+ .rev()
+ .enumerate()
+ .find_map(|x| if x.1.name == name { Some(x.0) } else { None} )
+ }
+
+ fn variable_expression(&mut self, block: &mut Block) {
+ let name = match self.eat() {
+ Token::Identifier(name) => name,
+ _ => unreachable!(),
+ };
+ if let Some(id) = self.find_local(&name, block) {
+ block.add(Op::ReadLocal(id), self.line());
+ } else {
+ error!(self, format!("Using undefined variable {}.", name));
+ }
+ }
+
+ fn define_variable(&mut self, name: &str, typ: Type, block: &mut Block) {
+
+ if self.find_local(&name, block).is_some() {
+ error!(self, format!("Multiple definitions of {}.", name));
+ return;
+ }
+
+ self.expression(block);
+
+ self.stack.push(Variable { name: String::from(name), typ });
+
+ // block.add(Op::Assign(self.stack.len() - 1), self.line());
+ }
+
fn statement(&mut self, block: &mut Block) {
self.clear_panic();
- match self.peek() {
- Token::Print => {
+ match self.peek_four() {
+ (Token::Print, _, _, _) => {
self.eat();
self.expression(block);
block.add(Op::Print, self.line());
expect!(self, Token::Newline, "Expect newline after expression.");
},
+ (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));
+ }
+ expect!(self, Token::Newline, "Expect newline after expression.");
+ }
+
+ (Token::Identifier(name), Token::ColonEqual, _, _) => {
+ self.eat();
+ self.eat();
+ self.define_variable(&name, Type::UnkownType, block);
+ expect!(self, Token::Newline, "Expect newline after expression.");
+ }
+
_ => {
self.expression(block);
block.add(Op::Pop, self.line());