valhallac

Compiler for set-theoretic programming language.
git clone git://git.knutsen.co/valhallac
Log | Files | Refs | README | LICENSE

commit 14d55b03d14a2603228c71ae55059034267707ae
parent 149100af825eb0de6b2f505a226987b7053c2c67
Author: Demonstrandum <moi@knutsen.co>
Date:   Mon, 29 Jul 2019 21:29:57 +0100

Compilation errors support.

Diffstat:
MCargo.toml | 4++--
Msrc/compiler/block.rs | 30+++++++++++++++++++++++++++---
Msrc/compiler/instructions.rs | 6+++++-
Msrc/err.rs | 45++++++++++++++++++++++++++++++++++++++-------
Msrc/lib.rs | 2+-
Msrc/syntax/analyser.rs | 2--
Msrc/syntax/ast.rs | 11+++++++++++
Msrc/syntax/parser.rs | 32+++++++++++++++++++++-----------
Mtest.vh | 13+++++--------
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