valhallac

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

commit 149100af825eb0de6b2f505a226987b7053c2c67
parent 1342459f378254412051a14b6512bedbbaf879d6
Author: Demonstrandum <moi@knutsen.co>
Date:   Mon, 29 Jul 2019 18:47:01 +0100

Implemented constant folding.

Diffstat:
Asamples/constant_folding.vh | 10++++++++++
Msrc/compiler/instructions.rs | 4+++-
Asrc/syntax/analyser.rs | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/syntax/ast.rs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/syntax/mod.rs | 6+++++-
Msrc/syntax/parser.rs | 28+++++++++++++++-------------
Mtest.vh | 14++++++++++----
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