commit 7b68c972a5cc2b827ee8c5f9e43f4aaf14eb7469
parent 2cc247137d27d4ab36719a37f2219e1858450385
Author: Demonstrandum <moi@knutsen.co>
Date: Tue, 30 Jul 2019 22:36:01 +0100
Basic type-annotations implemented.
Diffstat:
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