feat: add support for booleans and comparision operators
This commit is contained in:
@@ -192,6 +192,14 @@ impl<'a> FunctionTranslator<'a> {
|
||||
let inner_val = self.translate_expr(inner);
|
||||
match op {
|
||||
UnaryOp::Neg => self.builder.ins().ineg(inner_val),
|
||||
UnaryOp::Not => {
|
||||
// `!x` is equivalent to `x == 0` for booleans (0 or 1).
|
||||
let ty = CraneliftBackend::lower_type(&inner.ty);
|
||||
let zero = self.builder.ins().iconst(ty, 0);
|
||||
self.builder
|
||||
.ins()
|
||||
.icmp(ir::condcodes::IntCC::Equal, inner_val, zero)
|
||||
}
|
||||
}
|
||||
}
|
||||
TypedExprKind::Binary { op, lhs, rhs } => {
|
||||
@@ -218,6 +226,48 @@ impl<'a> FunctionTranslator<'a> {
|
||||
self.builder.ins().urem(lhs_val, rhs_val)
|
||||
}
|
||||
}
|
||||
BinaryOp::Eq => {
|
||||
self.builder
|
||||
.ins()
|
||||
.icmp(ir::condcodes::IntCC::Equal, lhs_val, rhs_val)
|
||||
}
|
||||
BinaryOp::Neq => {
|
||||
self.builder
|
||||
.ins()
|
||||
.icmp(ir::condcodes::IntCC::NotEqual, lhs_val, rhs_val)
|
||||
}
|
||||
BinaryOp::Lt => {
|
||||
let cc = if is_signed {
|
||||
ir::condcodes::IntCC::SignedLessThan
|
||||
} else {
|
||||
ir::condcodes::IntCC::UnsignedLessThan
|
||||
};
|
||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||
}
|
||||
BinaryOp::Le => {
|
||||
let cc = if is_signed {
|
||||
ir::condcodes::IntCC::SignedLessThanOrEqual
|
||||
} else {
|
||||
ir::condcodes::IntCC::UnsignedLessThanOrEqual
|
||||
};
|
||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||
}
|
||||
BinaryOp::Gt => {
|
||||
let cc = if is_signed {
|
||||
ir::condcodes::IntCC::SignedGreaterThan
|
||||
} else {
|
||||
ir::condcodes::IntCC::UnsignedGreaterThan
|
||||
};
|
||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||
}
|
||||
BinaryOp::Ge => {
|
||||
let cc = if is_signed {
|
||||
ir::condcodes::IntCC::SignedGreaterThanOrEqual
|
||||
} else {
|
||||
ir::condcodes::IntCC::UnsignedGreaterThanOrEqual
|
||||
};
|
||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ pub enum ExprKind {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -100,4 +101,11 @@ pub enum BinaryOp {
|
||||
Mul,
|
||||
Div,
|
||||
Rem,
|
||||
|
||||
Eq,
|
||||
Neq,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
@@ -142,6 +142,11 @@ impl<'src> Lexer<'src> {
|
||||
'/' => token!(TokenKind::Slash),
|
||||
'%' => token!(TokenKind::Percent),
|
||||
|
||||
'!' => token!(TokenKind::Bang, '=' => TokenKind::Unequal),
|
||||
'=' => token!(TokenKind::Assign, '=' => TokenKind::Equal),
|
||||
'<' => token!(TokenKind::LessThan, '=' => TokenKind::LessEqual),
|
||||
'>' => token!(TokenKind::GreaterThan, '=' => TokenKind::GreaterEqual),
|
||||
|
||||
'.' => token!(TokenKind::Dot),
|
||||
',' => token!(TokenKind::Comma),
|
||||
':' => token!(TokenKind::Colon),
|
||||
@@ -283,4 +288,21 @@ mod test {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comparison_and_logical() {
|
||||
assert_eq!(
|
||||
tokenize("== != < <= > >= ! ="),
|
||||
vec![
|
||||
Token::new(TokenKind::Equal, "==", Span::new(0, 2)),
|
||||
Token::new(TokenKind::Unequal, "!=", Span::new(3, 5)),
|
||||
Token::new(TokenKind::LessThan, "<", Span::new(6, 7)),
|
||||
Token::new(TokenKind::LessEqual, "<=", Span::new(8, 10)),
|
||||
Token::new(TokenKind::GreaterThan, ">", Span::new(11, 12)),
|
||||
Token::new(TokenKind::GreaterEqual, ">=", Span::new(13, 15)),
|
||||
Token::new(TokenKind::Bang, "!", Span::new(16, 17)),
|
||||
Token::new(TokenKind::Assign, "=", Span::new(18, 19)),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +481,7 @@ impl<'src> Parser<'src> {
|
||||
fn prefix_operator(&self, op: TokenKind) -> Option<(UnaryOp, u8)> {
|
||||
match op {
|
||||
TokenKind::Minus => Some((UnaryOp::Neg, 30)),
|
||||
TokenKind::Bang => Some((UnaryOp::Not, 30)),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
@@ -490,6 +491,13 @@ impl<'src> Parser<'src> {
|
||||
/// or `None` if the [TokenKind] is not a valid infix operator.
|
||||
fn infix_operator(&self, op: TokenKind) -> Option<(BinaryOp, u8, u8)> {
|
||||
match op {
|
||||
TokenKind::Equal => Some((BinaryOp::Eq, 5, 6)),
|
||||
TokenKind::Unequal => Some((BinaryOp::Neq, 5, 6)),
|
||||
TokenKind::LessThan => Some((BinaryOp::Lt, 5, 6)),
|
||||
TokenKind::LessEqual => Some((BinaryOp::Le, 5, 6)),
|
||||
TokenKind::GreaterThan => Some((BinaryOp::Gt, 5, 6)),
|
||||
TokenKind::GreaterEqual => Some((BinaryOp::Ge, 5, 6)),
|
||||
|
||||
TokenKind::Plus => Some((BinaryOp::Add, 10, 11)),
|
||||
TokenKind::Minus => Some((BinaryOp::Sub, 10, 11)),
|
||||
|
||||
@@ -764,4 +772,44 @@ mod test {
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comparison_expr() {
|
||||
assert_eq!(
|
||||
parse("a >= 5;", Parser::parse_expr),
|
||||
Success(Expr {
|
||||
kind: ExprKind::Binary {
|
||||
op: BinaryOp::Ge,
|
||||
lhs: Box::new(Expr {
|
||||
kind: ExprKind::Identifier {
|
||||
name: "a".to_string()
|
||||
},
|
||||
span: Span::new(0, 1)
|
||||
}),
|
||||
rhs: Box::new(Expr {
|
||||
kind: ExprKind::Integer { value: 5 },
|
||||
span: Span::new(5, 6)
|
||||
})
|
||||
},
|
||||
span: Span::new(0, 6)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logical_not_expr() {
|
||||
assert_eq!(
|
||||
parse("!true;", Parser::parse_expr),
|
||||
Success(Expr {
|
||||
kind: ExprKind::Unary {
|
||||
op: UnaryOp::Not,
|
||||
expr: Box::new(Expr {
|
||||
kind: ExprKind::Boolean { value: true },
|
||||
span: Span::new(1, 5)
|
||||
})
|
||||
},
|
||||
span: Span::new(0, 5)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+66
-2
@@ -348,6 +348,25 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
|
||||
ExprKind::Unary {
|
||||
op: UnaryOp::Not,
|
||||
expr,
|
||||
} => {
|
||||
let typed_inner = self.analyze_expr(expr);
|
||||
|
||||
if let Err(e) = self.unify(&Ty::Bool, &typed_inner.ty) {
|
||||
self.errors.push(SemanticError::new(e, expr.span));
|
||||
}
|
||||
|
||||
TypedExpr {
|
||||
kind: TypedExprKind::Unary {
|
||||
op: UnaryOp::Not,
|
||||
expr: Box::new(typed_inner),
|
||||
},
|
||||
ty: Ty::Bool,
|
||||
}
|
||||
}
|
||||
|
||||
ExprKind::Binary { op, lhs, rhs } => {
|
||||
let typed_lhs = self.analyze_expr(lhs);
|
||||
let typed_rhs = self.analyze_expr(rhs);
|
||||
@@ -356,8 +375,24 @@ impl Sema {
|
||||
self.errors.push(SemanticError::new(e, expr.span));
|
||||
}
|
||||
|
||||
let result_ty = typed_lhs.ty.clone();
|
||||
self.deferred_binary.push((expr.span, result_ty.clone()));
|
||||
let is_comparison = matches!(
|
||||
op,
|
||||
BinaryOp::Eq
|
||||
| BinaryOp::Neq
|
||||
| BinaryOp::Lt
|
||||
| BinaryOp::Le
|
||||
| BinaryOp::Gt
|
||||
| BinaryOp::Ge
|
||||
);
|
||||
|
||||
let result_ty = if is_comparison {
|
||||
Ty::Bool
|
||||
} else {
|
||||
typed_lhs.ty.clone()
|
||||
};
|
||||
|
||||
self.deferred_binary.push((expr.span, typed_lhs.ty.clone()));
|
||||
|
||||
TypedExpr {
|
||||
kind: TypedExprKind::Binary {
|
||||
op: *op,
|
||||
@@ -628,4 +663,33 @@ mod test {
|
||||
.contains("unary minus only works on integer types")
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_logical_not() {
|
||||
let src = "fn test(a: bool) -> bool { return !a; }";
|
||||
assert!(analyze(src).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_logical_not() {
|
||||
let src = "fn test(a: i32) -> bool { return !a; }";
|
||||
let errors = analyze(src).unwrap_err();
|
||||
assert!(errors.iter().any(|e| e.message.contains("type mismatch")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_comparison() {
|
||||
let src = "fn test(a: i32, b: i32) -> bool { return a <= b; }";
|
||||
assert!(analyze(src).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_comparison() {
|
||||
let src = "fn test(a: bool, b: bool) -> bool { return a == b; }";
|
||||
let errors = analyze(src).unwrap_err();
|
||||
assert!(errors.iter().any(|e| {
|
||||
e.message
|
||||
.contains("binary operators only work on integer types")
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ pub enum TokenKind {
|
||||
Slash,
|
||||
Percent,
|
||||
|
||||
Equal,
|
||||
Unequal,
|
||||
LessThan,
|
||||
LessEqual,
|
||||
GreaterThan,
|
||||
GreaterEqual,
|
||||
|
||||
Assign,
|
||||
Bang,
|
||||
|
||||
// Punctuation
|
||||
Dot,
|
||||
Comma,
|
||||
@@ -109,6 +119,14 @@ impl Display for TokenKind {
|
||||
TokenKind::Star => "`*`",
|
||||
TokenKind::Slash => "`/`",
|
||||
TokenKind::Percent => "`%`",
|
||||
TokenKind::Equal => "`==`",
|
||||
TokenKind::Unequal => "`!=`",
|
||||
TokenKind::LessThan => "`<`",
|
||||
TokenKind::LessEqual => "`<=`",
|
||||
TokenKind::GreaterThan => "`>`",
|
||||
TokenKind::GreaterEqual => "`>=`",
|
||||
TokenKind::Assign => "`=`",
|
||||
TokenKind::Bang => "`!`",
|
||||
TokenKind::Dot => "`.`",
|
||||
TokenKind::Comma => "`,`",
|
||||
TokenKind::Colon => "`:`",
|
||||
|
||||
Reference in New Issue
Block a user