Ensure that if
expressions parse and build correctly.
This commit is contained in:
parent
536e5cc5f4
commit
ee3df9a527
6 changed files with 175 additions and 52 deletions
|
@ -1,8 +1,11 @@
|
|||
use crate::ir::IR;
|
||||
use crate::stable::StringIdx;
|
||||
use crate::ty::TyIdx;
|
||||
use std::slice::IterMut;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct Block {
|
||||
arguments: Vec<(StringIdx, TyIdx)>,
|
||||
ir: Vec<IR>,
|
||||
}
|
||||
|
||||
|
@ -20,6 +23,13 @@ impl Block {
|
|||
self.ir.iter_mut()
|
||||
}
|
||||
|
||||
pub fn new(arguments: Vec<(StringIdx, TyIdx)>) -> Block {
|
||||
Block {
|
||||
arguments,
|
||||
ir: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<IR> {
|
||||
self.ir.pop()
|
||||
}
|
||||
|
@ -27,6 +37,10 @@ impl Block {
|
|||
pub fn push(&mut self, ir: IR) {
|
||||
self.ir.push(ir);
|
||||
}
|
||||
|
||||
pub fn set_arguments(&mut self, arguments: Vec<(StringIdx, TyIdx)>) {
|
||||
self.arguments = arguments;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -79,12 +79,8 @@ impl Builder {
|
|||
|
||||
let result_type = context.unknown_type(location.clone());
|
||||
|
||||
self.push_ir(IR::new_get_local(
|
||||
result_type.clone(),
|
||||
location.clone(),
|
||||
name,
|
||||
));
|
||||
self.push_ir(IR::new_call(result_type, location, arguments));
|
||||
let local = IR::new_get_local(result_type.clone(), location.clone(), name);
|
||||
self.push_ir(IR::new_call(result_type, location, local, arguments));
|
||||
}
|
||||
NodeType::Constructor => {
|
||||
let (ty, props) = node.constructor().unwrap();
|
||||
|
@ -152,7 +148,8 @@ impl Builder {
|
|||
self.build(node.clone(), &mut context);
|
||||
}
|
||||
|
||||
let clause_data = self.pop_block().unwrap();
|
||||
let mut clause_data = self.pop_block().unwrap();
|
||||
clause_data.set_arguments(arguments.clone());
|
||||
let idx = context.push_block(clause_data);
|
||||
fun.push(arguments, idx);
|
||||
}
|
||||
|
@ -180,15 +177,31 @@ impl Builder {
|
|||
self.build(node.clone(), &mut context);
|
||||
}
|
||||
let negative = self.pop_block().unwrap();
|
||||
let negative_idx = context.push_block(negative);
|
||||
|
||||
self.push_ir(IR::new_if(
|
||||
result_type,
|
||||
location,
|
||||
test,
|
||||
positive_idx,
|
||||
negative_idx,
|
||||
));
|
||||
if negative.is_empty() {
|
||||
// there's no `else` block, so let's just skip it.
|
||||
self.push_ir(IR::new_jump_if_true(
|
||||
result_type,
|
||||
location,
|
||||
test,
|
||||
positive_idx,
|
||||
));
|
||||
} else {
|
||||
// there is an else block, so let's push true and false jumps.
|
||||
let negative_idx = context.push_block(negative);
|
||||
self.push_ir(IR::new_jump_if_true(
|
||||
result_type.clone(),
|
||||
location.clone(),
|
||||
test.clone(),
|
||||
positive_idx,
|
||||
));
|
||||
self.push_ir(IR::new_jump_if_false(
|
||||
result_type,
|
||||
location,
|
||||
test,
|
||||
negative_idx,
|
||||
));
|
||||
}
|
||||
}
|
||||
NodeType::ImplDef => {
|
||||
let (ty, body) = node.impldef().unwrap();
|
||||
|
@ -319,7 +332,8 @@ impl Builder {
|
|||
for node in block {
|
||||
self.build(node.clone(), &mut context);
|
||||
}
|
||||
let clause_data = self.pop_block().unwrap();
|
||||
let mut clause_data = self.pop_block().unwrap();
|
||||
clause_data.set_arguments(arguments.clone());
|
||||
let idx = context.push_block(clause_data);
|
||||
method.push(arguments, idx);
|
||||
|
||||
|
@ -373,7 +387,8 @@ impl Builder {
|
|||
for node in block {
|
||||
self.build(node.clone(), &mut context);
|
||||
}
|
||||
let clause_data = self.pop_block().unwrap();
|
||||
let mut clause_data = self.pop_block().unwrap();
|
||||
clause_data.set_arguments(arguments.clone());
|
||||
let idx = context.push_block(clause_data);
|
||||
method.push(arguments, idx);
|
||||
|
||||
|
@ -427,7 +442,8 @@ impl Builder {
|
|||
for node in block {
|
||||
self.build(node.clone(), &mut context);
|
||||
}
|
||||
let clause_data = self.pop_block().unwrap();
|
||||
let mut clause_data = self.pop_block().unwrap();
|
||||
clause_data.set_arguments(arguments.clone());
|
||||
let idx = context.push_block(clause_data);
|
||||
method.push(arguments, idx);
|
||||
|
||||
|
@ -663,6 +679,33 @@ mod test {
|
|||
assert!(context.find_type("Huia.Native.Integer").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if() {
|
||||
let term = Term::input(r#" if true do 123 end "#).unwrap()[0].clone();
|
||||
let mut builder = Builder::default();
|
||||
let mut context = Context::test();
|
||||
builder.build(term, &mut context);
|
||||
let ir = builder.pop_ir().unwrap();
|
||||
assert!(ir.is_jump_if_true());
|
||||
let block = builder.pop_block().unwrap();
|
||||
assert!(block.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_else() {
|
||||
let term = Term::input(r#" if true do 123 else 456 end "#).unwrap()[0].clone();
|
||||
let mut builder = Builder::default();
|
||||
let mut context = Context::test();
|
||||
builder.build(term, &mut context);
|
||||
let ir = builder.pop_ir().unwrap();
|
||||
assert!(ir.is_jump_if_false());
|
||||
let ir = builder.pop_ir().unwrap();
|
||||
assert!(ir.is_jump_if_true());
|
||||
let block = builder.pop_block().unwrap();
|
||||
println!("Block: {:?}", block);
|
||||
assert!(block.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_infix() {
|
||||
let term = Term::input(r#" 1 + 2 "#).unwrap()[0].clone();
|
||||
|
@ -704,9 +747,7 @@ mod test {
|
|||
let mut context = Context::test();
|
||||
builder.build(term, &mut context);
|
||||
let call = builder.pop_ir().unwrap();
|
||||
let receiver = builder.pop_ir().unwrap();
|
||||
assert!(call.is_call());
|
||||
assert!(receiver.is_get_local());
|
||||
assert!(context.find_type("Huia.Native.Integer").is_some());
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ impl IR {
|
|||
improver: F,
|
||||
) {
|
||||
match self.kind {
|
||||
IRKind::Call(ref mut args) => {
|
||||
IRKind::Call(ref mut fun, ref mut args) => {
|
||||
improver(fun, &mut context);
|
||||
for mut arg in args.iter_mut() {
|
||||
improver(&mut arg, &mut context);
|
||||
}
|
||||
|
@ -78,13 +79,17 @@ impl IR {
|
|||
IRKind::FunctionRef(..) => (),
|
||||
IRKind::GetLocal(..) => (),
|
||||
IRKind::GetProperty(..) => (),
|
||||
IRKind::If(ref mut test, ..) => {
|
||||
improver(test, &mut context);
|
||||
}
|
||||
IRKind::Infix(_, ref mut lhs, ref mut rhs) => {
|
||||
improver(lhs, &mut context);
|
||||
improver(rhs, &mut context);
|
||||
}
|
||||
IRKind::Jump(..) => (),
|
||||
IRKind::JumpIfFalse(ref mut test, ..) => {
|
||||
improver(test, &mut context);
|
||||
}
|
||||
IRKind::JumpIfTrue(ref mut test, ..) => {
|
||||
improver(test, &mut context);
|
||||
}
|
||||
IRKind::SetLocal(_, ref mut rhs) => {
|
||||
improver(rhs, &mut context);
|
||||
}
|
||||
|
@ -157,6 +162,27 @@ impl IR {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_jump(&self) -> bool {
|
||||
match self.kind {
|
||||
IRKind::Jump(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_jump_if_false(&self) -> bool {
|
||||
match self.kind {
|
||||
IRKind::JumpIfFalse(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_jump_if_true(&self) -> bool {
|
||||
match self.kind {
|
||||
IRKind::JumpIfTrue(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_type_reference(&self) -> bool {
|
||||
match self.kind {
|
||||
IRKind::TypeReference(..) => true,
|
||||
|
@ -178,11 +204,16 @@ impl IR {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_call(result_type: TyIdx, location: Location, arguments: Vec<IR>) -> IR {
|
||||
pub fn new_call(
|
||||
result_type: TyIdx,
|
||||
location: Location,
|
||||
function: IR,
|
||||
arguments: Vec<IR>,
|
||||
) -> IR {
|
||||
IR {
|
||||
location,
|
||||
result_type,
|
||||
kind: IRKind::Call(arguments),
|
||||
kind: IRKind::Call(Box::new(function), arguments),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,20 +261,6 @@ impl IR {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_if(
|
||||
result_type: TyIdx,
|
||||
location: Location,
|
||||
test: IR,
|
||||
positive: BlockIdx,
|
||||
negative: BlockIdx,
|
||||
) -> IR {
|
||||
IR {
|
||||
location,
|
||||
result_type,
|
||||
kind: IRKind::If(Box::new(test), positive, negative),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_infix(result_type: TyIdx, location: Location, op: BinOp, lhs: IR, rhs: IR) -> IR {
|
||||
let lhs = Box::new(lhs);
|
||||
let rhs = Box::new(rhs);
|
||||
|
@ -254,6 +271,40 @@ impl IR {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_jump(result_type: TyIdx, location: Location, target: BlockIdx) -> IR {
|
||||
IR {
|
||||
location,
|
||||
result_type,
|
||||
kind: IRKind::Jump(target),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_jump_if_false(
|
||||
result_type: TyIdx,
|
||||
location: Location,
|
||||
test: IR,
|
||||
target: BlockIdx,
|
||||
) -> IR {
|
||||
IR {
|
||||
location,
|
||||
result_type,
|
||||
kind: IRKind::JumpIfFalse(Box::new(test), target),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_jump_if_true(
|
||||
result_type: TyIdx,
|
||||
location: Location,
|
||||
test: IR,
|
||||
target: BlockIdx,
|
||||
) -> IR {
|
||||
IR {
|
||||
location,
|
||||
result_type,
|
||||
kind: IRKind::JumpIfTrue(Box::new(test), target),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR {
|
||||
IR {
|
||||
location,
|
||||
|
@ -306,7 +357,7 @@ impl IR {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum IRKind {
|
||||
/// Call top of the stack with arguments.
|
||||
Call(Vec<IR>),
|
||||
Call(Box<IR>, Vec<IR>),
|
||||
/// A constant value of the specified type.
|
||||
Constant(Val),
|
||||
/// Construct an instance of type with the provided properties.
|
||||
|
@ -317,8 +368,12 @@ pub enum IRKind {
|
|||
GetLocal(StringIdx),
|
||||
/// Refernce an instance property.
|
||||
GetProperty(StringIdx),
|
||||
/// Consists of test, positives, negatives.
|
||||
If(Box<IR>, BlockIdx, BlockIdx),
|
||||
/// Unconditional jump.
|
||||
Jump(BlockIdx),
|
||||
/// Jump if IR value is false.
|
||||
JumpIfFalse(Box<IR>, BlockIdx),
|
||||
/// Jump if IR value is true.
|
||||
JumpIfTrue(Box<IR>, BlockIdx),
|
||||
/// A binary operation with LHS and RHS.
|
||||
Infix(BinOp, Box<IR>, Box<IR>),
|
||||
/// Take a value and set it as a local variable.
|
||||
|
|
|
@ -17,7 +17,7 @@ impl StringTable {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct StringIdx(Sym);
|
||||
|
||||
impl From<Sym> for StringIdx {
|
||||
|
|
|
@ -521,12 +521,7 @@ impl<'a> From<Pair<'a, Rule>> for Term {
|
|||
Rule::if_expression => {
|
||||
let mut inner = pair.clone().into_inner();
|
||||
let test = Term::from(inner.next().unwrap());
|
||||
let positives = inner
|
||||
.next()
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.map(|p| Term::from(p))
|
||||
.collect();
|
||||
let positives = inner.next().unwrap().into_inner().map(Term::from).collect();
|
||||
let negatives = inner.next();
|
||||
if negatives.is_some() {
|
||||
let negatives = negatives.unwrap().into_inner().map(Term::from).collect();
|
||||
|
@ -1087,6 +1082,24 @@ mod test {
|
|||
assert_eq!(clause1.body().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if() {
|
||||
let terms = Term::input(" if true do 123 end ").unwrap();
|
||||
let (_test, positive, negative) = terms[0].if_expr().unwrap();
|
||||
assert_eq!(positive.len(), 1);
|
||||
assert!(negative.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_else() {
|
||||
let terms = Term::input(" if true do 123 else 456 end ").unwrap();
|
||||
let (_test, positive, negative) = terms[0].if_expr().unwrap();
|
||||
println!("positive: {:?}", positive);
|
||||
println!("negative: {:?}", negative);
|
||||
assert_eq!(positive.len(), 1);
|
||||
assert_eq!(negative.len(), 1);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_acceptance() {
|
||||
// let terms = Term::input(
|
||||
|
|
|
@ -3,7 +3,7 @@ file = _{ SOI ~ definition* ~ EOI }
|
|||
|
||||
WHITESPACE = _{ (" " | "\t" | "\r" | "\n")+ }
|
||||
newline = _{ (" " | "\t")* ~ ("\n" | "\r")+ ~ (" " | "\t")* }
|
||||
reserved = { "end" | "let" | "true" | "false" }
|
||||
reserved = { "end" | "let" | "true" | "false" | "else" }
|
||||
|
||||
expression = _{ infix | expression_inner }
|
||||
infix = { expression_inner ~ (binary_operator ~ expression_inner)+ }
|
||||
|
@ -53,7 +53,7 @@ literal = _{ constructor | map | array | typename | string |
|
|||
ident = @{ !reserved ~ 'a'..'z' ~ ('a'..'z' | 'A'..'Z' | "_")* }
|
||||
keyword = @{ ident ~ ":" }
|
||||
|
||||
if_expression = { "if" ~ instance_espression ~ if_positive ~ (if_negative | "end") }
|
||||
if_expression = { "if" ~ instance_espression ~ if_positive ~ if_negative? ~ "end" }
|
||||
if_positive = { "do" ~ instance_espression* }
|
||||
if_negative = { "else" ~ instance_espression* }
|
||||
|
||||
|
|
Reference in a new issue