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![
Ty::native_array(strings.intern("Huia.Native.Array")),
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_integer(strings.intern("Huia.Native.Integer")),
Ty::native_map(strings.intern("Huia.Native.Map")),

View file

@ -19,6 +19,7 @@ impl Builder {
/// Consume an AST node and build IR.
///
/// Any IR will be on the Builder's stack.
#[allow(clippy::cyclomatic_complexity)]
pub fn build(&mut self, node: Term, mut context: &mut Context) {
match node.node_type() {
NodeType::Array => {
@ -58,7 +59,18 @@ impl Builder {
self.build(rhs.clone(), &mut context);
let rhs = 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(
result_type,
location,

View file

@ -1,6 +1,7 @@
use crate::context::Context;
use crate::ir::{IRKind, Val, IR};
use huia_parser::ast::binary::Operator as BinOp;
use huia_parser::ast::unary::Operator as UnOp;
use std::f64;
/// 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() {
let lhs = lhs.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 {
ir.set_kind(IRKind::Constant(Val::Integer(value)));
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() {
let lhs = lhs.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 {
ir.set_kind(IRKind::Constant(Val::Float(value)));
let idx = context.constant_string("Huia.Native.Float");
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 {
BinOp::BitwiseAnd => 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 {
BinOp::Divide => Some(lhs / rhs),
BinOp::Minus => Some(lhs - rhs),
@ -71,7 +112,7 @@ mod test {
use huia_parser::ast::Term;
#[test]
fn integer_folding() {
fn integer_infix_folding() {
let term = Term::input("3 * 2 + 13 / 2").unwrap()[0].clone();
let mut context = Context::test();
let mut builder = Builder::default();
@ -85,7 +126,7 @@ mod test {
}
#[test]
fn float_folding() {
fn float_infix_folding() {
let term = Term::input("1.5 * 2.0 / 3.0").unwrap()[0].clone();
let mut context = Context::test();
let mut builder = Builder::default();
@ -97,4 +138,21 @@ mod test {
assert!(ir.is_constant());
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;
mod constant_folding;
pub mod builder;
pub mod constant_folding;
pub mod desugar_infix;
pub mod desugar_unary;
use crate::block::BlockIdx;
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> {
match self.kind {
IRKind::Constant(ref value) => Some(value),
@ -92,7 +101,7 @@ impl IR {
}
IRKind::MethodCall(ref mut callee, _, ref mut arguments) => {
improver(callee, &mut context);
for ref mut argument in arguments {
for argument in arguments {
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 {
match self.kind {
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> {
match self {
Val::Float(ref value) => Some(*value),

View file

@ -1,5 +1,6 @@
mod block;
mod context;
mod env;
mod error;
mod function;
mod ir;
@ -7,4 +8,25 @@ mod location;
mod method;
mod stable;
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::stable::StringIdx;
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 {
match self.kind {
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 {
Ty {
name: Some(name),
@ -223,6 +238,7 @@ enum TyInner {
},
NativeArray,
NativeAtom,
NativeBoolean,
NativeFloat,
NativeInteger,
NativeMap,

View file

@ -9,6 +9,16 @@ pub struct Binary {
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)]
pub enum Operator {
BitwiseAnd,
@ -23,7 +33,6 @@ pub enum Operator {
LessThanOrEqual,
LogicalAnd,
LogicalOr,
Method,
Minus,
Modulus,
Multiply,
@ -33,6 +42,39 @@ pub enum Operator {
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 {
type Item = Operator;

View file

@ -31,7 +31,7 @@ impl<'a> From<Pair<'a, Rule>> for Function {
fn from(pair: Pair<'a, Rule>) -> Self {
match pair.as_rule() {
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());
Function { clauses, location }
}

View file

@ -9,6 +9,16 @@ pub struct Unary {
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)]
pub enum Operator {
LogicalNot,
@ -16,6 +26,23 @@ pub enum Operator {
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 {
type Item = Operator;