diff --git a/Cargo.lock b/Cargo.lock index 0b34ba8..d7933c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,6 +397,7 @@ dependencies = [ "concrete_session", "salsa-2022", "tempfile", + "test-case", "tracing", "tracing-subscriber", ] @@ -413,6 +414,7 @@ dependencies = [ "logos", "salsa-2022", "tracing", + "unescaper", ] [[package]] @@ -1586,6 +1588,39 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "test-case-core", +] + [[package]] name = "thiserror" version = "1.0.56" @@ -1721,6 +1756,15 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "unescaper" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f0f68e58d297ba8b22b8b5a96a87b863ba6bb46aaf51e19a4b02c5a6dd5b7f" +dependencies = [ + "thiserror", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/crates/concrete_ast/src/expressions.rs b/crates/concrete_ast/src/expressions.rs index 12f8752..cdecc70 100644 --- a/crates/concrete_ast/src/expressions.rs +++ b/crates/concrete_ast/src/expressions.rs @@ -18,8 +18,8 @@ pub enum Expression { pub enum ValueExpr { ConstBool(bool), ConstChar(char), - ConstInt(u64), - ConstFloat(()), + ConstInt(u128), + ConstFloat(String), ConstStr(String), Path(PathOp), Deref(PathOp), diff --git a/crates/concrete_codegen_mlir/src/codegen.rs b/crates/concrete_codegen_mlir/src/codegen.rs index 4bcae6f..6fe0b7e 100644 --- a/crates/concrete_codegen_mlir/src/codegen.rs +++ b/crates/concrete_codegen_mlir/src/codegen.rs @@ -19,7 +19,10 @@ use melior::{ cf, func, memref, }, ir::{ - attribute::{FlatSymbolRefAttribute, IntegerAttribute, StringAttribute, TypeAttribute}, + attribute::{ + FlatSymbolRefAttribute, FloatAttribute, IntegerAttribute, StringAttribute, + TypeAttribute, + }, r#type::{FunctionType, IntegerType, MemRefType}, Block, BlockRef, Location, Module as MeliorModule, Operation, Region, Type, Value, ValueLike, @@ -132,6 +135,7 @@ impl<'ctx, 'parent> ScopeContext<'ctx, 'parent> { "u32" | "i32" => IntegerType::new(context, 32).into(), "u16" | "i16" => IntegerType::new(context, 16).into(), "u8" | "i8" => IntegerType::new(context, 8).into(), + "char" => IntegerType::new(context, 32).into(), "f32" => Type::float32(context), "f64" => Type::float64(context), "bool" => IntegerType::new(context, 1).into(), @@ -828,7 +832,22 @@ fn compile_value_expr<'ctx, 'parent: 'ctx>( .result(0)? .into()) } - ValueExpr::ConstFloat(_) => todo!(), + ValueExpr::ConstFloat(value) => { + let float_type = if let Some(type_info) = type_info { + scope_ctx.resolve_type_spec(context, type_info)? + } else { + Type::float64(context) + }; + let value = FloatAttribute::new( + context, + value.parse().expect("failed to parse float"), + float_type, + ); + Ok(block + .append_operation(arith::constant(context, value.into(), location)) + .result(0)? + .into()) + } ValueExpr::ConstStr(_) => todo!(), ValueExpr::Path(value) => { compile_path_op(session, context, scope_ctx, _helper, block, value) diff --git a/crates/concrete_driver/Cargo.toml b/crates/concrete_driver/Cargo.toml index 865d496..ef0763a 100644 --- a/crates/concrete_driver/Cargo.toml +++ b/crates/concrete_driver/Cargo.toml @@ -19,3 +19,4 @@ concrete_check = { version = "0.1.0", path = "../concrete_check" } [dev-dependencies] tempfile = "3.9.0" +test-case = "3.3.1" diff --git a/crates/concrete_driver/tests/examples.rs b/crates/concrete_driver/tests/examples.rs index 310f5f2..8b06363 100644 --- a/crates/concrete_driver/tests/examples.rs +++ b/crates/concrete_driver/tests/examples.rs @@ -1,77 +1,24 @@ use crate::common::{compile_program, run_program}; +use test_case::test_case; mod common; -#[test] -fn borrow() { - let program = compile_program( - include_str!("../../../examples/borrow.con"), - "borrow", - false, - ) - .unwrap(); +#[test_case(include_str!("../../../examples/borrow.con"), "borrow", false, 2 ; "borrow.con")] +#[test_case(include_str!("../../../examples/factorial_if.con"), "factorial_if", false, 24 ; "factorial_if.con")] +#[test_case(include_str!("../../../examples/fib_if.con"), "fib_if", false, 55 ; "fib_if.con")] +#[test_case(include_str!("../../../examples/import.con"), "import", false, 12 ; "import.con")] +#[test_case(include_str!("../../../examples/simple.con"), "simple", false, 8 ; "simple.con")] +#[test_case(include_str!("../../../examples/while.con"), "while", false, 16 ; "while.con")] +#[test_case(include_str!("../../../examples/chars.con"), "chars", false, 117 ; "chars.con")] +#[test_case(include_str!("../../../examples/floats.con"), "floats", false, 1 ; "floats.con")] +fn example_tests(source: &str, name: &str, is_library: bool, status_code: i32) { + let program = compile_program(source, name, is_library).unwrap(); let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 2); -} - -#[test] -fn factorial_if() { - let program = compile_program( - include_str!("../../../examples/factorial_if.con"), - "factorial_if", - false, - ) - .unwrap(); - - let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 24); -} - -#[test] -fn fib_if() { - let program = compile_program( - include_str!("../../../examples/fib_if.con"), - "fib_if", - false, - ) - .unwrap(); - - let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 55); -} - -#[test] -fn import() { - let program = compile_program( - include_str!("../../../examples/import.con"), - "import", - false, - ) - .unwrap(); - - let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 12); -} - -#[test] -fn simple() { - let program = compile_program( - include_str!("../../../examples/simple.con"), - "simple", - false, - ) - .unwrap(); - - let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 8); -} - -#[test] -fn r#while() { - let program = - compile_program(include_str!("../../../examples/while.con"), "while", false).unwrap(); - - let result = run_program(&program.binary_file).unwrap(); - assert_eq!(result.status.code().unwrap(), 16); + assert_eq!( + result.status.code().unwrap(), + status_code, + "Program {} returned a unexpected status code", + name + ); } diff --git a/crates/concrete_driver/tests/programs.rs b/crates/concrete_driver/tests/programs.rs index fecc2f3..660d9bc 100644 --- a/crates/concrete_driver/tests/programs.rs +++ b/crates/concrete_driver/tests/programs.rs @@ -132,26 +132,58 @@ fn test_import() { } #[test] -fn test_reference() { +fn test_floats() { let source = r#" - mod Simple { - fn main(argc: i64) -> i64 { - let x: i64 = argc; - return references(x) + dereference(&x); - } + mod Simple { + fn main() -> i64 { + let a: f32 = my_f32(2.0, 4.0); + let b: f64 = my_f64(2.0, 4.0); + return 1; + } - fn dereference(a: &i64) -> i64 { - return *a; - } + fn my_f32(x: f32, y: f32) -> f32 { + let literal: f32 = 2.0; + let literal2: f32 = 2.001; + let literal3: f32 = 0.1; + return x + y + literal2 + literal3; + } - fn references(a: i64) -> i64 { - let x: i64 = a; - let y: &i64 = &x; - return *y; - } + fn my_f64(x: f64, y: f64) -> f64 { + let literal: f64 = 2.0; + let literal2: f64 = 2.002; + let literal3: f64 = 0.02; + return x + y + literal2 + literal3; } + } "#; + let result = compile_program(source, "floats", false).expect("failed to compile"); + + let output = run_program(&result.binary_file).expect("failed to run"); + let code = output.status.code().unwrap(); + assert_eq!(code, 1); +} + +#[test] +fn test_reference() { + let source = r#" + mod Simple { + fn main(argc: i64) -> i64 { + let x: i64 = argc; + return references(x) + dereference(&x); + } + + fn dereference(a: &i64) -> i64 { + return *a; + } + + fn references(a: i64) -> i64 { + let x: i64 = a; + let y: &i64 = &x; + return *y; + } + } + "#; let result = compile_program(source, "references", false).expect("failed to compile"); let output = run_program(&result.binary_file).expect("failed to run"); diff --git a/crates/concrete_parser/Cargo.toml b/crates/concrete_parser/Cargo.toml index 239eba6..f0422e6 100644 --- a/crates/concrete_parser/Cargo.toml +++ b/crates/concrete_parser/Cargo.toml @@ -13,6 +13,7 @@ concrete_ast = { path = "../concrete_ast"} salsa = { git = "https://github.com/salsa-rs/salsa.git", package = "salsa-2022" } ariadne = { version = "0.4.0", features = ["auto-color"] } itertools = "0.12.0" +unescaper = "0.1.3" [build-dependencies] lalrpop = "0.20.0" diff --git a/crates/concrete_parser/src/grammar.lalrpop b/crates/concrete_parser/src/grammar.lalrpop index e9ffbd8..e3d7714 100644 --- a/crates/concrete_parser/src/grammar.lalrpop +++ b/crates/concrete_parser/src/grammar.lalrpop @@ -30,8 +30,10 @@ extern { // literals "identifier" => Token::Identifier(), - "integer" => Token::Integer(), + "integer" => Token::Integer(), + "float" => Token::Float(), "string" => Token::String(), + "char" => Token::Char(), "boolean" => Token::Boolean(), // Other @@ -150,7 +152,7 @@ pub(crate) TypeSpec: ast::types::TypeSpec = { }, "[" )?> "]" => ast::types::TypeSpec::Array { of_type: Box::new(of_type), - size, + size: size.map(|x| x.try_into().expect("size is too big")), is_ref, span: Span::new(lo, hi), } @@ -358,8 +360,10 @@ pub UnaryOp: ast::expressions::UnaryOp = { pub(crate) ValueExpr: ast::expressions::ValueExpr = { <"integer"> => ast::expressions::ValueExpr::ConstInt(<>), + <"float"> => ast::expressions::ValueExpr::ConstFloat(<>), <"boolean"> => ast::expressions::ValueExpr::ConstBool(<>), <"string"> => ast::expressions::ValueExpr::ConstStr(<>), + <"char"> => ast::expressions::ValueExpr::ConstChar(<>), => ast::expressions::ValueExpr::Path(<>), "*" => ast::expressions::ValueExpr::Deref(<>), => ast::expressions::ValueExpr::AsRef { diff --git a/crates/concrete_parser/src/tokens.rs b/crates/concrete_parser/src/tokens.rs index 316a74d..1781db7 100644 --- a/crates/concrete_parser/src/tokens.rs +++ b/crates/concrete_parser/src/tokens.rs @@ -57,12 +57,25 @@ pub enum Token { Identifier(String), // Literals - #[regex(r"\d+", |lex| lex.slice().parse::().unwrap())] - Integer(u64), - #[regex(r#""(?:[^"]|\\")*""#, |lex| lex.slice().to_string())] + #[regex(r"\d+", |lex| lex.slice().parse::().unwrap(), priority = 2)] + Integer(u128), + #[regex(r"\d+\.\d+", |lex| lex.slice().to_string(), priority = 1)] + Float(String), + #[regex(r#""(?:[^"]|\\")*""#, |lex| { + let slice = lex.slice(); + let len = slice.len(); + unescaper::unescape(&slice[1..(len-1)]).expect("failed to unescape string") + })] String(String), #[regex(r"(true|false)", |lex| lex.slice().parse::().unwrap())] Boolean(bool), + #[regex(r#"'(?:[^']|\\')*'"#, |lex| { + let slice = lex.slice(); + let len = slice.len(); + let real_char = unescaper::unescape(&slice[1..(len-1)]).expect("failed to unescape char").to_string(); + real_char.chars().next().unwrap() + })] + Char(char), #[token("(")] LeftParen, diff --git a/examples/chars.con b/examples/chars.con new file mode 100644 index 0000000..1742a8a --- /dev/null +++ b/examples/chars.con @@ -0,0 +1,13 @@ + mod Simple { + fn main() -> char { + let a: char = hello_chars('\t'); + return a; + } + + fn hello_chars(a: char) -> char { + let x: char = 'b'; + let newline: char = '\n'; + + return x + newline + a; + } +} diff --git a/examples/floats.con b/examples/floats.con new file mode 100644 index 0000000..bcfca6f --- /dev/null +++ b/examples/floats.con @@ -0,0 +1,21 @@ + mod Simple { + fn main() -> i64 { + let a: f32 = my_f32(2.0, 4.0); + let b: f64 = my_f64(2.0, 4.0); + return 1; + } + + fn my_f32(x: f32, y: f32) -> f32 { + let literal: f32 = 2.0; + let literal2: f32 = 2.001; + let literal3: f32 = 0.1; + return x + y + literal2 + literal3; + } + + fn my_f64(x: f64, y: f64) -> f64 { + let literal: f64 = 2.0; + let literal2: f64 = 2.002; + let literal3: f64 = 0.02; + return x + y + literal2 + literal3; + } +}