diff --git a/book/listings/ch01-getting-started/listing01.mun b/book/listings/ch01-getting-started/listing01.mun index 2695cb766..81774a7f4 100644 --- a/book/listings/ch01-getting-started/listing01.mun +++ b/book/listings/ch01-getting-started/listing01.mun @@ -1,3 +1,7 @@ +# pub fn run() { +# fibonacci_n(); +# } + pub fn fibonacci_n() -> i64 { let n = arg(); fibonacci(n) diff --git a/book/listings/ch01-getting-started/listing02.mun b/book/listings/ch01-getting-started/listing02.mun index 7e8a407dd..63fd819c5 100644 --- a/book/listings/ch01-getting-started/listing02.mun +++ b/book/listings/ch01-getting-started/listing02.mun @@ -1,3 +1,7 @@ +# pub fn run() { +# fibonacci(arg()); +# } + pub fn arg() -> i64 { 5 } diff --git a/book/src/ch01-02-hello-fibonacci.md b/book/src/ch01-02-hello-fibonacci.md index 4ced272b7..204b6b674 100644 --- a/book/src/ch01-02-hello-fibonacci.md +++ b/book/src/ch01-02-hello-fibonacci.md @@ -28,7 +28,7 @@ Open up the new source file and enter the code in Listing 1-1. Filename: hello_fibonacci.mun -```mun +```mun,run {{#include ../listings/ch01-getting-started/listing01.mun}} ``` diff --git a/book/src/ch01-03-hello-hot-reloading.md b/book/src/ch01-03-hello-hot-reloading.md index ffbddebd0..90b9f62d6 100644 --- a/book/src/ch01-03-hello-hot-reloading.md +++ b/book/src/ch01-03-hello-hot-reloading.md @@ -10,7 +10,7 @@ both `args` and `fibonacci`. Filename: hello_fibonacci.mun -```mun +```mun,run {{#include ../listings/ch01-getting-started/listing02.mun}} ``` diff --git a/book/src/ch02-01-values-and-types.md b/book/src/ch02-01-values-and-types.md index 1bd4a49af..a8c5efdc1 100644 --- a/book/src/ch02-01-values-and-types.md +++ b/book/src/ch02-01-values-and-types.md @@ -17,7 +17,10 @@ inferencing to determine variable types at compile time. However, you are still forced to explicitly annotate variables in a few locations to ensure a contract between interdependent code. -```mun +```mun,run +# pub fn run() { +# bar(1); +# } fn bar(a: i32) -> i32 { let foo = 3 + a; foo @@ -69,7 +72,10 @@ are represented according to the IEEE-754 standard. The `f32` type is a single-precision float of 32 bits, and the `f64` type has double precision - requiring 64 bits. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let f = 3.0; // f64 } @@ -82,6 +88,9 @@ The `bool` (or *boolean*) type has two values, `true` and `false`, that are used to evaluate conditions. It takes up one 1 byte (or 8 bits). ```mun +# pub fn run() { +# main(); +# } fn main() { let t = true; @@ -100,8 +109,8 @@ An integer literal is a number without a decimal separator (`.`). It can be written as a decimal, hexadecimal, octal or binary value. These are all examples of valid literals: -```mun -# fn main() { +```mun,run +# pub fn run() { let a = 367; let b = 0xbeaf; let c = 0o76532; @@ -117,8 +126,8 @@ A floating-point literal comes in two forms: Examples of valid floating-point literals are: -```mun -# fn main() { +```mun,run +# pub fn run() { let a: f64 = 3.1415; let b: f64 = 3.; let c: f64 = 314.1592654e-2; @@ -131,8 +140,8 @@ Both integer and floating-point literals can contain underscores (`_`) to visually separate numbers from one another. They do not have any semantic significance but can be useful to the eye. -```mun -# fn main() { +```mun,run +# pub fn run() { let a: i64 = 1_000_000; let b: f64 = 1_000.12; # } @@ -153,8 +162,8 @@ explicitly specify the type of the literal. Note that integer literals can have floating-point suffixes. This is not the case the other way around. -```mun -# fn main() { +```mun,run +# pub fn run() { let a: u8 = 128_u8; let b: i128 = 99999999999999999_i128; let c: f32 = 10_f32; // integer literal with float suffix @@ -165,7 +174,7 @@ When providing a literal, the compiler will always check if a literal value will fit the type. If not, an error will be emitted: ```mun,compile_fail -# fn main() { +# fn run() { let a: u8 = 1123123124124_u8; // literal out of range for `u8` # } ``` @@ -175,7 +184,10 @@ let a: u8 = 1123123124124_u8; // literal out of range for `u8` Mun supports all basic mathematical operations for number types: addition, subtraction, division, multiplication, and remainder. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { // addition let a = 10 + 5; @@ -200,7 +212,10 @@ same type. Unary operators are also supported: -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let a = 4; // negate @@ -218,8 +233,8 @@ Redeclaring a variable by the same name with a `let` statement is valid and will shadow any previous declaration in the same block. This is often useful if you want to change the type of a variable. -```mun -# fn main() { +```mun,run +# pub fn run() { let a: i32 = 3; let a: f64 = 5.0; # } @@ -246,8 +261,8 @@ the above could have better been written by *returning* a value from the `if`/`else` block instead of assigning to `a`. This avoids the use of an uninitialized value. -```mun -# fn main() { +```mun,run +# pub fn run() { # let some_conditional = true; let a: i32 = if some_conditional { 4 diff --git a/book/src/ch02-02-functions.md b/book/src/ch02-02-functions.md index f8a6c59a9..45422c00c 100644 --- a/book/src/ch02-02-functions.md +++ b/book/src/ch02-02-functions.md @@ -7,7 +7,10 @@ the `fn` keyword, which is used to define a function. Mun uses *snake case* as the conventional style for function and variable names. In snake case all letters are lowercase and words are separated by underscores. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { another_function(); } @@ -30,7 +33,10 @@ the function will only be accessible from the current source file. Marking a function with the `pub` keyword allows you to use it from outside of the module it is defined in. -```mun +```mun,run +# pub fn run() { +# bar(); +# } // This function is not accessible outside of this code fn foo() { // ... @@ -58,7 +64,10 @@ define a *contract* of what your function can accept as its input. The following is a rewritten version of `another_function` that shows what an argument looks like: -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { another_function(3); } @@ -71,7 +80,10 @@ The declaration of `another_function` specifies an argument `x` of the `i32` type. When you want a function to use multiple arguments, separate them with commas: -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { another_function(3, 4); } @@ -89,7 +101,10 @@ value. *Expressions* evaluate to a result value. Creating a variable and assigning a value to it with the `let` keyword is a statement. In the following example, `let y = 6;` is a statement. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let y = 6; } @@ -107,7 +122,10 @@ The body of a function is just a block. In Mun, not just bodies, but all blocks evaluate to the last expression in them. Blocks can therefore also be used on the right hand side of a `let` statement. -```mun +```mun,run +# pub fn run() { +# foo(); +# } fn foo() -> i32 { let bar = { let b = 3; @@ -126,7 +144,10 @@ values in the function declaration, but we do declare their type after an arrow in the function body. You can however return early from a function by using the `return` keyword and specifying a value. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn five() -> i32 { 5 } diff --git a/book/src/ch02-03-control-flow.md b/book/src/ch02-03-control-flow.md index e76ae6ef4..75456fa27 100644 --- a/book/src/ch02-03-control-flow.md +++ b/book/src/ch02-03-control-flow.md @@ -8,7 +8,10 @@ constructs that allow developers to control the flow of execution. Mun provides An `if` expression allows you to branch your code depending on conditions. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let number = 3; @@ -29,7 +32,10 @@ Optionally, an `else` expression can be added that will be executed when the condition evaluates to false. You can also have multiple conditions by combining `if` and `else` in an `else if` expression. For example: -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let number = 6; if number > 10 { @@ -50,7 +56,10 @@ fn main() { The `if` expression can be used on the right side of a `let` statement just like a block: -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let condition = true; let number = if condition { @@ -72,7 +81,10 @@ will report an error. A `loop` expression can be used to create an infinite loop. Breaking out of the loop is done using the `break` statement. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let i = 0; loop { @@ -88,7 +100,10 @@ fn main() { Similar to `if`/`else` expressions, `loop` blocks can have a return value that can be returned through the use of a `break` statement. -```mun +```mun,run +# pub fn run() { +# count(4, 4); +# } fn count(i: i32, n: i32) -> i32 { let loop_count = 0; loop { @@ -120,7 +135,10 @@ loop starts with the keyword `while` followed by a condition expression and a block of code to execute upon each iteration. Just like with the `if` expression, no parentheses are required around the condition expression. -```mun +```mun,run +# pub fn run() { +# main(); +# } fn main() { let i = 0; while i <= 5 { diff --git a/book/src/ch02-04-extern-fn.md b/book/src/ch02-04-extern-fn.md index 34cc84211..c049dad17 100644 --- a/book/src/ch02-04-extern-fn.md +++ b/book/src/ch02-04-extern-fn.md @@ -6,7 +6,7 @@ definitions have to be provided to the runtime when loading a Mun library. Failure to do so will result in a runtime link error, and loading the library will fail. Take this code for example: -```mun,no_run +```mun {{#include ../listings/ch02-basic-concepts/listing01.mun}} ``` diff --git a/book/src/ch03-04-hot-reloading-structs.md b/book/src/ch03-04-hot-reloading-structs.md index aa0590ed6..8433bce46 100644 --- a/book/src/ch03-04-hot-reloading-structs.md +++ b/book/src/ch03-04-hot-reloading-structs.md @@ -13,7 +13,7 @@ our simulation does it to log the elapsed time, every frame. Filename: buoyancy.mun -```mun,no_run +```mun {{#include ../listings/ch03-structs/listing13.mun}} ``` @@ -58,7 +58,11 @@ account, but for the sake of simplicity we'll only consider vertical movement. Let's add this to the `SimContext` struct and update the `new_sim` function accordingly, as shown in Listing 3-15. -```mun +```mun,run +# pub fn run() { +# new_sim(); +# new_sphere(); +# } {{#include ../listings/ch03-structs/listing15.mun:3:41}} ``` @@ -98,8 +102,12 @@ As before, the `token` value will be initialized to zero when the library has been hot reloaded. Next, we add a `hot_reload_token` function that returns a non-zero `u32` value, e.g. `1`: -```mun +```mun,run +# pub fn run() { +# hot_reload_token(); +# } {{#include ../listings/ch03-structs/listing16.mun:45:47}} + ``` Finally, we add this `if` statement to the `sim_update` function: diff --git a/crates/mun_mdbook_test_plugin/src/main.rs b/crates/mun_mdbook_test_plugin/src/main.rs index 6c85d8a99..20bfe01bc 100644 --- a/crates/mun_mdbook_test_plugin/src/main.rs +++ b/crates/mun_mdbook_test_plugin/src/main.rs @@ -7,7 +7,7 @@ use regex::Regex; use mun_test::{CompileAndRunTestDriver, CompileTestDriver}; -use mun_runtime::RuntimeBuilder; +use mun_runtime::{invoke_fn, RuntimeBuilder}; use std::panic; @@ -21,12 +21,15 @@ enum TestingMethod { impl Default for TestingMethod { fn default() -> Self { - TestingMethod::CompileAndRun + TestingMethod::Compile } } fn get_testing_method(code: &str) -> TestingMethod { let mut testing_method = TestingMethod::default(); + + let function_name_regex = Regex::new(r###"fn\("((?s:.)+)"\)"###).unwrap(); + for line in code.lines() { if let Some(first_char) = line.chars().next() { if first_char != ',' { @@ -42,10 +45,10 @@ fn get_testing_method(code: &str) -> TestingMethod { _ => (), } } - if line == ",no_run" { + if line == ",run" { match testing_method { - TestingMethod::CompileAndRun => testing_method = TestingMethod::Compile, - TestingMethod::CompileAndRunFail => testing_method = TestingMethod::CompileFail, + TestingMethod::Compile => testing_method = TestingMethod::CompileAndRun, + TestingMethod::CompileFail => testing_method = TestingMethod::CompileAndRunFail, _ => (), } } @@ -63,8 +66,8 @@ fn test_code(code: &str) { // Removing '#' from code let mut code = code.replace("\n#", "\n"); - // Removing testing commands like "no_run", "compile_fail" - for testing_command in ["no_run", "compile_fail"].iter() { + // Removing testing commands + for testing_command in ["run", "compile_fail"].iter() { code = code.replace(format!(",{}", testing_command).as_str(), ""); } @@ -88,8 +91,22 @@ fn test_code(code: &str) { let previous_hook = panic::take_hook(); panic::set_hook(Box::new(|_| {})); - if panic::catch_unwind(|| CompileAndRunTestDriver::new(&code, config_fn).unwrap()) - .is_ok() + if panic::catch_unwind(|| { + let compile_and_run_test_driver = + CompileAndRunTestDriver::new(&code, config_fn).unwrap(); + + if let None = compile_and_run_test_driver + .runtime() + .borrow() + .get_function_definition("run") + { + panic!("Function `run` not found in mun code, but requested by `run` command."); + } + + let _: () = + invoke_fn!(compile_and_run_test_driver.runtime().borrow_mut(), "run").unwrap(); + }) + .is_ok() { panic::set_hook(previous_hook); panic!("Code that should have caused the error compiled successfully ❌") @@ -101,7 +118,19 @@ fn test_code(code: &str) { CompileTestDriver::new(&code); } TestingMethod::CompileAndRun => { - CompileAndRunTestDriver::new(&code, config_fn).unwrap(); + let compile_and_run_test_driver = + CompileAndRunTestDriver::new(&code, config_fn).unwrap(); + + if let None = compile_and_run_test_driver + .runtime() + .borrow() + .get_function_definition("run") + { + panic!("Function `run` not found in mun code, but requested by `run` command."); + } + + let _: () = + invoke_fn!(compile_and_run_test_driver.runtime().borrow_mut(), "run").unwrap(); } TestingMethod::Skip => {} }