feat: add floats
This commit is contained in:
+97
-11
@@ -158,6 +158,8 @@ impl CraneliftBackend {
|
|||||||
Ty::I16 | Ty::U16 => types::I16,
|
Ty::I16 | Ty::U16 => types::I16,
|
||||||
Ty::I32 | Ty::U32 => types::I32,
|
Ty::I32 | Ty::U32 => types::I32,
|
||||||
Ty::I64 | Ty::U64 => types::I64,
|
Ty::I64 | Ty::U64 => types::I64,
|
||||||
|
Ty::F32 => types::F32,
|
||||||
|
Ty::F64 => types::F64,
|
||||||
Ty::Bool => types::I8, // Booleans are represented as 8-bit integers
|
Ty::Bool => types::I8, // Booleans are represented as 8-bit integers
|
||||||
_ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty),
|
_ => unimplemented!("Unsupported type for Cranelift lowering: {:?}", ty),
|
||||||
}
|
}
|
||||||
@@ -221,6 +223,7 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
match op {
|
match op {
|
||||||
Operand::Copy(local_id) => self.locals[local_id.0].ty.clone(),
|
Operand::Copy(local_id) => self.locals[local_id.0].ty.clone(),
|
||||||
Operand::Constant(ConstantValue::Integer(_, ty)) => ty.clone(),
|
Operand::Constant(ConstantValue::Integer(_, ty)) => ty.clone(),
|
||||||
|
Operand::Constant(ConstantValue::Float(_, ty)) => ty.clone(),
|
||||||
Operand::Constant(ConstantValue::Boolean(_)) => Ty::Bool,
|
Operand::Constant(ConstantValue::Boolean(_)) => Ty::Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,6 +238,11 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
let cl_ty = CraneliftBackend::lower_type(ty);
|
let cl_ty = CraneliftBackend::lower_type(ty);
|
||||||
self.builder.ins().iconst(cl_ty, *val as i64)
|
self.builder.ins().iconst(cl_ty, *val as i64)
|
||||||
}
|
}
|
||||||
|
Operand::Constant(ConstantValue::Float(val, ty)) => match ty {
|
||||||
|
Ty::F32 => self.builder.ins().f32const(*val as f32),
|
||||||
|
Ty::F64 => self.builder.ins().f64const(*val),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
Operand::Constant(ConstantValue::Boolean(val)) => self
|
Operand::Constant(ConstantValue::Boolean(val)) => self
|
||||||
.builder
|
.builder
|
||||||
.ins()
|
.ins()
|
||||||
@@ -247,10 +255,17 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
Rvalue::Use(op) => self.translate_operand(op),
|
Rvalue::Use(op) => self.translate_operand(op),
|
||||||
Rvalue::UnaryOp(op, inner) => {
|
Rvalue::UnaryOp(op, inner) => {
|
||||||
let inner_val = self.translate_operand(inner);
|
let inner_val = self.translate_operand(inner);
|
||||||
match op {
|
|
||||||
UnaryOp::Neg => self.builder.ins().ineg(inner_val),
|
|
||||||
UnaryOp::Not => {
|
|
||||||
let ty = self.get_operand_type(inner);
|
let ty = self.get_operand_type(inner);
|
||||||
|
|
||||||
|
match op {
|
||||||
|
UnaryOp::Neg => {
|
||||||
|
if ty.is_float() {
|
||||||
|
self.builder.ins().fneg(inner_val)
|
||||||
|
} else {
|
||||||
|
self.builder.ins().ineg(inner_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnaryOp::Not => {
|
||||||
let cl_ty = CraneliftBackend::lower_type(&ty);
|
let cl_ty = CraneliftBackend::lower_type(&ty);
|
||||||
let zero = self.builder.ins().iconst(cl_ty, 0);
|
let zero = self.builder.ins().iconst(cl_ty, 0);
|
||||||
self.builder
|
self.builder
|
||||||
@@ -265,36 +280,82 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
|
|
||||||
let ty = self.get_operand_type(lhs);
|
let ty = self.get_operand_type(lhs);
|
||||||
let is_signed = matches!(ty, Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64);
|
let is_signed = matches!(ty, Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64);
|
||||||
|
let is_float = ty.is_float();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
BinaryOp::Add => self.builder.ins().iadd(lhs_val, rhs_val),
|
BinaryOp::Add => {
|
||||||
BinaryOp::Sub => self.builder.ins().isub(lhs_val, rhs_val),
|
if is_float {
|
||||||
BinaryOp::Mul => self.builder.ins().imul(lhs_val, rhs_val),
|
self.builder.ins().fadd(lhs_val, rhs_val)
|
||||||
|
} else {
|
||||||
|
self.builder.ins().iadd(lhs_val, rhs_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BinaryOp::Sub => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fsub(lhs_val, rhs_val)
|
||||||
|
} else {
|
||||||
|
self.builder.ins().isub(lhs_val, rhs_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BinaryOp::Mul => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fmul(lhs_val, rhs_val)
|
||||||
|
} else {
|
||||||
|
self.builder.ins().imul(lhs_val, rhs_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
BinaryOp::Div => {
|
BinaryOp::Div => {
|
||||||
if is_signed {
|
if is_float {
|
||||||
|
self.builder.ins().fdiv(lhs_val, rhs_val)
|
||||||
|
} else if is_signed {
|
||||||
self.builder.ins().sdiv(lhs_val, rhs_val)
|
self.builder.ins().sdiv(lhs_val, rhs_val)
|
||||||
} else {
|
} else {
|
||||||
self.builder.ins().udiv(lhs_val, rhs_val)
|
self.builder.ins().udiv(lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BinaryOp::Rem => {
|
BinaryOp::Rem => {
|
||||||
if is_signed {
|
if is_float {
|
||||||
|
unimplemented!("float remainder is not natively supported in Cranelift")
|
||||||
|
} else if is_signed {
|
||||||
self.builder.ins().srem(lhs_val, rhs_val)
|
self.builder.ins().srem(lhs_val, rhs_val)
|
||||||
} else {
|
} else {
|
||||||
self.builder.ins().urem(lhs_val, rhs_val)
|
self.builder.ins().urem(lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BinaryOp::Eq => {
|
BinaryOp::Eq => {
|
||||||
|
if is_float {
|
||||||
|
self.builder
|
||||||
|
.ins()
|
||||||
|
.fcmp(ir::condcodes::FloatCC::Equal, lhs_val, rhs_val)
|
||||||
|
} else {
|
||||||
self.builder
|
self.builder
|
||||||
.ins()
|
.ins()
|
||||||
.icmp(ir::condcodes::IntCC::Equal, lhs_val, rhs_val)
|
.icmp(ir::condcodes::IntCC::Equal, lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BinaryOp::Neq => {
|
BinaryOp::Neq => {
|
||||||
self.builder
|
if is_float {
|
||||||
.ins()
|
self.builder.ins().fcmp(
|
||||||
.icmp(ir::condcodes::IntCC::NotEqual, lhs_val, rhs_val)
|
ir::condcodes::FloatCC::NotEqual,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.builder.ins().icmp(
|
||||||
|
ir::condcodes::IntCC::NotEqual,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BinaryOp::Lt => {
|
BinaryOp::Lt => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fcmp(
|
||||||
|
ir::condcodes::FloatCC::LessThan,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
let cc = if is_signed {
|
let cc = if is_signed {
|
||||||
ir::condcodes::IntCC::SignedLessThan
|
ir::condcodes::IntCC::SignedLessThan
|
||||||
} else {
|
} else {
|
||||||
@@ -302,7 +363,15 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
};
|
};
|
||||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BinaryOp::Le => {
|
BinaryOp::Le => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fcmp(
|
||||||
|
ir::condcodes::FloatCC::LessThanOrEqual,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
let cc = if is_signed {
|
let cc = if is_signed {
|
||||||
ir::condcodes::IntCC::SignedLessThanOrEqual
|
ir::condcodes::IntCC::SignedLessThanOrEqual
|
||||||
} else {
|
} else {
|
||||||
@@ -310,7 +379,15 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
};
|
};
|
||||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BinaryOp::Gt => {
|
BinaryOp::Gt => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fcmp(
|
||||||
|
ir::condcodes::FloatCC::GreaterThan,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
let cc = if is_signed {
|
let cc = if is_signed {
|
||||||
ir::condcodes::IntCC::SignedGreaterThan
|
ir::condcodes::IntCC::SignedGreaterThan
|
||||||
} else {
|
} else {
|
||||||
@@ -318,7 +395,15 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
};
|
};
|
||||||
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
self.builder.ins().icmp(cc, lhs_val, rhs_val)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BinaryOp::Ge => {
|
BinaryOp::Ge => {
|
||||||
|
if is_float {
|
||||||
|
self.builder.ins().fcmp(
|
||||||
|
ir::condcodes::FloatCC::GreaterThanOrEqual,
|
||||||
|
lhs_val,
|
||||||
|
rhs_val,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
let cc = if is_signed {
|
let cc = if is_signed {
|
||||||
ir::condcodes::IntCC::SignedGreaterThanOrEqual
|
ir::condcodes::IntCC::SignedGreaterThanOrEqual
|
||||||
} else {
|
} else {
|
||||||
@@ -331,3 +416,4 @@ impl<'a> FunctionTranslator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-7
@@ -34,18 +34,18 @@ pub type TypedStmtKind = StmtKind<Typed>;
|
|||||||
pub type TypedExpr = Expr<Typed>;
|
pub type TypedExpr = Expr<Typed>;
|
||||||
pub type TypedExprKind = ExprKind<Typed>;
|
pub type TypedExprKind = ExprKind<Typed>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Module<P: Phase = Untyped> {
|
pub struct Module<P: Phase = Untyped> {
|
||||||
pub decls: Vec<Decl<P>>,
|
pub decls: Vec<Decl<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Decl<P: Phase = Untyped> {
|
pub struct Decl<P: Phase = Untyped> {
|
||||||
pub kind: DeclKind<P>,
|
pub kind: DeclKind<P>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DeclKind<P: Phase = Untyped> {
|
pub enum DeclKind<P: Phase = Untyped> {
|
||||||
Function {
|
Function {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -79,16 +79,18 @@ pub enum TypeKind {
|
|||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
U64,
|
U64,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
Bool,
|
Bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Stmt<P: Phase = Untyped> {
|
pub struct Stmt<P: Phase = Untyped> {
|
||||||
pub kind: StmtKind<P>,
|
pub kind: StmtKind<P>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum StmtKind<P: Phase = Untyped> {
|
pub enum StmtKind<P: Phase = Untyped> {
|
||||||
Compound {
|
Compound {
|
||||||
inner: Vec<Stmt<P>>,
|
inner: Vec<Stmt<P>>,
|
||||||
@@ -118,14 +120,14 @@ pub enum StmtKind<P: Phase = Untyped> {
|
|||||||
Continue,
|
Continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Expr<P: Phase = Untyped> {
|
pub struct Expr<P: Phase = Untyped> {
|
||||||
pub kind: ExprKind<P>,
|
pub kind: ExprKind<P>,
|
||||||
pub ty: P::ExprType,
|
pub ty: P::ExprType,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ExprKind<P: Phase = Untyped> {
|
pub enum ExprKind<P: Phase = Untyped> {
|
||||||
Identifier {
|
Identifier {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -133,6 +135,9 @@ pub enum ExprKind<P: Phase = Untyped> {
|
|||||||
Integer {
|
Integer {
|
||||||
value: u64,
|
value: u64,
|
||||||
},
|
},
|
||||||
|
Float {
|
||||||
|
value: f64,
|
||||||
|
},
|
||||||
Boolean {
|
Boolean {
|
||||||
value: bool,
|
value: bool,
|
||||||
},
|
},
|
||||||
|
|||||||
+57
-2
@@ -80,6 +80,8 @@ impl<'src> Lexer<'src> {
|
|||||||
"u16" => TokenKind::U16,
|
"u16" => TokenKind::U16,
|
||||||
"u32" => TokenKind::U32,
|
"u32" => TokenKind::U32,
|
||||||
"u64" => TokenKind::U64,
|
"u64" => TokenKind::U64,
|
||||||
|
"f32" => TokenKind::F32,
|
||||||
|
"f64" => TokenKind::F64,
|
||||||
"bool" => TokenKind::Bool,
|
"bool" => TokenKind::Bool,
|
||||||
|
|
||||||
"true" | "false" => TokenKind::BooleanLit,
|
"true" | "false" => TokenKind::BooleanLit,
|
||||||
@@ -108,6 +110,43 @@ impl<'src> Lexer<'src> {
|
|||||||
|
|
||||||
self.advance_while(|ch| ch.is_digit(radix));
|
self.advance_while(|ch| ch.is_digit(radix));
|
||||||
|
|
||||||
|
// Handle floating point literals (fraction and optional exponent)
|
||||||
|
if radix == 10 {
|
||||||
|
let mut is_float = false;
|
||||||
|
|
||||||
|
if self.peek() == Some('.') {
|
||||||
|
let mut chars = self.source[self.cursor..].chars();
|
||||||
|
chars.next(); // consume '.'
|
||||||
|
if chars.next().is_some_and(|ch| ch.is_ascii_digit()) {
|
||||||
|
self.advance(); // consume '.'
|
||||||
|
self.advance_while(|ch| ch.is_ascii_digit());
|
||||||
|
is_float = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(self.peek(), Some('e' | 'E')) {
|
||||||
|
let mut chars = self.source[self.cursor..].chars();
|
||||||
|
chars.next(); // consume 'e'
|
||||||
|
let next_char = chars.next();
|
||||||
|
|
||||||
|
if next_char.is_some_and(|ch| ch.is_ascii_digit())
|
||||||
|
|| (matches!(next_char, Some('+' | '-'))
|
||||||
|
&& chars.next().is_some_and(|ch| ch.is_ascii_digit()))
|
||||||
|
{
|
||||||
|
self.advance(); // consume 'e'
|
||||||
|
if matches!(self.peek(), Some('+' | '-')) {
|
||||||
|
self.advance(); // consume sign
|
||||||
|
}
|
||||||
|
self.advance_while(|ch| ch.is_ascii_digit());
|
||||||
|
is_float = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_float {
|
||||||
|
return TokenKind::FloatLit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TokenKind::IntegerLit
|
TokenKind::IntegerLit
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +279,20 @@ mod test {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_literals() {
|
||||||
|
assert_eq!(
|
||||||
|
tokenize("3.14 0.001 42.0 1e5 2.5e-3"),
|
||||||
|
vec![
|
||||||
|
Token::new(TokenKind::FloatLit, "3.14", Span::new(0, 4)),
|
||||||
|
Token::new(TokenKind::FloatLit, "0.001", Span::new(5, 10)),
|
||||||
|
Token::new(TokenKind::FloatLit, "42.0", Span::new(11, 15)),
|
||||||
|
Token::new(TokenKind::FloatLit, "1e5", Span::new(16, 19)),
|
||||||
|
Token::new(TokenKind::FloatLit, "2.5e-3", Span::new(20, 26)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn boolean_literals() {
|
fn boolean_literals() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -254,7 +307,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn types() {
|
fn types() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tokenize("i8 i16 i32 i64 u8 u16 u32 u64 bool"),
|
tokenize("i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 bool"),
|
||||||
vec![
|
vec![
|
||||||
Token::new(TokenKind::I8, "i8", Span::new(0, 2)),
|
Token::new(TokenKind::I8, "i8", Span::new(0, 2)),
|
||||||
Token::new(TokenKind::I16, "i16", Span::new(3, 6)),
|
Token::new(TokenKind::I16, "i16", Span::new(3, 6)),
|
||||||
@@ -264,7 +317,9 @@ mod test {
|
|||||||
Token::new(TokenKind::U16, "u16", Span::new(18, 21)),
|
Token::new(TokenKind::U16, "u16", Span::new(18, 21)),
|
||||||
Token::new(TokenKind::U32, "u32", Span::new(22, 25)),
|
Token::new(TokenKind::U32, "u32", Span::new(22, 25)),
|
||||||
Token::new(TokenKind::U64, "u64", Span::new(26, 29)),
|
Token::new(TokenKind::U64, "u64", Span::new(26, 29)),
|
||||||
Token::new(TokenKind::Bool, "bool", Span::new(30, 34)),
|
Token::new(TokenKind::F32, "f32", Span::new(30, 33)),
|
||||||
|
Token::new(TokenKind::F64, "f64", Span::new(34, 37)),
|
||||||
|
Token::new(TokenKind::Bool, "bool", Span::new(38, 42)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-3
@@ -223,6 +223,7 @@ impl<'src> Parser<'src> {
|
|||||||
/// ```ebnf
|
/// ```ebnf
|
||||||
/// type = "i8" | "i16" | "i32" | "i64"
|
/// type = "i8" | "i16" | "i32" | "i64"
|
||||||
/// | "u8" | "u16" | "u32" | "u64"
|
/// | "u8" | "u16" | "u32" | "u64"
|
||||||
|
/// | "f32" | "f64"
|
||||||
/// | "bool" ;
|
/// | "bool" ;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_type(&mut self) -> ParseResult<Type> {
|
pub fn parse_type(&mut self) -> ParseResult<Type> {
|
||||||
@@ -261,6 +262,14 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance();
|
self.advance();
|
||||||
TypeKind::U64
|
TypeKind::U64
|
||||||
}
|
}
|
||||||
|
TokenKind::F32 => {
|
||||||
|
self.advance();
|
||||||
|
TypeKind::F32
|
||||||
|
}
|
||||||
|
TokenKind::F64 => {
|
||||||
|
self.advance();
|
||||||
|
TypeKind::F64
|
||||||
|
}
|
||||||
TokenKind::Bool => {
|
TokenKind::Bool => {
|
||||||
self.advance();
|
self.advance();
|
||||||
TypeKind::Bool
|
TypeKind::Bool
|
||||||
@@ -617,6 +626,17 @@ impl<'src> Parser<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenKind::FloatLit => {
|
||||||
|
let token = self.advance().unwrap();
|
||||||
|
let value = token.text.parse().unwrap();
|
||||||
|
|
||||||
|
Ok(Expr {
|
||||||
|
kind: ExprKind::Float { value },
|
||||||
|
ty: (),
|
||||||
|
span: token.span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
TokenKind::BooleanLit => {
|
TokenKind::BooleanLit => {
|
||||||
let token = self.advance().unwrap();
|
let token = self.advance().unwrap();
|
||||||
|
|
||||||
@@ -706,8 +726,8 @@ mod test {
|
|||||||
token::Span,
|
token::Span,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum TestResult<T: Debug + Eq + PartialEq> {
|
enum TestResult<T: Debug + PartialEq> {
|
||||||
Success(T),
|
Success(T),
|
||||||
Recovered(T, Vec<ParseError>),
|
Recovered(T, Vec<ParseError>),
|
||||||
Error(Vec<ParseError>),
|
Error(Vec<ParseError>),
|
||||||
@@ -715,7 +735,7 @@ mod test {
|
|||||||
|
|
||||||
use TestResult::*;
|
use TestResult::*;
|
||||||
|
|
||||||
fn parse<'src, T: Debug + Eq + PartialEq>(
|
fn parse<'src, T: Debug + PartialEq>(
|
||||||
source: &'src str,
|
source: &'src str,
|
||||||
method: impl Fn(&mut Parser<'src>) -> ParseResult<T>,
|
method: impl Fn(&mut Parser<'src>) -> ParseResult<T>,
|
||||||
) -> TestResult<T> {
|
) -> TestResult<T> {
|
||||||
@@ -775,6 +795,18 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_literals() {
|
||||||
|
assert_eq!(
|
||||||
|
parse("3.14;", Parser::parse_expr),
|
||||||
|
Success(Expr {
|
||||||
|
kind: ExprKind::Float { value: 3.14 },
|
||||||
|
ty: (),
|
||||||
|
span: Span::new(0, 4)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn boolean_literals() {
|
fn boolean_literals() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -969,6 +1001,29 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_stmt_float() {
|
||||||
|
assert_eq!(
|
||||||
|
parse("let a: f32 = 3.14;", Parser::parse_stmt),
|
||||||
|
Success(Stmt {
|
||||||
|
kind: StmtKind::Let {
|
||||||
|
name: "a".to_string(),
|
||||||
|
name_span: Span::new(4, 5),
|
||||||
|
ty: Some(Type {
|
||||||
|
kind: TypeKind::F32,
|
||||||
|
span: Span::new(7, 10),
|
||||||
|
}),
|
||||||
|
initializer: Some(Expr {
|
||||||
|
kind: ExprKind::Float { value: 3.14 },
|
||||||
|
ty: (),
|
||||||
|
span: Span::new(13, 17),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
span: Span::new(0, 18)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_stmt() {
|
fn while_stmt() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
+98
-21
@@ -35,6 +35,8 @@ pub enum Ty {
|
|||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
U64,
|
U64,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
Bool,
|
Bool,
|
||||||
Unit,
|
Unit,
|
||||||
Var(usize),
|
Var(usize),
|
||||||
@@ -49,6 +51,16 @@ impl Ty {
|
|||||||
Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64 | Ty::U8 | Ty::U16 | Ty::U32 | Ty::U64
|
Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64 | Ty::U8 | Ty::U16 | Ty::U32 | Ty::U64
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the type is a fundamental floating point type.
|
||||||
|
pub fn is_float(&self) -> bool {
|
||||||
|
matches!(self, Ty::F32 | Ty::F64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the type is numeric (integer or float).
|
||||||
|
pub fn is_numeric(&self) -> bool {
|
||||||
|
self.is_integer() || self.is_float()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TypeKind> for Ty {
|
impl From<&TypeKind> for Ty {
|
||||||
@@ -62,6 +74,8 @@ impl From<&TypeKind> for Ty {
|
|||||||
TypeKind::U16 => Ty::U16,
|
TypeKind::U16 => Ty::U16,
|
||||||
TypeKind::U32 => Ty::U32,
|
TypeKind::U32 => Ty::U32,
|
||||||
TypeKind::U64 => Ty::U64,
|
TypeKind::U64 => Ty::U64,
|
||||||
|
TypeKind::F32 => Ty::F32,
|
||||||
|
TypeKind::F64 => Ty::F64,
|
||||||
TypeKind::Bool => Ty::Bool,
|
TypeKind::Bool => Ty::Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +91,8 @@ pub struct Sema {
|
|||||||
errors: Vec<SemanticError>,
|
errors: Vec<SemanticError>,
|
||||||
deferred_unary_neg: Vec<(Span, Ty, Ty, Option<u64>)>,
|
deferred_unary_neg: Vec<(Span, Ty, Ty, Option<u64>)>,
|
||||||
deferred_binary: Vec<(Span, Ty)>,
|
deferred_binary: Vec<(Span, Ty)>,
|
||||||
deferred_literals: Vec<(Span, Ty)>,
|
deferred_int_literals: Vec<(Span, Ty)>,
|
||||||
|
deferred_float_literals: Vec<(Span, Ty)>,
|
||||||
loop_depth: usize,
|
loop_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +106,8 @@ impl Sema {
|
|||||||
errors: Vec::new(),
|
errors: Vec::new(),
|
||||||
deferred_unary_neg: Vec::new(),
|
deferred_unary_neg: Vec::new(),
|
||||||
deferred_binary: Vec::new(),
|
deferred_binary: Vec::new(),
|
||||||
deferred_literals: Vec::new(),
|
deferred_int_literals: Vec::new(),
|
||||||
|
deferred_float_literals: Vec::new(),
|
||||||
loop_depth: 0,
|
loop_depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,7 +485,7 @@ impl Sema {
|
|||||||
|
|
||||||
ExprKind::Integer { value } => {
|
ExprKind::Integer { value } => {
|
||||||
let ty = self.new_var();
|
let ty = self.new_var();
|
||||||
self.deferred_literals.push((expr.span, ty.clone()));
|
self.deferred_int_literals.push((expr.span, ty.clone()));
|
||||||
|
|
||||||
TypedExpr {
|
TypedExpr {
|
||||||
kind: TypedExprKind::Integer { value: *value },
|
kind: TypedExprKind::Integer { value: *value },
|
||||||
@@ -478,6 +494,17 @@ impl Sema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExprKind::Float { value } => {
|
||||||
|
let ty = self.new_var();
|
||||||
|
self.deferred_float_literals.push((expr.span, ty.clone()));
|
||||||
|
|
||||||
|
TypedExpr {
|
||||||
|
kind: TypedExprKind::Float { value: *value },
|
||||||
|
ty,
|
||||||
|
span: expr.span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExprKind::Boolean { value } => TypedExpr {
|
ExprKind::Boolean { value } => TypedExpr {
|
||||||
kind: TypedExprKind::Boolean { value: *value },
|
kind: TypedExprKind::Boolean { value: *value },
|
||||||
ty: Ty::Bool,
|
ty: Ty::Bool,
|
||||||
@@ -685,6 +712,7 @@ impl Sema {
|
|||||||
let kind = match expr.kind {
|
let kind = match expr.kind {
|
||||||
TypedExprKind::Identifier { name } => TypedExprKind::Identifier { name },
|
TypedExprKind::Identifier { name } => TypedExprKind::Identifier { name },
|
||||||
TypedExprKind::Integer { value } => TypedExprKind::Integer { value },
|
TypedExprKind::Integer { value } => TypedExprKind::Integer { value },
|
||||||
|
TypedExprKind::Float { value } => TypedExprKind::Float { value },
|
||||||
TypedExprKind::Boolean { value } => TypedExprKind::Boolean { value },
|
TypedExprKind::Boolean { value } => TypedExprKind::Boolean { value },
|
||||||
|
|
||||||
TypedExprKind::Unary { op, expr } => TypedExprKind::Unary {
|
TypedExprKind::Unary { op, expr } => TypedExprKind::Unary {
|
||||||
@@ -715,7 +743,7 @@ impl Sema {
|
|||||||
let result_resolved = self.apply_subst(&result_ty);
|
let result_resolved = self.apply_subst(&result_ty);
|
||||||
|
|
||||||
let inner_final = if let Ty::Var(_) = inner_resolved {
|
let inner_final = if let Ty::Var(_) = inner_resolved {
|
||||||
if result_resolved.is_integer() {
|
if result_resolved.is_numeric() {
|
||||||
let _ = self.unify(&inner_resolved, &result_resolved);
|
let _ = self.unify(&inner_resolved, &result_resolved);
|
||||||
result_resolved.clone()
|
result_resolved.clone()
|
||||||
} else {
|
} else {
|
||||||
@@ -733,7 +761,7 @@ impl Sema {
|
|||||||
// If the value is known at compile time, we can avoid promoting to a larger size
|
// If the value is known at compile time, we can avoid promoting to a larger size
|
||||||
// as long as the exact value fits within the signed equivalent of the same size.
|
// as long as the exact value fits within the signed equivalent of the same size.
|
||||||
let promoted_ty = match inner_final {
|
let promoted_ty = match inner_final {
|
||||||
Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64 => Ok(inner_final),
|
Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64 | Ty::F32 | Ty::F64 => Ok(inner_final),
|
||||||
|
|
||||||
Ty::U8 => Ok(if known_value.is_some_and(|v| v <= i8::MAX as u64) {
|
Ty::U8 => Ok(if known_value.is_some_and(|v| v <= i8::MAX as u64) {
|
||||||
Ty::I8
|
Ty::I8
|
||||||
@@ -762,7 +790,7 @@ impl Sema {
|
|||||||
Err("cannot promote u64 to a larger signed type")
|
Err("cannot promote u64 to a larger signed type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err("unary minus only works on integer types"),
|
_ => Err("unary minus only works on numeric types"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match promoted_ty {
|
match promoted_ty {
|
||||||
@@ -788,15 +816,15 @@ impl Sema {
|
|||||||
resolved
|
resolved
|
||||||
};
|
};
|
||||||
|
|
||||||
if !final_ty.is_integer() {
|
if !final_ty.is_numeric() {
|
||||||
self.errors.push(SemanticError::new(
|
self.errors.push(SemanticError::new(
|
||||||
"binary operators only work on integer types",
|
"binary operators only work on numeric types",
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (span, ty) in std::mem::take(&mut self.deferred_literals) {
|
for (span, ty) in std::mem::take(&mut self.deferred_int_literals) {
|
||||||
let resolved = self.apply_subst(&ty);
|
let resolved = self.apply_subst(&ty);
|
||||||
|
|
||||||
let final_ty = if let Ty::Var(_) = resolved {
|
let final_ty = if let Ty::Var(_) = resolved {
|
||||||
@@ -814,6 +842,23 @@ impl Sema {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (span, ty) in std::mem::take(&mut self.deferred_float_literals) {
|
||||||
|
let resolved = self.apply_subst(&ty);
|
||||||
|
|
||||||
|
let final_ty = if let Ty::Var(_) = resolved {
|
||||||
|
let default = Ty::F32;
|
||||||
|
let _ = self.unify(&resolved, &default);
|
||||||
|
default
|
||||||
|
} else {
|
||||||
|
resolved
|
||||||
|
};
|
||||||
|
|
||||||
|
if !final_ty.is_float() {
|
||||||
|
self.errors
|
||||||
|
.push(SemanticError::new("expected float type for literal", span));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,6 +912,48 @@ mod test {
|
|||||||
assert!(errors[0].message.contains("undeclared identifier `x`"));
|
assert!(errors[0].message.contains("undeclared identifier `x`"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_float_ops() {
|
||||||
|
let src = "fn test(a: f32, b: f32) -> f32 { return a + b - a * b / a; }";
|
||||||
|
assert!(analyze(src).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_float_comparison() {
|
||||||
|
let src = "fn test(a: f64, b: f64) -> bool { return a <= b; }";
|
||||||
|
assert!(analyze(src).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_float_unary_neg() {
|
||||||
|
let src = "fn test(a: f64) -> f64 { return -a; }";
|
||||||
|
assert!(analyze(src).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_type_mismatch() {
|
||||||
|
let src = "fn test(a: f32, b: i32) -> f32 { return a + b; }";
|
||||||
|
let errors = analyze(src).unwrap_err();
|
||||||
|
assert!(errors.iter().any(|e| e.message.contains("type mismatch")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_literal_inference() {
|
||||||
|
let src = "fn test() { let a = 3.14; }";
|
||||||
|
assert!(analyze(src).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_literal_invalid_type() {
|
||||||
|
let src = "fn test() { let a: i32 = 3.14; }";
|
||||||
|
let errors = analyze(src).unwrap_err();
|
||||||
|
assert!(
|
||||||
|
errors
|
||||||
|
.iter()
|
||||||
|
.any(|e| e.message.contains("expected float type for literal"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn binary_op_non_integer() {
|
fn binary_op_non_integer() {
|
||||||
let src = "fn bad_bin() -> bool { return true + false; }";
|
let src = "fn bad_bin() -> bool { return true + false; }";
|
||||||
@@ -874,7 +961,7 @@ mod test {
|
|||||||
|
|
||||||
assert!(errors.iter().any(|e| {
|
assert!(errors.iter().any(|e| {
|
||||||
e.message
|
e.message
|
||||||
.contains("binary operators only work on integer types")
|
.contains("binary operators only work on numeric types")
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,7 +985,7 @@ mod test {
|
|||||||
|
|
||||||
assert!(errors.iter().any(|e| {
|
assert!(errors.iter().any(|e| {
|
||||||
e.message
|
e.message
|
||||||
.contains("unary minus only works on integer types")
|
.contains("unary minus only works on numeric types")
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -921,16 +1008,6 @@ mod test {
|
|||||||
assert!(analyze(src).is_ok());
|
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")
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_if() {
|
fn valid_if() {
|
||||||
let src = "fn test() { if true { return; } }";
|
let src = "fn test() { if true { return; } }";
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ pub enum TokenKind {
|
|||||||
Identifier,
|
Identifier,
|
||||||
IntegerLit,
|
IntegerLit,
|
||||||
BooleanLit,
|
BooleanLit,
|
||||||
|
FloatLit,
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
Fn,
|
Fn,
|
||||||
@@ -73,6 +74,8 @@ pub enum TokenKind {
|
|||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
U64,
|
U64,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
Bool,
|
Bool,
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
@@ -115,6 +118,7 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Identifier => "an identifier",
|
TokenKind::Identifier => "an identifier",
|
||||||
TokenKind::IntegerLit => "an integer",
|
TokenKind::IntegerLit => "an integer",
|
||||||
TokenKind::BooleanLit => "a boolean",
|
TokenKind::BooleanLit => "a boolean",
|
||||||
|
TokenKind::FloatLit => "a float",
|
||||||
TokenKind::Fn => "`fn`",
|
TokenKind::Fn => "`fn`",
|
||||||
TokenKind::If => "`if`",
|
TokenKind::If => "`if`",
|
||||||
TokenKind::Else => "`else`",
|
TokenKind::Else => "`else`",
|
||||||
@@ -131,6 +135,8 @@ impl Display for TokenKind {
|
|||||||
TokenKind::U16 => "`u16`",
|
TokenKind::U16 => "`u16`",
|
||||||
TokenKind::U32 => "`u32`",
|
TokenKind::U32 => "`u32`",
|
||||||
TokenKind::U64 => "`u64`",
|
TokenKind::U64 => "`u64`",
|
||||||
|
TokenKind::F32 => "`f32`",
|
||||||
|
TokenKind::F64 => "`f64`",
|
||||||
TokenKind::Bool => "`bool`",
|
TokenKind::Bool => "`bool`",
|
||||||
TokenKind::Plus => "`+`",
|
TokenKind::Plus => "`+`",
|
||||||
TokenKind::Minus => "`-`",
|
TokenKind::Minus => "`-`",
|
||||||
|
|||||||
@@ -343,6 +343,9 @@ impl FuncBuilder {
|
|||||||
TypedExprKind::Integer { value } => {
|
TypedExprKind::Integer { value } => {
|
||||||
Operand::Constant(ConstantValue::Integer(*value, expr.ty.clone()))
|
Operand::Constant(ConstantValue::Integer(*value, expr.ty.clone()))
|
||||||
}
|
}
|
||||||
|
TypedExprKind::Float { value } => {
|
||||||
|
Operand::Constant(ConstantValue::Float(*value, expr.ty.clone()))
|
||||||
|
}
|
||||||
TypedExprKind::Boolean { value } => Operand::Constant(ConstantValue::Boolean(*value)),
|
TypedExprKind::Boolean { value } => Operand::Constant(ConstantValue::Boolean(*value)),
|
||||||
TypedExprKind::Unary { op, expr: inner } => {
|
TypedExprKind::Unary { op, expr: inner } => {
|
||||||
let inner_op = self.lower_expr(inner);
|
let inner_op = self.lower_expr(inner);
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ fn evaluate_unary(op: UnaryOp, val: &ConstantValue) -> Option<ConstantValue> {
|
|||||||
(UnaryOp::Neg, ConstantValue::Integer(v, ty)) => {
|
(UnaryOp::Neg, ConstantValue::Integer(v, ty)) => {
|
||||||
Some(ConstantValue::Integer(v.wrapping_neg(), ty.clone()))
|
Some(ConstantValue::Integer(v.wrapping_neg(), ty.clone()))
|
||||||
}
|
}
|
||||||
|
(UnaryOp::Neg, ConstantValue::Float(v, ty)) => Some(ConstantValue::Float(-v, ty.clone())),
|
||||||
(UnaryOp::Not, ConstantValue::Boolean(b)) => Some(ConstantValue::Boolean(!b)),
|
(UnaryOp::Not, ConstantValue::Boolean(b)) => Some(ConstantValue::Boolean(!b)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -165,6 +166,20 @@ fn evaluate_binary(
|
|||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
(ConstantValue::Float(l, ty), ConstantValue::Float(r, _)) => match op {
|
||||||
|
BinaryOp::Add => Some(ConstantValue::Float(l + r, ty.clone())),
|
||||||
|
BinaryOp::Sub => Some(ConstantValue::Float(l - r, ty.clone())),
|
||||||
|
BinaryOp::Mul => Some(ConstantValue::Float(l * r, ty.clone())),
|
||||||
|
BinaryOp::Div => Some(ConstantValue::Float(l / r, ty.clone())),
|
||||||
|
BinaryOp::Rem => Some(ConstantValue::Float(l % r, ty.clone())),
|
||||||
|
BinaryOp::Eq => Some(ConstantValue::Boolean(l == r)),
|
||||||
|
BinaryOp::Neq => Some(ConstantValue::Boolean(l != r)),
|
||||||
|
BinaryOp::Lt => Some(ConstantValue::Boolean(l < r)),
|
||||||
|
BinaryOp::Le => Some(ConstantValue::Boolean(l <= r)),
|
||||||
|
BinaryOp::Gt => Some(ConstantValue::Boolean(l > r)),
|
||||||
|
BinaryOp::Ge => Some(ConstantValue::Boolean(l >= r)),
|
||||||
|
},
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ pub enum Operand {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ConstantValue {
|
pub enum ConstantValue {
|
||||||
Integer(u64, Ty),
|
Integer(u64, Ty),
|
||||||
|
Float(f64, Ty),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[code]
|
||||||
|
fn is_less_f64(x: f64, y: f64) -> bool {
|
||||||
|
return x < y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_greater_eq_f32(x: f32, y: f32) -> bool {
|
||||||
|
return x >= y;
|
||||||
|
}
|
||||||
|
|
||||||
|
[harness]
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern bool is_less_f64(double x, double y);
|
||||||
|
extern bool is_greater_eq_f32(float x, float y);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (!is_less_f64(3.14, 5.0)) return 1;
|
||||||
|
if (is_less_f64(5.0, 3.14)) return 2;
|
||||||
|
if (!is_greater_eq_f32(2.5f, 2.5f)) return 3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
0
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[code]
|
||||||
|
fn test_f32(x: f32, y: f32) -> f32 {
|
||||||
|
let z: f32 = 1.5;
|
||||||
|
// (4.0 + 2.0) * 1.5 - (4.0 / 2.0) = 6.0 * 1.5 - 2.0 = 9.0 - 2.0 = 7.0
|
||||||
|
return (x + y) * z - (x / y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[harness]
|
||||||
|
extern float test_f32(float x, float y);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
float result = test_f32(4.0f, 2.0f);
|
||||||
|
if (result == 7.0f) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
0
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[code]
|
||||||
|
fn test_f64(x: f64, y: f64) -> f64 {
|
||||||
|
let z: f64 = 2.5;
|
||||||
|
// (7.0 - 2.0) / 2.5 + (7.0 * 2.0) = 5.0 / 2.5 + 14.0 = 2.0 + 14.0 = 16.0
|
||||||
|
return (x - y) / z + (x * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[harness]
|
||||||
|
extern double test_f64(double x, double y);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
double result = test_f64(7.0, 2.0);
|
||||||
|
if (result == 16.0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
0
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[code]
|
||||||
|
fn test_exponents() -> f64 {
|
||||||
|
let a: f64 = 1e3;
|
||||||
|
let b: f64 = 2.5e-1;
|
||||||
|
return a * b; // 1000.0 * 0.25 = 250.0
|
||||||
|
}
|
||||||
|
|
||||||
|
[harness]
|
||||||
|
extern double test_exponents();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (test_exponents() == 250.0) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
0
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[code]
|
||||||
|
fn negate_f32(x: f32) -> f32 {
|
||||||
|
return -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate_f64(x: f64) -> f64 {
|
||||||
|
return -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
[harness]
|
||||||
|
extern float negate_f32(float x);
|
||||||
|
extern double negate_f64(double x);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (negate_f32(3.5f) != -3.5f) return 1;
|
||||||
|
if (negate_f64(-4.2) != 4.2) return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[expected_return_code]
|
||||||
|
0
|
||||||
Reference in New Issue
Block a user