valhallac

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

commit d912dc4659fd5744e26810e444d5c635d3c34f39
parent 72b3c7a9d43eadedbaf1fa0dddfc784feb034487
Author: Demonstrandum <moi@knutsen.co>
Date:   Sat, 27 Jul 2019 02:36:08 +0100

Started on compiler, basic operations on constants.

Diffstat:
Asamples/set_comp.vh | 2++
R.ideas/subsets.vh -> samples/subsets.vh | 0
Msrc/bin.rs | 13++++++++++++-
Asrc/compiler/block.rs | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/compiler/element.rs | 28++++++++++++++++++++++++++++
Asrc/compiler/instructions.rs | 19+++++++++++++++++++
Asrc/compiler/marshal.rs | 0
Asrc/compiler/mod.rs | 6++++++
Msrc/lib.rs | 13++++++++-----
Msrc/syntax/mod.rs | 4++--
Msrc/syntax/parser.rs | 67+++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtest.vh | 13+++++--------
12 files changed, 174 insertions(+), 38 deletions(-)

diff --git a/samples/set_comp.vh b/samples/set_comp.vh @@ -0,0 +1 @@ +n : Nat => n mod 3 is 0+ \ No newline at end of file diff --git a/.ideas/subsets.vh b/samples/subsets.vh diff --git a/src/bin.rs b/src/bin.rs @@ -1,5 +1,16 @@ use valhalla; +use std::env; + +fn is_vh_file(filename : &String) -> bool { + filename.ends_with(".vh") +} pub fn main() { - valhalla::parse(); + let args = env::args(); + + let files = args.filter(is_vh_file); + + for file in files { + valhalla::parse(&file); + } } \ No newline at end of file diff --git a/src/compiler/block.rs b/src/compiler/block.rs @@ -0,0 +1,47 @@ +use super::super::syntax; +use syntax::ast; + +use super::element; +use super::instructions; + +use element::Element; +use instructions::{Instr, Operators}; + +#[derive(Debug)] +pub struct LocalBlock<'a> { + constants : Vec<Element<'a>>, + locals : Vec<Element<'a>>, + instructions : Vec<Instr> +} + +impl<'a> LocalBlock<'a> { + pub fn new() -> Self { + LocalBlock { + constants: vec![], + locals: vec![], + instructions: vec![] + } + } + + pub fn generate(&mut self, node : ast::Nodes) { + match node { + ast::Nodes::Num(num_node) => { + let elem = match num_node.value { + ast::Numerics::Natural(n) => Element::ENatural(n), + ast::Numerics::Integer(n) => Element::EInteger(n), + ast::Numerics::Real(n) => Element::EReal(n) + }; + let index = self.append_const(elem); + self.instructions.push(Instr::Operator(Operators::PUSH_CONST as u8)); + self.instructions.push(Instr::Operand(index as u16)) + }, + _ => () + }; + } + + fn append_const(&mut self, e : Element<'a>) -> usize { + let index = self.constants.iter().position(|c| c == &e); + if index.is_none() { self.constants.push(e); } + index.unwrap_or(self.constants.len() - 1) + } +} diff --git a/src/compiler/element.rs b/src/compiler/element.rs @@ -0,0 +1,27 @@ +use std::fmt; + +pub struct Symbol<'a> { + hash : u32, + string : &'a str +} + +impl<'a> fmt::Debug for Symbol<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, ":{}", self.string) + } +} + +impl<'a> PartialEq for Symbol<'a> { + fn eq(&self, other : &Self) -> bool { + self.hash == other.hash + } +} + +#[derive(PartialEq, Debug)] +pub enum Element<'a> { + ENatural(usize), + EInteger(isize), + EReal(f64), + EString(String), + ESymbol(Symbol<'a>), +}+ \ No newline at end of file diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs @@ -0,0 +1,18 @@ +#[derive(Debug)] +pub enum Instr { + Operator(u8), + Operand(u16) +} + +#[repr(u8)] +pub enum Operators { + HALT = 0, + PUSH_CONST = 1, + PUSH_LOCAL = 2, + PUSH_SUPER = 3, + POP = 4, + STORE_LOCAL = 5, + DUP = 6, + DUP_N = 7, + SWAP = 8, +}+ \ No newline at end of file diff --git a/src/compiler/marshal.rs b/src/compiler/marshal.rs diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs @@ -0,0 +1,5 @@ +//! Compilation of the syntax tree. +mod element; +mod instructions; +pub mod block; +mod marshal;+ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs @@ -5,11 +5,14 @@ /// Syntax submodule, responsible for lexical analysis, /// parsing and static analysis. mod syntax; +mod compiler; - -pub fn parse() { - println!("\nTill Valhalla!\n"); - - syntax::parse_file("./test.vh"); +pub fn parse(filename : &str) { + syntax::parse_file(filename); + let mut code_block = compiler::block::LocalBlock::new(); + code_block.generate(syntax::ast::NumNode::new(3.14)); + code_block.generate(syntax::ast::NumNode::new(34)); + code_block.generate(syntax::ast::NumNode::new(3.14)); + println!("Code Block:\n{:#?}", code_block) } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs @@ -8,14 +8,14 @@ mod location; mod token; /// Abstract Syntax Tree nodes and methods. -mod ast; +pub mod ast; /// Dealing with associativity and precedence. mod operators; /// Error messages. #[macro_use] -mod err; +pub mod err; /// Lexer splits code up into a token-stream /// of relevant lexical tokens, making the diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs @@ -19,7 +19,9 @@ struct ParseEnvironment<'a> { pub root : ast::Root, pub stream : Vec<Token>, pub optable : operators::PrecedenceTable<'a>, - pub file : &'a str + pub file : &'a str, + + ignore_newline : bool } impl<'a> ParseEnvironment<'a> { @@ -28,7 +30,9 @@ impl<'a> ParseEnvironment<'a> { root: ast::Root::new(), stream: stream, optable: operators::PrecedenceTable::new(), - file + file, + + ignore_newline: false } } @@ -46,6 +50,12 @@ impl<'a> ParseEnvironment<'a> { } } + fn skip_newlines(&mut self) { + while !self.stream.is_empty() && self.stream[0].string == "\n" { + self.stream.remove(0); + } + } + fn null_den(&mut self, token : &Token) -> Nodes { match token.class { TokenType::Op | TokenType::Ident => { @@ -73,6 +83,7 @@ impl<'a> ParseEnvironment<'a> { }, TokenType::Num => ast::NumNode::new(&*token.string), TokenType::Str => ast::StrNode::new(&token.string), + TokenType::Sym => ast::SymNode::new(&token.string), TokenType::LParen => { let current = self.stream.get(0); if current.is_none() || current.unwrap().class == TokenType::EOF { @@ -81,7 +92,13 @@ impl<'a> ParseEnvironment<'a> { self.stream.remove(0); return ast::EmptyNode::new(); } + + + self.ignore_newline = true; + self.skip_newlines(); let expr = self.expr(0); + self.skip_newlines(); + self.ignore_newline = false; self.expect(TokenType::RParen, self.stream.get(0)); self.stream.remove(0); expr @@ -92,9 +109,13 @@ impl<'a> ParseEnvironment<'a> { } fn expr(&mut self, right_prec : i32) -> Nodes { - let popped = &self.stream.remove(0); - let mut left = self.null_den(popped); + let mut popped = self.stream.remove(0); + while !self.stream.is_empty() && self.ignore_newline && popped.string == "\n" { + popped = self.stream.remove(0); + } + let mut left = self.null_den(&popped); + if self.ignore_newline { self.skip_newlines(); } if self.stream.is_empty() || self.stream[0].class == TokenType::EOF || self.stream[0].class == TokenType::Term @@ -104,6 +125,10 @@ impl<'a> ParseEnvironment<'a> { while self.optable.precedence(&self.stream[0].string).unwrap_or(190) > right_prec { let next = &(&self.stream[0].string).clone(); + if self.ignore_newline && next == "\n" { + self.stream.remove(0); + continue; + } if next == "\0" || next == "\n" || next == ")" { break; } let maybe_op = self.optable.lookup(next, 2); @@ -112,36 +137,34 @@ impl<'a> ParseEnvironment<'a> { let cloned = operators::Operator::new(next, op.precedence, op.associativity, 2); left = self.left_den(left, cloned); } else { // Function call. - let mut pushed = false; - match left { - Nodes::Call(ref mut call) => { - if call.operands.is_empty() { - call.operands.push(self.expr(190)); - pushed = true; - } - } - _ => () - }; - if !pushed { - left = self.func_appl(left); - } + left = self.func_apply(left); } } return left; } - fn func_appl(&mut self, left : Nodes) -> Nodes { - println!("Creating function call with:\n --> {}", left); + fn func_apply(&mut self, mut left : Nodes) -> Nodes { + let mut pushed = false; + match left { + Nodes::Call(ref mut call) => { + if call.operands.is_empty() { + call.operands.push(self.expr(190)); + pushed = true; + } + }, + _ => () + }; + if pushed { return left; } ast::CallNode::new(left, vec![self.expr(190)]) } fn left_den(&mut self, left : Nodes, op : operators::Operator) -> Nodes { - let first_appl = ast::CallNode::new(ast::IdentNode::new(op.name), vec![left]); + let first_apply = ast::CallNode::new(ast::IdentNode::new(op.name), vec![left]); if self.stream[0].class == TokenType::RParen { - return first_appl; + return first_apply; } let right = self.expr(op.precedence - (if op.is_right() { 1 } else { 0 })); - ast::CallNode::new(first_appl, vec![right]) + ast::CallNode::new(first_apply, vec![right]) } fn expect(&self, tt : TokenType, maybe_t : Option<&Token>) { diff --git a/test.vh b/test.vh @@ -1,7 +1,4 @@ -2 + 3 --- same as -(+) 2 3 --- same as -(2 +) 3 --- same as -(+ 3) 2- \ No newline at end of file +2 +34 +2.3 +-2+ \ No newline at end of file