commit 149100af825eb0de6b2f505a226987b7053c2c67
parent 1342459f378254412051a14b6512bedbbaf879d6
Author: Demonstrandum <moi@knutsen.co>
Date: Mon, 29 Jul 2019 18:47:01 +0100
Implemented constant folding.
Diffstat:
7 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/samples/constant_folding.vh b/samples/constant_folding.vh
@@ -0,0 +1,9 @@
+2 + 3.0 -- Constant folds to 5.0 (Real).
+
+-4 + (10 + 2) -- Folds to 8 (Natural).
+
+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
diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs
@@ -1,7 +1,7 @@
use std::fmt;
use enum_primitive_derive::Primitive;
-use num_traits::{FromPrimitive, ToPrimitive};
+use num_traits::{FromPrimitive};
#[derive(Debug, Clone, PartialEq)]
pub enum Instr {
@@ -52,6 +52,8 @@ pub enum Operators {
I_DIV = 54,
R_DIV = 55,
U_DIV = 56,
+
+ NOP = 255
}
diff --git a/src/syntax/analyser.rs b/src/syntax/analyser.rs
@@ -0,0 +1,63 @@
+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() {
+ let operation = node.call().unwrap().callee.call().unwrap().callee.ident();
+ if let Some(op) = operation {
+ let right = node.call().unwrap().operands.get(0);
+ let left = node.call().unwrap().callee.call().unwrap().operands.get(0);
+
+ if left.is_none()
+ || right.is_none()
+ { return None; }
+
+ let mut l_value = ast::Numerics::Natural(0);
+ let mut r_value = ast::Numerics::Natural(0);
+
+ if left.unwrap().num().is_some()
+ && right.unwrap().num().is_some() {
+ 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 foldl = constant_fold(&l.unwrap());
+ let foldr = constant_fold(&r.unwrap());
+ if foldl.is_none() || foldr.is_none() { return None; }
+
+ l_value = foldl.unwrap().num().unwrap().value;
+ r_value = foldr.unwrap().num().unwrap().value;
+ }
+ return Some(ast::Nodes::Num(ast::NumNode {
+ value: match op.value.as_str() {
+ "+" => l_value + r_value,
+ "-" => l_value - r_value,
+ "*" => l_value * r_value,
+ "/" => l_value / r_value,
+ _ => ast::Numerics::Natural(0)
+ }
+ }));
+ }
+ }
+ None
+}
+
+pub fn replace(root : &mut ast::Root) {
+ println!("start replace");
+ let length = root.branches.len();
+ let mut i = 0;
+ while i < length {
+ let node = &root.branches[i];
+ { // START TOP-LEVEL CONSTANT FOLD
+ let new = constant_fold(node);
+ if let Some(nbranch) = new {
+ root.branches[i] = nbranch;
+ }
+ } // 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
@@ -1,4 +1,4 @@
-use std::fmt;
+use std::{fmt, ops};
/// Identifiers, node representing a name that
/// will represent a value stored.
@@ -10,7 +10,7 @@ pub struct IdentNode {
/// Different types of possible number types in the langauge.
/// Max size is determined by max pointer size.
-#[derive(PartialEq, Clone, Debug)]
+#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Numerics {
/// Naturals are unsigned ints.
Natural(usize),
@@ -20,6 +20,79 @@ pub enum Numerics {
Real(f64)
}
+fn stronges_cast(left : Numerics, right : Numerics) -> BaseTypes {
+ let mut cast = BaseTypes::TNatural;
+ match left {
+ Numerics::Real(_) => cast = BaseTypes::TReal,
+ Numerics::Integer(_) => cast = BaseTypes::TInteger,
+ _ => ()
+ };
+ if cast == BaseTypes::TReal { return cast; }
+ match right {
+ Numerics::Real(_) => cast = BaseTypes::TReal,
+ Numerics::Integer(_) => cast = BaseTypes::TInteger,
+ _ => ()
+ };
+ cast
+}
+
+macro_rules! new_base {
+ ($arg:expr, $base:ident) => {
+ match &$arg {
+ Numerics::Natural(n) => *n as $base,
+ Numerics::Integer(n) => *n as $base,
+ Numerics::Real(n) => *n as $base,
+ };
+ };
+}
+
+macro_rules! fold_on_numeric {
+ ($op:tt, $left:expr, $right:expr) => {
+ {
+ let cast = stronges_cast($left, $right);
+ match cast {
+ BaseTypes::TNatural => (new_base!($left, usize) $op new_base!($right, usize)).to_numeric(),
+ BaseTypes::TInteger => (new_base!($left, isize) $op new_base!($right, isize)).to_numeric(),
+ BaseTypes::TReal => (new_base!($left, f64) $op new_base!($right, f64)).to_numeric(),
+ _ => panic!("Numeric porting non-numeric type?")
+ }
+ }
+ };
+}
+
+impl ops::Add<Numerics> for Numerics {
+ type Output = Numerics;
+ fn add(self, right : Numerics) -> Numerics {
+ fold_on_numeric!(+, self, right)
+ }
+}
+
+impl ops::Sub<Numerics> for Numerics {
+ type Output = Numerics;
+ fn sub(self, right : Numerics) -> Numerics {
+ if fold_on_numeric!(>, right, self) == Numerics::Natural(1) {
+ if let Numerics::Natural(u) = right {
+ return fold_on_numeric!(-, self, Numerics::Integer(u as isize));
+ }
+ }
+ fold_on_numeric!(-, self, right)
+ }
+}
+
+impl ops::Mul<Numerics> for Numerics {
+ type Output = Numerics;
+ fn mul(self, right : Numerics) -> Numerics {
+ fold_on_numeric!(*, self, right)
+ }
+}
+
+impl ops::Div<Numerics> for Numerics {
+ type Output = Numerics;
+ fn div(self, right : Numerics) -> Numerics {
+ fold_on_numeric!(/, self, right)
+ }
+}
+
/// Parse a string of more than two chars with a specified radix, into an ast::Numeric.
fn parse_with_radix(neg : bool, s : &str, radix : u32) -> Numerics {
let unsigned = usize::from_str_radix(s.get(2..).unwrap(), radix).unwrap();
@@ -71,6 +144,10 @@ impl ToNumeric for &str {
}
}
+impl ToNumeric for bool {
+ fn to_numeric(&self) -> Numerics { Numerics::Natural(if *self { 1 } else { 0 }) }
+}
+
impl ToNumeric for usize {
fn to_numeric(&self) -> Numerics { Numerics::Natural(*self) }
}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
@@ -21,6 +21,9 @@ pub mod lexer;
/// Converts a token-stream into a nested AST.
pub mod parser;
+/// Tree static analysis.
+pub mod analyser;
+
use std::fs;
use token::ShowStream;
@@ -34,7 +37,8 @@ pub fn parse_file(filename : &str) -> ast::Root {
let stream = lexer::lex(&code);
println!("Stream:\n{}\n", stream.to_string());
- let tree = parser::parse(stream, filename);
+ let mut tree = parser::parse(stream, filename);
println!("AST:\n{}\n", tree);
+ analyser::replace(&mut tree);
tree
}
\ No newline at end of file
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
@@ -54,7 +54,7 @@ impl<'a> ParseEnvironment<'a> {
fn skip_newlines(&mut self) {
while !self.stream.is_empty() && self.stream[0].string == "\n" {
- self.stream.remove(0);
+ self.stream.remove(0);
}
}
@@ -64,22 +64,24 @@ impl<'a> ParseEnvironment<'a> {
let is_op = self.optable.exists(&token.string);
if is_op {
let prefix = self.optable.lookup(&token.string, 1);
- if prefix.is_none() {
- return match self.stream[0].class {
- TokenType::RParen => {
- ast::CallNode::new(ast::IdentNode::new(&token.string), vec![])
- },
- _ => ast::CallNode::new(
+ return match self.stream[0].class {
+ TokenType::RParen => {
+ ast::CallNode::new(ast::IdentNode::new(&token.string), vec![])
+ },
+ _ => {
+ if prefix.is_none() {
+ ast::CallNode::new(
ast::CallNode::new(
ast::IdentNode::new(&token.string),
vec![ast::EmptyNode::new()]),
vec![self.expr(500)])
- };
- } else { // It is a prefix unary operator.
- return ast::CallNode::new(
- ast::IdentNode::new(&token.string),
- vec![self.expr(500)]);
- }
+ } else {
+ ast::CallNode::new(
+ ast::IdentNode::new(&token.string),
+ vec![self.expr(500)])
+ }
+ }
+ };
}
ast::IdentNode::new(&token.string)
},
diff --git a/test.vh b/test.vh
@@ -1,4 +1,9 @@
-4 + 3
-5 - 4
-3 * 4.0
-3 + :a
+2 + 3.0 -- Constant folds to 5.0 (Real).
+
+-4 + (10 + 2) -- Folds to 8 (Natural).
+
+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