commit 14d55b03d14a2603228c71ae55059034267707ae
parent 149100af825eb0de6b2f505a226987b7053c2c67
Author: Demonstrandum <moi@knutsen.co>
Date: Mon, 29 Jul 2019 21:29:57 +0100
Compilation errors support.
Diffstat:
9 files changed, 110 insertions(+), 35 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -27,4 +27,5 @@ regex = "1"
snailquote = "0.2.0"
unicode-width = "0.1.5"
enum-primitive-derive = "^0.1"
-num-traits = "^0.1"-
\ No newline at end of file
+num-traits = "^0.1"
+colored = "1.8"
diff --git a/src/compiler/block.rs b/src/compiler/block.rs
@@ -1,4 +1,7 @@
use std::fmt;
+use std::collections::HashMap;
+
+use super::super::err;
use super::super::syntax;
use syntax::ast;
@@ -28,18 +31,27 @@ pub fn numerics_to_element<'a>(num : &ast::Numerics) -> Element<'a> {
#[derive(Clone, PartialEq)]
pub struct LocalBlock<'a> {
pub name : &'a str,
+ filename : &'a str,
constants : Vec<Element<'a>>,
locals : Vec<Element<'a>>,
- instructions : Vec<Instr>
+ instructions : Vec<Instr>,
+
+ // Used only for compilation:
+ locals_map : HashMap<String, usize>,
+ current_line : usize,
}
impl<'a> LocalBlock<'a> {
- pub fn new(name : &'a str) -> Self {
+ pub fn new(name : &'a str, filename : &'a str) -> Self {
LocalBlock {
name,
+ filename,
constants: vec![],
locals: vec![],
- instructions: vec![]
+ instructions: vec![],
+
+ locals_map: HashMap::new(),
+ current_line: 0,
}
}
@@ -51,6 +63,18 @@ impl<'a> LocalBlock<'a> {
fn emit(&mut self, node : &'a ast::Nodes) {
match node {
+ ast::Nodes::Line(line_node) => {
+ self.current_line = line_node.line;
+ self.instructions.push(Instr::Operator(Operators::SET_LINE as u8));
+ self.instructions.push(Instr::Operand(self.current_line as u16));
+ }
+ ast::Nodes::Ident(ident_node) => {
+ let s = &ident_node.value;
+ if !self.locals_map.contains_key(s) {
+ issue!(err::Types::CompError, self.filename, err::NO_TOKEN, self.current_line,
+ "Trying to use unbound local variable `{}`.", s);
+ }
+ },
ast::Nodes::Num(num_node) => {
self.push_const_instr(numerics_to_element(&num_node.value));
},
diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs
@@ -53,7 +53,9 @@ pub enum Operators {
R_DIV = 55,
U_DIV = 56,
- NOP = 255
+ // Misc- / Meta-codes
+ SET_LINE = 254,
+ NOP = 255,
}
@@ -91,6 +93,8 @@ impl fmt::Display for Operators {
Operators::R_DIV => "R_DIV\n",
Operators::U_DIV => "U_DIV\n",
+ Operators::SET_LINE => "SET_LINE",
+
_ => "INVALID_OPCODE\n"
};
write!(f, "{}", s)
diff --git a/src/err.rs b/src/err.rs
@@ -4,6 +4,11 @@ use std::fs;
use std::fmt;
use std::io::{BufRead, BufReader};
+use colored;
+use colored::*;
+
+pub struct NO_TOKEN;
+
pub enum Types {
LexError,
ParseError,
@@ -23,31 +28,57 @@ impl fmt::Display for Types {
}
}
-pub fn fissue(class : Types, filename : &str, token : &token::Token, message : &str) {
+pub fn tissue(class : Types, filename : &str, token : &token::Token, message : &str) {
let file = fs::File::open(filename).expect("Invalid filename for error message.");
let line = BufReader::new(file).lines().nth((token.location.line - 1) as usize).unwrap().unwrap();
- eprintln!("{}", message);
- eprintln!(" ==> {class} in (`{file}`:{line}:{col}):\n{space}|\n{line_str}| {stuff}",
- class=class, file=filename, line=token.location.line,
+ eprintln!("{}{} {}", "issue".bold().red(), ":".white(), message);
+ eprint!("{}", "".clear());
+ eprintln!(" ==> {class} in (`{file}`:{line}:{col}):\n{space}|\n{line_str}| {stuff}",
+ class=class.to_string().bold(), file=filename, line=token.location.line,
col=token.location.col, space=" ".repeat(5),
- line_str=format!("{: >4} ", token.location.line), stuff=line);
+ line_str=format!("{: >4} ", token.location.line.to_string().bold()), stuff=line);
eprintln!("{space}|{: >offset$}",
"^".repeat(token.location.span as usize), space=" ".repeat(5),
offset=((token.location.col + token.location.span) as usize));
}
+pub fn lissue(class : Types, filename : &str, line_n : usize, message : &str) {
+ let file = fs::File::open(filename).expect("Invalid filename for error message.");
+ let line = BufReader::new(file).lines().nth((line_n - 1) as usize).unwrap().unwrap();
+
+ eprintln!("{}{} {}", "issue".bold().red(), ":".white(), message.bold());
+ eprint!("{}", "".clear());
+ eprintln!(" ==> {class} in (`{file}`:{line}):\n{space}|\n{line_str}| {stuff}",
+ class=class.to_string().bold(), file=filename, line=line_n,
+ space=" ".repeat(5),
+ line_str=format!("{: >4} ", line_n.to_string().bold()), stuff=line);
+ eprintln!(" |");
+}
+
#[macro_export]
macro_rules! issue {
+ ($type:path, $file:expr, err::NO_TOKEN, $line:expr, $message:expr) => {
+ {
+ err::lissue($type, $file, $line, $message);
+ std::process::exit(1)
+ }
+ };
+ ($type:path, $file:expr, err::NO_TOKEN, $line:expr, $message:expr, $($form:expr),*) => {
+ {
+ err::lissue($type, $file, $line, &format!($message, $($form),*));
+ std::process::exit(1)
+ }
+ };
($type:path, $file:expr, $token:expr, $message:expr) => {
{
- err::fissue($type, $file, $token, $message);
+ err::tissue($type, $file, $token, $message);
std::process::exit(1)
}
};
($type:path, $file:expr, $token:expr, $message:expr, $($form:expr),*) => {
{
- err::fissue($type, $file, $token, &format!($message, $($form),*));
+ err::tissue($type, $file, $token, &format!($message, $($form),*));
std::process::exit(1)
}
};
diff --git a/src/lib.rs b/src/lib.rs
@@ -16,7 +16,7 @@ pub mod compiler;
pub fn parse(filename : &str) {
let root = syntax::parse_file(filename);
- let mut code_block = compiler::block::LocalBlock::new("<main>");
+ let mut code_block = compiler::block::LocalBlock::new("<main>", filename);
code_block.generate(&root.branches);
diff --git a/src/syntax/analyser.rs b/src/syntax/analyser.rs
@@ -46,7 +46,6 @@ fn constant_fold(node : &ast::Nodes) -> Option<ast::Nodes> {
}
pub fn replace(root : &mut ast::Root) {
- println!("start replace");
let length = root.branches.len();
let mut i = 0;
while i < length {
@@ -59,5 +58,4 @@ pub fn replace(root : &mut ast::Root) {
} // END TOP-LEVEL CONSTANT FOLD
i += 1;
}
- println!("end replace");
}
\ No newline at end of file
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
@@ -223,6 +223,11 @@ pub struct BlockNode {
}
#[derive(Clone)]
+pub struct LineNode {
+ pub line : usize
+}
+
+#[derive(Clone)]
pub struct EmptyNode;
/// All base types, determined at compile time.
@@ -243,6 +248,7 @@ pub enum Nodes {
Sym(SymNode),
Call(CallNode),
Block(BlockNode),
+ Line(LineNode),
Empty(EmptyNode),
}
@@ -258,6 +264,7 @@ impl fmt::Display for Nodes {
"%call{{\n :callee ({})\n :operands [|\n {}\n |]\n}}", node.callee,
node.operands.iter().map(Nodes::to_string).collect::<Vec<String>>().join("\n ")),
Nodes::Block(node) => format!("%block{{ ... }}"),
+ Nodes::Line(node) => format!("%newline{{ :line {} }}", node.line),
Nodes::Empty(_) => String::from("()"),
};
write!(f, "{}", printable)
@@ -354,6 +361,10 @@ impl CallNode {
}
}
+impl LineNode {
+ pub fn new(line : usize) -> Nodes { Nodes::Line(LineNode { line }) }
+}
+
impl EmptyNode {
pub fn new() -> Nodes { Nodes::Empty(EmptyNode { }) }
}
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -2,7 +2,6 @@ use super::token;
use super::ast;
use super::operators;
-
use super::super::err;
use token::{Token, TokenType};
@@ -23,7 +22,8 @@ struct ParseEnvironment<'a> {
pub optable : operators::PrecedenceTable<'a>,
pub file : &'a str,
- ignore_newline : bool
+ ignore_newline : bool,
+ line_number : usize,
}
impl<'a> ParseEnvironment<'a> {
@@ -34,7 +34,8 @@ impl<'a> ParseEnvironment<'a> {
optable: operators::PrecedenceTable::new(),
file,
- ignore_newline: false
+ ignore_newline: false,
+ line_number: 0,
}
}
@@ -42,7 +43,7 @@ impl<'a> ParseEnvironment<'a> {
let mut current = self.stream.first();
while current.is_some() && current.unwrap().class != TokenType::EOF {
if current.unwrap().class == TokenType::Term {
- self.stream.remove(0);
+ self.shift();
current = self.stream.get(0);
continue;
}
@@ -52,9 +53,18 @@ impl<'a> ParseEnvironment<'a> {
}
}
+ fn shift(&mut self) -> Token {
+ let shifted = self.stream.remove(0);
+ if shifted.location.line as usize != self.line_number {
+ self.line_number = shifted.location.line as usize;
+ self.root.branches.push(ast::LineNode::new(self.line_number));
+ }
+ shifted
+ }
+
fn skip_newlines(&mut self) {
while !self.stream.is_empty() && self.stream[0].string == "\n" {
- self.stream.remove(0);
+ self.shift();
}
}
@@ -93,7 +103,7 @@ impl<'a> ParseEnvironment<'a> {
if current.is_none() || current.unwrap().class == TokenType::EOF {
self.expect(TokenType::RParen, current)
} else if current.unwrap().class == TokenType::RParen {
- self.stream.remove(0);
+ self.shift();
return ast::EmptyNode::new();
}
@@ -104,7 +114,7 @@ impl<'a> ParseEnvironment<'a> {
self.skip_newlines();
self.ignore_newline = false;
self.expect(TokenType::RParen, self.stream.get(0));
- self.stream.remove(0);
+ self.shift();
expr
}
_ => issue!(err::Types::ParseError, self.file, token,
@@ -113,9 +123,9 @@ impl<'a> ParseEnvironment<'a> {
}
fn expr(&mut self, right_prec : i32) -> Nodes {
- let mut popped = self.stream.remove(0);
+ let mut popped = self.shift();
while !self.stream.is_empty() && self.ignore_newline && popped.string == "\n" {
- popped = self.stream.remove(0);
+ popped = self.shift();
}
let mut left = self.null_den(&popped);
@@ -130,15 +140,15 @@ impl<'a> ParseEnvironment<'a> {
let next = &(&self.stream[0].string).clone();
if self.ignore_newline && next == "\n" {
- self.stream.remove(0);
+ self.shift();
continue;
}
if next == "\0" || next == "\n" || next == ")" { break; }
let maybe_op = self.optable.lookup(next, 2);
if let Some(op) = maybe_op {
- self.stream.remove(0);
let cloned = operators::Operator::new(next, op.precedence, op.associativity, 2);
+ self.shift();
left = self.left_den(left, cloned);
} else { // Function call.
left = self.func_apply(left);
diff --git a/test.vh b/test.vh
@@ -1,9 +1,6 @@
-2 + 3.0 -- Constant folds to 5.0 (Real).
+f = 2
+x = 4
--4 + (10 + 2) -- Folds to 8 (Natural).
+x
-3 + -2.0 -- Folds to 1.0 (Real).
-
-(-) 2 3 -- Folds to -1 (Integer).
-
-2 + 3 * 3 - 8 / 2.0 -- 7.0 (Real).-
\ No newline at end of file
+f x+
\ No newline at end of file