Add some simple desugaring to the compiler.

This commit is contained in:
James Harton 2019-03-21 16:15:24 +13:00
parent a45d9627dc
commit 91a52574c8
11 changed files with 317 additions and 14 deletions

View file

@ -188,6 +188,7 @@ impl Context {
let types = vec![ let types = vec![
Ty::native_array(strings.intern("Huia.Native.Array")), Ty::native_array(strings.intern("Huia.Native.Array")),
Ty::native_atom(strings.intern("Huia.Native.Atom")), Ty::native_atom(strings.intern("Huia.Native.Atom")),
Ty::native_boolean(strings.intern("Huia.Native.Boolean")),
Ty::native_float(strings.intern("Huia.Native.Float")), Ty::native_float(strings.intern("Huia.Native.Float")),
Ty::native_integer(strings.intern("Huia.Native.Integer")), Ty::native_integer(strings.intern("Huia.Native.Integer")),
Ty::native_map(strings.intern("Huia.Native.Map")), Ty::native_map(strings.intern("Huia.Native.Map")),

View file

@ -19,6 +19,7 @@ impl Builder {
/// Consume an AST node and build IR. /// Consume an AST node and build IR.
/// ///
/// Any IR will be on the Builder's stack. /// Any IR will be on the Builder's stack.
#[allow(clippy::cyclomatic_complexity)]
pub fn build(&mut self, node: Term, mut context: &mut Context) { pub fn build(&mut self, node: Term, mut context: &mut Context) {
match node.node_type() { match node.node_type() {
NodeType::Array => { NodeType::Array => {
@ -58,7 +59,18 @@ impl Builder {
self.build(rhs.clone(), &mut context); self.build(rhs.clone(), &mut context);
let rhs = self.pop_ir().unwrap(); let rhs = self.pop_ir().unwrap();
let lhs = self.pop_ir().unwrap(); let lhs = self.pop_ir().unwrap();
let result_type = lhs.result_type();
let result_type = if op.is_logical() {
let name = context.constant_string("Huia.Native.Boolean");
context.reference_type(&name, location.clone())
}
else if op.is_arithmetic() {
lhs.result_type()
}
else {
panic!("Operator {:?} is neither arithmetic nor logical", op);
};
self.push_ir(IR::new_infix( self.push_ir(IR::new_infix(
result_type, result_type,
location, location,

View file

@ -1,6 +1,7 @@
use crate::context::Context; use crate::context::Context;
use crate::ir::{IRKind, Val, IR}; use crate::ir::{IRKind, Val, IR};
use huia_parser::ast::binary::Operator as BinOp; use huia_parser::ast::binary::Operator as BinOp;
use huia_parser::ast::unary::Operator as UnOp;
use std::f64; use std::f64;
/// Perform constant folding on the provided node, or not. /// Perform constant folding on the provided node, or not.
@ -17,7 +18,7 @@ pub fn pass(ir: &mut IR, context: &mut Context) {
if lhs.is_integer() && rhs.is_integer() { if lhs.is_integer() && rhs.is_integer() {
let lhs = lhs.integer().unwrap(); let lhs = lhs.integer().unwrap();
let rhs = rhs.integer().unwrap(); let rhs = rhs.integer().unwrap();
let value = perform_i64(&op, lhs, rhs); let value = perform_i64_infix_fold(&op, lhs, rhs);
if let Some(value) = value { if let Some(value) = value {
ir.set_kind(IRKind::Constant(Val::Integer(value))); ir.set_kind(IRKind::Constant(Val::Integer(value)));
let idx = context.constant_string("Huia.Native.Integer"); let idx = context.constant_string("Huia.Native.Integer");
@ -26,17 +27,57 @@ pub fn pass(ir: &mut IR, context: &mut Context) {
} else if lhs.is_float() && rhs.is_float() { } else if lhs.is_float() && rhs.is_float() {
let lhs = lhs.float().unwrap(); let lhs = lhs.float().unwrap();
let rhs = rhs.float().unwrap(); let rhs = rhs.float().unwrap();
let value = perform_f64(&op, lhs, rhs); let value = perform_f64_infix_fold(&op, lhs, rhs);
if let Some(value) = value { if let Some(value) = value {
ir.set_kind(IRKind::Constant(Val::Float(value))); ir.set_kind(IRKind::Constant(Val::Float(value)));
let idx = context.constant_string("Huia.Native.Float"); let idx = context.constant_string("Huia.Native.Float");
ir.set_result_type(context.reference_type(&idx, ir.location.clone())); ir.set_result_type(context.reference_type(&idx, ir.location.clone()));
} }
} }
} else if ir.is_unary() {
let (op, rhs) = ir.get_unary().unwrap();
if !rhs.is_constant() {
return;
}
let rhs = rhs.get_value().unwrap();
match op {
UnOp::LogicalNot => {
if rhs.is_boolean() {
// if the RHS is a boolean constant then fold it.
let value = rhs.boolean().unwrap();
ir.set_kind(IRKind::Constant(Val::Boolean(!value)));
} else {
// otherwise just return false for all other constant values.
ir.set_kind(IRKind::Constant(Val::Boolean(false)));
}
let tyname = context.constant_string("Huia.Native.Boolean");
let ty = context.reference_type(&tyname, ir.location.clone());
ir.set_result_type(ty)
}
UnOp::Minus => {
if rhs.is_integer() {
let value = rhs.integer().unwrap();
ir.set_kind(IRKind::Constant(Val::Integer(-value)));
} else if rhs.is_float() {
let value = rhs.float().unwrap();
ir.set_kind(IRKind::Constant(Val::Float(-value)));
}
}
UnOp::Plus => {
if rhs.is_integer() {
let value = rhs.integer().unwrap();
ir.set_kind(IRKind::Constant(Val::Integer(value)));
} else if rhs.is_float() {
let value = rhs.float().unwrap();
ir.set_kind(IRKind::Constant(Val::Float(value)));
}
}
}
} }
} }
fn perform_i64(op: &BinOp, lhs: i64, rhs: i64) -> Option<i64> { fn perform_i64_infix_fold(op: &BinOp, lhs: i64, rhs: i64) -> Option<i64> {
match op { match op {
BinOp::BitwiseAnd => Some(lhs & rhs), BinOp::BitwiseAnd => Some(lhs & rhs),
BinOp::BitwiseOr => Some(lhs | rhs), BinOp::BitwiseOr => Some(lhs | rhs),
@ -53,7 +94,7 @@ fn perform_i64(op: &BinOp, lhs: i64, rhs: i64) -> Option<i64> {
} }
} }
fn perform_f64(op: &BinOp, lhs: f64, rhs: f64) -> Option<f64> { fn perform_f64_infix_fold(op: &BinOp, lhs: f64, rhs: f64) -> Option<f64> {
match op { match op {
BinOp::Divide => Some(lhs / rhs), BinOp::Divide => Some(lhs / rhs),
BinOp::Minus => Some(lhs - rhs), BinOp::Minus => Some(lhs - rhs),
@ -71,7 +112,7 @@ mod test {
use huia_parser::ast::Term; use huia_parser::ast::Term;
#[test] #[test]
fn integer_folding() { fn integer_infix_folding() {
let term = Term::input("3 * 2 + 13 / 2").unwrap()[0].clone(); let term = Term::input("3 * 2 + 13 / 2").unwrap()[0].clone();
let mut context = Context::test(); let mut context = Context::test();
let mut builder = Builder::default(); let mut builder = Builder::default();
@ -85,7 +126,7 @@ mod test {
} }
#[test] #[test]
fn float_folding() { fn float_infix_folding() {
let term = Term::input("1.5 * 2.0 / 3.0").unwrap()[0].clone(); let term = Term::input("1.5 * 2.0 / 3.0").unwrap()[0].clone();
let mut context = Context::test(); let mut context = Context::test();
let mut builder = Builder::default(); let mut builder = Builder::default();
@ -97,4 +138,21 @@ mod test {
assert!(ir.is_constant()); assert!(ir.is_constant());
assert!(ir.get_value().unwrap().float().unwrap() - 1.0 < f64::EPSILON); assert!(ir.get_value().unwrap().float().unwrap() - 1.0 < f64::EPSILON);
} }
#[test]
fn integer_unary_folding() {
let term = Term::input("!3").unwrap()[0].clone();
let mut context = Context::test();
let mut builder = Builder::default();
builder.build(term, &mut context);
let mut ir = builder.pop_ir().unwrap();
ir.improve(&mut context, pass);
assert!(ir.is_constant());
assert!(!ir.get_value().unwrap().boolean().unwrap());
let ty = context.get_type(&ir.result_type()).unwrap();
assert!(ty.is_native_boolean());
}
} }

View file

@ -0,0 +1,59 @@
use crate::context::Context;
use crate::ir::{IRKind, IR};
use huia_parser::ast::binary::Operator as BinOp;
/// Desugar infix operations into method calls.
pub fn pass(ir: &mut IR, context: &mut Context) {
if ir.is_infix() {
let (op, lhs, rhs) = ir.get_infix().unwrap();
let method_name = match op {
BinOp::BitwiseAnd => "bitwise_and",
BinOp::BitwiseOr => "bitwise_or",
BinOp::BitwiseXor => "bitwise_xor",
BinOp::Divide => "divide",
BinOp::Equal => "equal",
BinOp::Exponent => "exponent",
BinOp::GreaterThan => "greater_than",
BinOp::GreaterThanOrEqual => "greater_than_or_equal",
BinOp::LessThan => "less_than",
BinOp::LessThanOrEqual => "less_than_or_equal",
BinOp::LogicalAnd => "logical_and",
BinOp::LogicalOr => "logical_or",
BinOp::Minus => "minus",
BinOp::Modulus => "modulus",
BinOp::Multiply => "multiply",
BinOp::NotEqual => "not_equal",
BinOp::Plus => "plus",
BinOp::ShiftLeft => "shift_left",
BinOp::ShiftRight => "shift_right",
};
let method_name = context.constant_string(method_name);
ir.kind = IRKind::MethodCall(Box::new(lhs.clone()), method_name, vec![rhs.clone()]);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::ir::builder::Builder;
use huia_parser::ast::Term;
#[test]
fn works() {
let terms = Term::input("3 * 2").unwrap();
let mut context = Context::test();
let mut builder = Builder::default();
for term in terms {
builder.build(term, &mut context);
}
let mut ir = builder.pop_ir().unwrap();
assert!(ir.is_infix());
ir.improve(&mut context, pass);
assert!(ir.is_method_call());
}
}

View file

@ -0,0 +1,43 @@
use crate::context::Context;
use crate::ir::{IRKind, IR};
use huia_parser::ast::unary::Operator as UnOp;
/// Desugar unary operations into method calls.
pub fn pass(ir: &mut IR, context: &mut Context) {
if ir.is_unary() {
let (op, rhs) = ir.get_unary().unwrap();
let method_name = match op {
UnOp::LogicalNot => "logical_not",
UnOp::Minus => "unary_minus",
UnOp::Plus => "unary_plus",
};
let method_name = context.constant_string(method_name);
ir.kind = IRKind::MethodCall(Box::new(rhs.clone()), method_name, Vec::default());
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::ir::builder::Builder;
use huia_parser::ast::Term;
#[test]
fn works() {
let terms = Term::input("!3").unwrap();
let mut context = Context::test();
let mut builder = Builder::default();
for term in terms {
builder.build(term, &mut context);
}
let mut ir = builder.pop_ir().unwrap();
assert!(ir.is_unary());
ir.improve(&mut context, pass);
assert!(ir.is_method_call());
}
}

View file

@ -1,5 +1,7 @@
mod builder; pub mod builder;
mod constant_folding; pub mod constant_folding;
pub mod desugar_infix;
pub mod desugar_unary;
use crate::block::BlockIdx; use crate::block::BlockIdx;
use crate::context::Context; use crate::context::Context;
@ -33,6 +35,13 @@ impl IR {
} }
} }
pub fn get_unary(&self) -> Option<(&UnOp, &IR)> {
match self.kind {
IRKind::Unary(ref op, ref rhs) => Some((op, rhs)),
_ => None,
}
}
pub fn get_value(&self) -> Option<&Val> { pub fn get_value(&self) -> Option<&Val> {
match self.kind { match self.kind {
IRKind::Constant(ref value) => Some(value), IRKind::Constant(ref value) => Some(value),
@ -92,7 +101,7 @@ impl IR {
} }
IRKind::MethodCall(ref mut callee, _, ref mut arguments) => { IRKind::MethodCall(ref mut callee, _, ref mut arguments) => {
improver(callee, &mut context); improver(callee, &mut context);
for ref mut argument in arguments { for argument in arguments {
improver(argument, &mut context); improver(argument, &mut context);
} }
} }
@ -189,6 +198,13 @@ impl IR {
} }
} }
pub fn is_method_call(&self) -> bool {
match self.kind {
IRKind::MethodCall(..) => true,
_ => false,
}
}
pub fn is_type_reference(&self) -> bool { pub fn is_type_reference(&self) -> bool {
match self.kind { match self.kind {
IRKind::TypeReference(..) => true, IRKind::TypeReference(..) => true,
@ -428,6 +444,13 @@ impl Val {
} }
} }
pub fn boolean(&self) -> Option<bool> {
match self {
Val::Boolean(ref value) => Some(*value),
_ => None,
}
}
pub fn float(&self) -> Option<f64> { pub fn float(&self) -> Option<f64> {
match self { match self {
Val::Float(ref value) => Some(*value), Val::Float(ref value) => Some(*value),

View file

@ -1,5 +1,6 @@
mod block; mod block;
mod context; mod context;
mod env;
mod error; mod error;
mod function; mod function;
mod ir; mod ir;
@ -7,4 +8,25 @@ mod location;
mod method; mod method;
mod stable; mod stable;
mod ty; mod ty;
mod env;
use crate::context::Context;
use huia_parser::ast::Term;
use std::fs;
pub fn compile_file(path: &str) -> Context {
let contents = fs::read_to_string(path).expect("Unable to open file");
let terms = Term::file(&contents).expect("Unable to parse file");
let mut context = Context::new(path);
let mut builder = ir::builder::Builder::default();
for term in terms {
builder.build(term, &mut context);
}
context.improve_ir(|mut ir, mut context| {
ir::constant_folding::pass(&mut ir, &mut context);
ir::desugar_infix::pass(&mut ir, &mut context);
ir::desugar_unary::pass(&mut ir, &mut context);
});
context
}

View file

@ -1,4 +1,3 @@
use crate::ir::IR;
use crate::location::Location; use crate::location::Location;
use crate::stable::StringIdx; use crate::stable::StringIdx;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -32,6 +31,13 @@ impl Ty {
} }
} }
pub fn is_native_boolean(&self) -> bool {
match self.inner {
TyInner::NativeBoolean => true,
_ => false,
}
}
pub fn is_unresolved(&self) -> bool { pub fn is_unresolved(&self) -> bool {
match self.kind { match self.kind {
TyKind::Unresolved => true, TyKind::Unresolved => true,
@ -135,6 +141,15 @@ impl Ty {
} }
} }
pub fn native_boolean(name: StringIdx) -> Ty {
Ty {
name: Some(name),
location: None,
kind: TyKind::Native,
inner: TyInner::NativeBoolean,
}
}
pub fn native_float(name: StringIdx) -> Ty { pub fn native_float(name: StringIdx) -> Ty {
Ty { Ty {
name: Some(name), name: Some(name),
@ -223,6 +238,7 @@ enum TyInner {
}, },
NativeArray, NativeArray,
NativeAtom, NativeAtom,
NativeBoolean,
NativeFloat, NativeFloat,
NativeInteger, NativeInteger,
NativeMap, NativeMap,

View file

@ -9,6 +9,16 @@ pub struct Binary {
location: InputLocation, location: InputLocation,
} }
impl Binary {
pub fn is_arithmetic(&self) -> bool {
self.value.is_arithmetic()
}
pub fn is_logical(&self) -> bool {
self.value.is_logical()
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Operator { pub enum Operator {
BitwiseAnd, BitwiseAnd,
@ -23,7 +33,6 @@ pub enum Operator {
LessThanOrEqual, LessThanOrEqual,
LogicalAnd, LogicalAnd,
LogicalOr, LogicalOr,
Method,
Minus, Minus,
Modulus, Modulus,
Multiply, Multiply,
@ -33,6 +42,39 @@ pub enum Operator {
ShiftRight, ShiftRight,
} }
impl Operator {
pub fn is_arithmetic(&self) -> bool {
match self {
Operator::BitwiseAnd => true,
Operator::BitwiseOr => true,
Operator::BitwiseXor => true,
Operator::Divide => true,
Operator::Exponent => true,
Operator::Minus => true,
Operator::Modulus => true,
Operator::Multiply => true,
Operator::Plus => true,
Operator::ShiftLeft => true,
Operator::ShiftRight => true,
_ => false,
}
}
pub fn is_logical(&self) -> bool {
match self {
Operator::Equal => true,
Operator::GreaterThan => true,
Operator::GreaterThanOrEqual => true,
Operator::LessThan => true,
Operator::LessThanOrEqual => true,
Operator::LogicalAnd => true,
Operator::LogicalOr => true,
Operator::NotEqual => true,
_ => false,
}
}
}
impl Value for Binary { impl Value for Binary {
type Item = Operator; type Item = Operator;

View file

@ -31,7 +31,7 @@ impl<'a> From<Pair<'a, Rule>> for Function {
fn from(pair: Pair<'a, Rule>) -> Self { fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() { match pair.as_rule() {
Rule::function => { Rule::function => {
let clauses = pair.clone().into_inner().map(|p| Clause::from(p)).collect(); let clauses = pair.clone().into_inner().map(Clause::from).collect();
let location = InputLocation::from(pair.into_span()); let location = InputLocation::from(pair.into_span());
Function { clauses, location } Function { clauses, location }
} }

View file

@ -9,6 +9,16 @@ pub struct Unary {
location: InputLocation, location: InputLocation,
} }
impl Unary {
pub fn is_arithmetic(&self) -> bool {
self.value.is_arithmetic()
}
pub fn is_logical(&self) -> bool {
self.value.is_logical()
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Operator { pub enum Operator {
LogicalNot, LogicalNot,
@ -16,6 +26,23 @@ pub enum Operator {
Plus, Plus,
} }
impl Operator {
pub fn is_arithmetic(&self) -> bool {
match self {
Operator::Minus => true,
Operator::Plus => true,
_ => false,
}
}
pub fn is_logical(&self) -> bool {
match self {
Operator::LogicalNot => true,
_ => false,
}
}
}
impl Value for Unary { impl Value for Unary {
type Item = Operator; type Item = Operator;