commit 59e198024a6f1eb1001bcd79e71276580298e223
parent c13b46254d442af6b0f701ce98c4e5a39569efd3
Author: Demonstrandum <moi@knutsen.co>
Date: Wed, 7 Aug 2019 21:41:50 +0100
Rewrote static typing for variables.
Diffstat:
6 files changed, 145 insertions(+), 109 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -24,6 +24,7 @@ path = "src/bin.rs"
[dependencies]
lazy_static = "1.3.0"
regex = "1"
+unindent = "0.1.3"
snailquote = "0.2.0"
unicode-width = "0.1.5"
enum-primitive-derive = "^0.1"
diff --git a/src/err.rs b/src/err.rs
@@ -7,6 +7,8 @@ use std::io::{BufRead, BufReader};
use colored;
use colored::*;
+use unindent::unindent;
+
#[allow(non_camel_case_types)]
pub struct NO_TOKEN;
@@ -33,7 +35,8 @@ pub fn tissue(class : Types, filename : &str, token : &token::Token, message :
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!("{}{} {}", "issue".bold().red(), ":".white(), message);
+ let unindented = unindent(message);
+ eprintln!("{}{} {}", "issue".bold().red(), ":".white(), unindented.bold());
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,
@@ -48,7 +51,8 @@ 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());
+ let unindented = unindent(message);
+ eprintln!("{}{} {}", "issue".bold().red(), ":".white(), unindented.bold());
eprint!("{}", "".clear());
eprintln!(" ==> {class} in (`{file}`:{line}):\n{space}|\n{line_str}| {stuff}",
class=class.to_string().bold(), file=filename, line=line_n,
diff --git a/src/syntax/analyser.rs b/src/syntax/analyser.rs
@@ -1,4 +1,9 @@
+use std::collections::{HashMap, VecDeque};
+
+use crate::err;
+
use super::ast;
+use ast::Nodes;
/// Constant folding.
/// A static optimisation that relieves the runtime of having to perform
@@ -6,8 +11,8 @@ use super::ast;
/// instead. This function takes a node and recurses down, looking
/// for arithmetic operations containing exactly two numeric type nodes
/// as operands, and performs the stated operation.
-fn const_fold(node : &ast::Nodes) -> ast::Nodes {
- if let ast::Nodes::Call(call) = node {
+fn const_fold(node : &Nodes) -> Nodes {
+ if let Nodes::Call(call) = node {
if call.is_binary() {
let bin_op = call.callee.call().unwrap().callee.ident().unwrap();
let left = const_fold(&call.callee.call().unwrap().operands[0]);
@@ -39,7 +44,7 @@ fn const_fold(node : &ast::Nodes) -> ast::Nodes {
return default;
}
};
- return ast::Nodes::Num(ast::NumNode { value });
+ return Nodes::Num(ast::NumNode { value });
} else {
return default;
}
@@ -52,7 +57,7 @@ fn const_fold(node : &ast::Nodes) -> ast::Nodes {
}
-fn create_cast(node : &ast::Nodes, cast : &ast::StaticTypes) -> ast::Nodes {
+fn create_cast(node : &Nodes, cast : &ast::StaticTypes) -> Nodes {
let to_type = match cast {
ast::StaticTypes::TReal => ":Real",
ast::StaticTypes::TInteger => ":Int",
@@ -65,7 +70,7 @@ fn create_cast(node : &ast::Nodes, cast : &ast::StaticTypes) -> ast::Nodes {
ast::IdentNode::new("cast"),
vec![node.clone()]),
vec![ast::SymNode::new(to_type)]);
- if let ast::Nodes::Call(ref mut call) = cast_node {
+ if let Nodes::Call(ref mut call) = cast_node {
call.set_return_type(cast.clone())
}
cast_node
@@ -86,8 +91,8 @@ fn cast_strength(st : &ast::StaticTypes) -> i32 {
/// cast call to one of the arguments.
/// We always cast up (without loss of information), so, 4.3 + 6 will cast the 6
/// to be 6.0. i.e. 4.3 + 6 ==> 4.3 + (cast 6 :Real) <=> 4.3 + 6.0.
-fn balance_types(node : &ast::Nodes) -> ast::Nodes {
- if let ast::Nodes::Call(call) = node {
+fn balance_types(node : &Nodes) -> Nodes {
+ if let Nodes::Call(call) = node {
if call.is_binary() {
let bin_op = call.callee.call().unwrap().callee.ident().unwrap();
let left = balance_types(&call.callee.call().unwrap().operands[0]);
@@ -114,13 +119,13 @@ fn balance_types(node : &ast::Nodes) -> ast::Nodes {
vec![create_cast(&left, &cast_to)]),
vec![right]);
}
- if let ast::Nodes::Call(ref mut c) = new_call {
+ if let Nodes::Call(ref mut c) = new_call {
c.set_return_type(cast_to);
}
return new_call;
} else {
let mut cloned_node = node.clone();
- if let ast::Nodes::Call(ref mut c) = cloned_node {
+ if let Nodes::Call(ref mut c) = cloned_node {
c.set_return_type(right_yield);
}
return cloned_node;
@@ -132,7 +137,7 @@ fn balance_types(node : &ast::Nodes) -> ast::Nodes {
let mut new_call = ast::CallNode::new(
*call.callee.clone(),
vec![create_cast(&right, &left_yield)]);
- if let ast::Nodes::Call(ref mut c) = new_call {
+ if let Nodes::Call(ref mut c) = new_call {
c.set_return_type(left_yield);
}
return new_call;
@@ -143,7 +148,7 @@ fn balance_types(node : &ast::Nodes) -> ast::Nodes {
let mut non_bi = ast::CallNode::new(
balance_types(&*call.callee),
vec![balance_types(&call.operands[0])]);
- if let ast::Nodes::Call(ref mut c) = non_bi {
+ if let Nodes::Call(ref mut c) = non_bi {
c.set_return_type(node.yield_type());
}
return non_bi;
@@ -151,10 +156,90 @@ fn balance_types(node : &ast::Nodes) -> ast::Nodes {
return node.to_owned();
}
+type VarType = (String, ast::StaticTypes);
+
+struct TypeChecker {
+ source_line : usize,
+ source_file : String,
+ annotations : VecDeque<VarType>,
+ last_annotated : Option<VarType>,
+}
+
+impl TypeChecker {
+ pub fn new() -> Self {
+ Self {
+ source_line: 0,
+ source_file: String::from("UNANNOUNCED_FILE"),
+ annotations: VecDeque::new(),
+ last_annotated: None,
+ }
+ }
+
+ pub fn type_branch(&mut self, node : &Nodes) -> Nodes {
+ let mut clone = node.to_owned();
+ match clone {
+ Nodes::Line(l) => self.source_line = l.line,
+ Nodes::File(f) => self.source_file = f.filename.to_owned(),
+ Nodes::Ident(ref mut i) => {
+ for pairs in &self.annotations {
+ if pairs.0 == i.value {
+ if let ast::StaticTypes::TSet(class) = pairs.1.to_owned() {
+ i.static_type = *class;
+ }
+ }
+ }
+ return Nodes::Ident(i.to_owned());
+ }
+ Nodes::Call(ref mut call) => {
+ if let Nodes::Call(ref mut callee) = *call.callee {
+ if let Nodes::Ident(ref binary_ident) = *callee.callee {
+ match binary_ident.value.as_str() {
+ ":" => {
+ if let Nodes::Ident(ref mut annotatee) = callee.operands[0] {
+ let annotation = (
+ annotatee.value.to_owned(),
+ self.type_branch(&call.operands[0]).yield_type()
+ );
+ self.last_annotated = Some(annotation.clone());
+ self.annotations.push_back(annotation.clone());
+
+ if let ast::StaticTypes::TSet(class) = annotation.1 {
+ annotatee.static_type = *class;
+ }
+ return clone;
+ } else {
+ // Error: We need the left to be an ident.
+ issue!(err::Types::TypeError,
+ self.source_file.as_str(),
+ err::NO_TOKEN, self.source_line,
+ "The left side of the member-of operator (`:`), must be an identifier.
+ Only variable names can be declared as being members of sets.");
+ }
+ },
+ _ => ()
+ }
+ }
+ }
+ call.callee = Box::new(self.type_branch(&*call.callee));
+ call.operands = vec![self.type_branch(&call.operands[0])];
+ return Nodes::Call(call.to_owned());
+ },
+ _ => ()
+ };
+ node.to_owned()
+ }
+}
+
pub fn replace(root : &mut ast::Root) {
+ let mut type_checker = TypeChecker::new();
+
let length = root.branches.len();
let mut i = 0;
while i < length {
+ { // START TOP-LEVEL TYPE-CHECKING
+ let new = type_checker.type_branch(&root.branches[i]);
+ root.branches[i] = new;
+ } // END TOP-LEVEL TYPE-CHECKING
{ // START TOP-LEVEL CONSTANT FOLD
let new = const_fold(&root.branches[i]);
root.branches[i] = new;
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
@@ -331,8 +331,8 @@ impl Nodes {
/// of any syntactic node generated.
pub fn yield_type(&self) -> StaticTypes {
match self {
- Nodes::Num(nn) => {
- match nn.value {
+ Nodes::Num(num) => {
+ match num.value {
Numerics::Natural(_) => StaticTypes::TNatural,
Numerics::Integer(_) => StaticTypes::TInteger,
Numerics::Real(_) => StaticTypes::TReal,
@@ -340,9 +340,40 @@ impl Nodes {
},
Nodes::Str(_) => StaticTypes::TString,
Nodes::Sym(_) => StaticTypes::TSymbol,
- Nodes::Ident(i) => i.static_type.clone(),
- Nodes::Call(c) => c.return_type.clone(),
-
+ Nodes::Ident(ident) => {
+ match ident.value.as_str() {
+ "Nat" => StaticTypes::TSet(Box::new(StaticTypes::TNatural)),
+ "Int" => StaticTypes::TSet(Box::new(StaticTypes::TInteger)),
+ "Real" => StaticTypes::TSet(Box::new(StaticTypes::TReal)),
+ "Universal" => StaticTypes::TSet(Box::new(StaticTypes::TUnknown)),
+ _ => ident.static_type.to_owned()
+ }
+ },
+ Nodes::Call(call) => {
+ match &*call.callee {
+ Nodes::Ident(ident) => {
+ match ident.value.as_str() {
+ "Set" => return StaticTypes::TSet(Box::new(call.operands[0].yield_type())),
+ _ => ()
+ };
+ },
+ Nodes::Call(sub_call) => {
+ if let Nodes::Ident(ident) = &*sub_call.callee {
+ match ident.value.as_str() {
+ "->" => {
+ return StaticTypes::TFunction(
+ Box::new(sub_call.operands[0].yield_type()),
+ Box::new(call.operands[0].yield_type())
+ );
+ },
+ _ => ()
+ }
+ }
+ }
+ _ => ()
+ };
+ call.return_type.to_owned()
+ },
_ => StaticTypes::TUnknown
}
}
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -1,5 +1,3 @@
-use std::collections::{HashMap, VecDeque};
-
use super::token;
use super::ast;
use super::operators;
@@ -8,7 +6,6 @@ use super::super::err;
use token::{Token, TokenType};
use ast::Nodes;
-use ast::StaticTypes as ST;
pub fn parse(stream : Vec<Token>, file : &str) -> ast::Root {
let mut environment = ParseEnvironment::new(stream, file);
@@ -24,8 +21,6 @@ struct ParseEnvironment<'a> {
pub stream : Vec<Token>,
pub optable : operators::PrecedenceTable<'a>,
pub file : &'a str,
- pub annotations : VecDeque<ast::CallNode>,
- pub ident_types : HashMap<String, ast::StaticTypes>,
ignore_newline : bool,
line_number : usize,
@@ -37,8 +32,6 @@ impl<'a> ParseEnvironment<'a> {
root: ast::Root::new(),
stream: stream,
optable: operators::PrecedenceTable::new(),
- annotations: VecDeque::new(),
- ident_types: HashMap::new(),
file,
ignore_newline: false,
@@ -60,28 +53,7 @@ impl<'a> ParseEnvironment<'a> {
self.root.branches.push(e);
current = self.stream.get(0);
}
- self.assign_types();
- }
-
- fn get_type(&self, node : &ast::Nodes) -> ast::StaticTypes {
- if let Some(ident) = node.ident() {
- return match ident.value.as_str() {
- "Nat" => ST::TSet(Box::new(ST::TNatural)),
- "Int" => ST::TSet(Box::new(ST::TInteger)),
- "Real" => ST::TSet(Box::new(ST::TReal)),
- "Universal" => ST::TSet(Box::new(ST::TUnknown)),
- _ => ident.static_type.clone()
- };
- }
- node.yield_type()
- }
-
- fn remember_type(&mut self, k : String, v : ast::StaticTypes) {
- if self.ident_types.contains_key(&k) {
- self.ident_types.insert(k, ast::StaticTypes::TUnknown);
- return;
- }
- self.ident_types.insert(k, v);
+ //self.assign_types();
}
fn shift(&mut self) -> Token {
@@ -189,27 +161,6 @@ impl<'a> ParseEnvironment<'a> {
left = self.func_apply(left);
}
}
- if left.call().is_none() || !left.call().unwrap().is_binary() { return left; }
- if let Some(call_ident) = left.call().unwrap().callee.call().unwrap().callee.ident() {
- if call_ident.value == ":" {
- self.annotations.push_back(left.call().unwrap().clone());
- }
- if call_ident.value == "=" {
- let maybe_annotation = self.annotations.pop_front();
- if let Some(annotation) = maybe_annotation {
- let maybe_set = self.get_type(&annotation.operands[0]);
- if let ast::StaticTypes::TSet(set) = maybe_set {
- self.remember_type(
- left.call().unwrap().callee.call().unwrap().operands[0].ident().unwrap().value.to_owned(),
- *set);
- } else {
- // Error, annotation must be set.
- }
- } else {
- // Error, missing annotation for assignment.
- }
- }
- }
return left;
}
@@ -248,39 +199,6 @@ impl<'a> ParseEnvironment<'a> {
"Unexpected token type: `{}`, expected: `{}`.", t.class, tt);
}
}
-
-
-
- fn assign_types(&mut self) {
-
- fn recurse_type_assign(subtree : &Nodes, map : &HashMap<String, ST>) -> Nodes {
- match subtree {
- Nodes::Ident(ident_node) => {
- if map.contains_key(&ident_node.value) {
- let mut cloned_ident = ident_node.clone();
- cloned_ident.static_type = map[&ident_node.value].clone();
- return Nodes::Ident(cloned_ident);
- }
- },
- Nodes::Call(call_node) => {
- let mut cloned_call = call_node.clone();
- cloned_call.callee = Box::new(recurse_type_assign(&*call_node.callee, map));
- cloned_call.operands = vec![recurse_type_assign(&call_node.operands[0], map)];
- return Nodes::Call(cloned_call);
- },
- _ => ()
- };
- return subtree.to_owned();
- };
-
- let mut i = 0;
- let tree_size = self.root.branches.len();
-
- while i < tree_size {
- self.root.branches[i] = recurse_type_assign(&self.root.branches[i], &self.ident_types);
- i += 1;
- }
- }
}
#[cfg(test)]
diff --git a/test.vh b/test.vh
@@ -1,8 +1,5 @@
-a : Nat
-a = 3 + 9
+a : Set Nat
+a = Nat
-b : Real
-b = a + 4
-
-c : XyZ
-c = hello (3 * 8)-
\ No newline at end of file
+b : a
+b = 6 * 7+
\ No newline at end of file