valhallac

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

commit 7b68c972a5cc2b827ee8c5f9e43f4aaf14eb7469
parent 2cc247137d27d4ab36719a37f2219e1858450385
Author: Demonstrandum <moi@knutsen.co>
Date:   Tue, 30 Jul 2019 22:36:01 +0100

Basic type-annotations implemented.

Diffstat:
Msrc/compiler/block.rs | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/compiler/element.rs | 3+++
Msrc/compiler/instructions.rs | 6++++--
Msrc/compiler/mod.rs | 1+
Asrc/compiler/types.rs | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/syntax/analyser.rs | 18++++++++++++++----
Msrc/syntax/ast.rs | 1+
Mtest.vh | 6+++---
8 files changed, 149 insertions(+), 26 deletions(-)

diff --git a/src/compiler/block.rs b/src/compiler/block.rs @@ -1,5 +1,6 @@ use std::fmt; use std::collections::HashMap; +use std::collections::VecDeque; use super::super::err; @@ -8,13 +9,14 @@ use syntax::ast; use super::element; use super::instructions; +use super::types; use element::{Element, Symbol}; use instructions::{Instr, Operators}; use super::internal_functions; -fn append_unique<'a>(v : &mut Vec<Element<'a>>, e : Element<'a>) -> usize { +fn append_unique<'a, T : Clone + PartialEq>(v : &mut Vec<T>, e : T) -> usize { let index = v.iter().position(|c| c == &e); if index.is_none() { v.push(e.clone()); } index.unwrap_or(v.len() - 1) @@ -28,29 +30,41 @@ pub fn numerics_to_element<'a>(num : &ast::Numerics) -> Element<'a> { } } -#[derive(Clone, PartialEq)] +#[derive(Clone)] +struct IdentTypePair<'a>(String, &'a ast::Nodes); + +#[derive(Clone)] pub struct LocalBlock<'a> { pub name : &'a str, filename : &'a str, constants : Vec<Element<'a>>, - locals : Vec<Element<'a>>, instructions : Vec<Instr>, + globals : Vec<String>, // Used only for compilation: locals_map : HashMap<String, u16>, + types_to_check : VecDeque<IdentTypePair<'a>>, current_line : usize, } +impl<'a> PartialEq for LocalBlock<'a> { + fn eq(&self, other : &Self) -> bool { + self.constants == other.constants + && self.instructions == other.instructions + } +} + impl<'a> LocalBlock<'a> { pub fn new(name : &'a str, filename : &'a str) -> Self { LocalBlock { name, filename, constants: vec![], - locals: vec![], instructions: vec![], + globals: vec![], locals_map: HashMap::new(), + types_to_check: VecDeque::new(), current_line: 0, } } @@ -61,6 +75,31 @@ impl<'a> LocalBlock<'a> { self.instructions.push(Instr::Operand(index as u16)); } + fn ident_assignment(&mut self, left : &ast::IdentNode, right : &'a ast::Nodes) { + if self.types_to_check.is_empty() { + issue!(err::Types::TypeError, self.filename, err::NO_TOKEN, self.current_line, + "You must state what set `{}' is a member of. No type annotation found.", left.value); + } + if self.locals_map.contains_key(&left.value) { + issue!(err::Types::CompError, self.filename, err::NO_TOKEN, self.current_line, + "Cannot mutate value of `{}', as is already bound.", left.value); + } + let index = self.locals_map.len() as u16; + self.locals_map.insert(left.value.to_owned(), index); + + self.emit(right); + self.instructions.push(Instr::Operator(Operators::DUP as u8)); + let type_node = self.types_to_check.pop_front().unwrap().1; + self.emit(type_node); + self.instructions.push(Instr::Operator(Operators::CHECK_TYPE as u8)); + self.instructions.push(Instr::Operator(Operators::STORE_LOCAL as u8)); + self.instructions.push(Instr::Operand(index)); + } + + fn annotation(&mut self, left : &ast::IdentNode, right : &'a ast::Nodes) { + self.types_to_check.push_back(IdentTypePair(left.value.to_owned(), right)); + } + fn emit(&mut self, node : &'a ast::Nodes) { match node { ast::Nodes::Line(line_node) => { @@ -71,8 +110,10 @@ impl<'a> LocalBlock<'a> { 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); + self.instructions.push(Instr::Operator(Operators::PUSH_SUPER as u8)); + let index = append_unique(&mut self.globals, s.to_owned()); + self.instructions.push(Instr::Operand(index as u16)); + return; } self.instructions.push(Instr::Operator(Operators::PUSH_LOCAL as u8)); @@ -99,16 +140,23 @@ impl<'a> LocalBlock<'a> { if ident.value == "=" { // Direct variable assignment: if let Some(left) = args[0].ident() { - if self.locals_map.contains_key(&left.value) { - issue!(err::Types::CompError, self.filename, err::NO_TOKEN, self.current_line, - "Cannot mutate value of `{}', as is already bound.", left.value); - } - let index = self.locals_map.len() as u16; - self.locals_map.insert(left.value.to_owned(), index); - self.emit(args[1]); - self.instructions.push(Instr::Operator(Operators::STORE_LOCAL as u8)); - self.instructions.push(Instr::Operand(index)); + self.ident_assignment(left, args[1]); + } + return; + } + + // Check for type annotation. + if ident.value == ":" { + // If the LHS is not an ident, it is not a + // valid annotation. + if args[0].ident().is_none() { + issue!(err::Types::CompError, self.filename, err::NO_TOKEN, self.current_line, + "Left of `:` type annotator must be an identifier."); } + let left = args[0].ident().unwrap(); + + // Annotation of variable or function. + self.annotation(left, args[1]); return; } @@ -123,8 +171,7 @@ impl<'a> LocalBlock<'a> { } self.emit(&call_node.operands[0]); self.emit(&*call_node.callee); - self.instructions.push(Instr::Operator(Operators::CALL_N as u8)); - self.instructions.push(Instr::Operand(2)); + self.instructions.push(Instr::Operator(Operators::CALL_1 as u8)); }, _ => () }; @@ -147,6 +194,10 @@ impl<'a> fmt::Display for LocalBlock<'a> { for key in self.locals_map.keys() { write!(f, "{: >3} | {}\n", self.locals_map[key], key)?; } + write!(f, "===Globals=================\n")?; + for (i, c) in self.globals.iter().enumerate() { + write!(f, "{: >3} | {}\n", i, c)?; + } write!(f, "===Bytecodes===============\n")?; for inst in &self.instructions { write!(f, "{}", inst)?; diff --git a/src/compiler/element.rs b/src/compiler/element.rs @@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher}; use snailquote::escape; use super::block; +use super::types; #[derive(Clone, Copy)] pub struct Symbol<'a> { @@ -47,6 +48,7 @@ pub enum Element<'a> { EString(&'a str), ESymbol(Symbol<'a>), ECode(block::LocalBlock<'a>), + ESet(types::Set<'a>), ENil } @@ -71,6 +73,7 @@ impl<'a> fmt::Display for Element<'a> { Element::EString(t) => format!("{: <5} => (String)", format!("\"{}\"", escape(t))), Element::ESymbol(t) => format!("{: <5} => (Sym) ", t.to_string()), Element::ECode(t) => format!("{: <5} => (Block) ", t.name), + Element::ESet(t) => format!("{: <5p} => (Set) ", t), Element::ENil => format!("{: <5} => (Nil) ", "nil"), }; write!(f, "{}", s) diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs @@ -39,7 +39,8 @@ pub enum Operators { DUP = 6, DUP_N = 7, SWAP = 8, - CALL_N = 9, + CALL_1 = 9, + CHECK_TYPE = 10, N_ADD = 40, I_ADD = 41, @@ -77,7 +78,8 @@ impl fmt::Display for Operators { Operators::DUP => "DUP\n", Operators::DUP_N => "DUP_N", Operators::SWAP => "SWAP\n", - Operators::CALL_N => "CALL_N", + Operators::CALL_1 => "CALL_1\n", + Operators::CHECK_TYPE => "CHECK_TYPE\n", Operators::N_ADD => "N_ADD\n", Operators::I_ADD => "I_ADD\n", diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs @@ -2,5 +2,6 @@ pub mod element; pub mod instructions; pub mod internal_functions; +pub mod types; pub mod block; pub mod marshal; \ No newline at end of file diff --git a/src/compiler/types.rs b/src/compiler/types.rs @@ -0,0 +1,54 @@ +use super::element; +use element::Element; + +use super::block; + +use super::super::syntax::ast; + +macro_rules! is_elem { + ($e:expr, $elem:path) => { + { + match $e { + $elem(_) => true, + _ => false + } + } + }; +} + +#[derive(Clone, PartialEq)] +pub struct Set<'a> { + base_type : Option<ast::BaseTypes>, + elements : Vec<Element<'a>>, + unions : Vec<Set<'a>>, + intersections : Vec<Set<'a>>, + difference : Vec<Set<'a>>, + conditons : block::LocalBlock<'a> +} + +impl<'a> Set<'a> { + pub fn new(filename : &'a str, base_type : Option<ast::BaseTypes>) -> Self { + Self { + base_type, + elements: vec![], + unions: vec![], + intersections: vec![], + difference: vec![], + conditons : block::LocalBlock::new("<set-conditions>", filename) + } + } + pub fn is_memeber(&self, e : Element) -> bool { + if let Some(base) = self.base_type { + return match base { + ast::BaseTypes::TNatural => is_elem!(e, Element::ENatural), + ast::BaseTypes::TInteger => is_elem!(e, Element::EInteger), + ast::BaseTypes::TReal => is_elem!(e, Element::EReal), + ast::BaseTypes::TSym => is_elem!(e, Element::ESymbol), + ast::BaseTypes::TString => is_elem!(e, Element::EString), + ast::BaseTypes::TNil => e == Element::ENil, + _ => false + }; + } + false + } +}+ \ No newline at end of file diff --git a/src/syntax/analyser.rs b/src/syntax/analyser.rs @@ -1,5 +1,6 @@ use super::ast; + fn constant_fold(node : &ast::Nodes) -> Option<ast::Nodes> { if node.num().is_some() { return Some(node.clone()); } if node.call().is_some() && node.call().unwrap().is_binary() { @@ -38,9 +39,14 @@ fn constant_fold(node : &ast::Nodes) -> Option<ast::Nodes> { l_value = left.unwrap().num().unwrap().value; r_value = right.unwrap().num().unwrap().value; } else { - let l = constant_fold(left.unwrap()); - let r = constant_fold(right.unwrap()); - if l.is_none() || r.is_none() { return None; } + let mut l = constant_fold(left.unwrap()); + let mut r = constant_fold(right.unwrap()); + if l.is_none() && r.is_none() { return None; } + if l.is_some() { + r = Some(right.unwrap().clone()); + } else { + l = Some(left.unwrap().clone()); + } let foldl = constant_fold(&l.unwrap()); let foldr = constant_fold(&r.unwrap()); @@ -55,7 +61,10 @@ fn constant_fold(node : &ast::Nodes) -> Option<ast::Nodes> { "*" => l_value * r_value, "/" => { if r_value == ast::Numerics::Natural(0) { - return None; + return Some(ast::CallNode::new( + ast::CallNode::new(ast::IdentNode::new("/"), + vec![ast::Nodes::Num(ast::NumNode { value : l_value })]), + vec![ast::NumNode::new(0)])); } l_value / r_value }, @@ -80,4 +89,5 @@ pub fn replace(root : &mut ast::Root) { } // END TOP-LEVEL CONSTANT FOLD i += 1; } + println!("\n\n{}", root); } \ No newline at end of file diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs @@ -236,6 +236,7 @@ pub enum BaseTypes { TNatural, TInteger, TReal, TString, TSym, + TNil, TUnknown } diff --git a/test.vh b/test.vh @@ -1,2 +1,2 @@ -a = 3 + 6 -b = a + 2- \ No newline at end of file +a : Int +a = 3 + 9+ \ No newline at end of file