From 35c4d2161ea07cfbb4085d7e5242ab9939889afa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 Jun 2015 18:43:36 +0200 Subject: [PATCH 1/1] add some @-magic to generate a workspace --- .gitignore | 3 +- Makefile | 29 ++++++++++++++----- dup-unimpl.sed | 8 ++++++ src/part00.rs | 62 ++++++++++++++++++++--------------------- workspace/src/main.rs | 13 +++++---- workspace/src/part00.rs | 1 - workspace/src/part01.rs | 1 - workspace/src/part02.rs | 1 - workspace/src/part03.rs | 1 - 9 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 dup-unimpl.sed delete mode 100644 workspace/src/part00.rs delete mode 100644 workspace/src/part01.rs delete mode 100644 workspace/src/part02.rs delete mode 100644 workspace/src/part03.rs diff --git a/.gitignore b/.gitignore index 4903df5..aded1db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ sync-docs -rawsrc/ +.tmp/ +workspace/src/part*.rs diff --git a/Makefile b/Makefile index eb1851e..0061b2b 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,33 @@ FILES=$(wildcard src/*.rs) DOCFILES=$(addsuffix .html,$(addprefix docs/,$(notdir $(basename $(FILES))))) +WORKSPACEFILES=$(addprefix workspace/,$(FILES)) -all: docs crates -.PHONY: docs rawsrc crates +all: docs workspace crates +.PHONY: docs workspace crates +# Documentation docs: $(DOCFILES) -docs/%.html: src/%.rs - @./pycco-rs $^ +.tmp/docs/%.rs: src/%.rs Makefile + @mkdir -p .tmp/docs + @echo "$< -> $@" + @sed 's|^\(\s*//\)@|\1|;s|\s*/\*@\*/||' $< > $@ -rawsrc: - @mkdir -p rawsrc - @for file in $(FILES); do echo "$$file -> rawsrc/$$file"; egrep -v "^[[:space:]]*// " "$$file" > "rawsrc/""$$file"; done +docs/%.html: .tmp/docs/%.rs + @./pycco-rs $< +# Workspace +workspace: $(WORKSPACEFILES) + +workspace/src/%.rs: src/%.rs Makefile dup-unimpl.sed + @mkdir -p .tmp/docs + @echo "$< -> $@" + @sed '/^\s*\/\/@/d;s|\(\s*\)[^\s].*/\*@\*/|\1unimplemented!()|' $< | sed -f dup-unimpl.sed > $@ + +workspace/src/main.rs: + # Don't touch this file + +# Crates crates: @cargo build @cd solutions && cargo build diff --git a/dup-unimpl.sed b/dup-unimpl.sed new file mode 100644 index 0000000..6de156b --- /dev/null +++ b/dup-unimpl.sed @@ -0,0 +1,8 @@ +/^\s*unimplemented!()$/ { # if the current line is an "unimplemented!()" + : isunimp + N; # load next line + /\n\s*unimplemented!()$/ { # see if that one is also "unimplemented!()" + D; # delete first line, keep all the rest + b isunimp + } +} diff --git a/src/part00.rs b/src/part00.rs index 1648ba1..2bc40d9 100644 --- a/src/part00.rs +++ b/src/part00.rs @@ -4,36 +4,36 @@ // As our first piece of Rust code, we want to write a function that computes the // minimum of a list. -// ## Getting started -// Let us start by thinking about the *type* of our function. Rust forces us to give the types of -// all arguments, and the return type, before we even start writing the body. In the case of our minimum -// function, we may be inclined to say that it returns a number. But then we would be in trouble: What's -// the minimum of an empty list? The type of the function says we have to return *something*. -// We could just choose 0, but that would be kind of arbitrary. What we need -// is a type that is "a number, or nothing". Such a type (of multiple exclusive options) -// is called an "algebraic datatype", and Rust lets us define such types with the keyword `enum`. -// Coming from C(++), you can think of such a type as a `union`, together with a field that -// stores the variant of the union that's currently used. +//@ ## Getting started +//@ Let us start by thinking about the *type* of our function. Rust forces us to give the types of +//@ all arguments, and the return type, before we even start writing the body. In the case of our minimum +//@ function, we may be inclined to say that it returns a number. But then we would be in trouble: What's +//@ the minimum of an empty list? The type of the function says we have to return *something*. +//@ We could just choose 0, but that would be kind of arbitrary. What we need +//@ is a type that is "a number, or nothing". Such a type (of multiple exclusive options) +//@ is called an "algebraic datatype", and Rust lets us define such types with the keyword `enum`. +//@ Coming from C(++), you can think of such a type as a `union`, together with a field that +//@ stores the variant of the union that's currently used. // An `enum` for "a number or nothing" could look as follows: enum NumberOrNothing { Number(i32), Nothing } -// Notice that `i32` is the type of (signed, 32-bit) integers. To write down the type of -// the minimum function, we need just one more ingredient: `Vec` is the type of -// (growable) arrays of numbers, and we will use that as our list type. +//@ Notice that `i32` is the type of (signed, 32-bit) integers. To write down the type of +//@ the minimum function, we need just one more ingredient: `Vec` is the type of +//@ (growable) arrays of numbers, and we will use that as our list type. // Observe how in Rust, the return type comes *after* the arguments. fn vec_min(vec: Vec) -> NumberOrNothing { - // In the function, we first need some variable to store the minimum as computed so far. - // Since we start out with nothing computed, this will again be a - // "number or nothing": + //@ In the function, we first need some variable to store the minimum as computed so far. + //@ Since we start out with nothing computed, this will again be a + //@ "number or nothing": let mut min = NumberOrNothing::Nothing; - // We do not have to write a type next to `min`, Rust can figure that out automatically - // (a bit like `auto` in C++11). Also notice the `mut`: In Rust, variables are - // immutable per default, and you need to tell Rust if you want - // to change a variable later. + //@ We do not have to write a type next to `min`, Rust can figure that out automatically + //@ (a bit like `auto` in C++11). Also notice the `mut`: In Rust, variables are + //@ immutable per default, and you need to tell Rust if you want + //@ to change a variable later. // Now we want to *iterate* over the list. Rust has some nice syntax for // iterators: @@ -43,13 +43,13 @@ fn vec_min(vec: Vec) -> NumberOrNothing { match min { // In this case (*arm*) of the `match`, `min` is currently nothing, so let's just make it the number `el`. NumberOrNothing::Nothing => { - min = NumberOrNothing::Number(el); + min = NumberOrNothing::Number(el); /*@*/ }, // In this arm, `min` is currently the number `n`, so let's compute the new minimum and store it. We will write // the function `min_i32` just after we completed this one. NumberOrNothing::Number(n) => { - let new_min = min_i32(n, el); - min = NumberOrNothing::Number(new_min); + let new_min = min_i32(n, el); /*@*/ + min = NumberOrNothing::Number(new_min); /*@*/ } } } @@ -60,9 +60,9 @@ fn vec_min(vec: Vec) -> NumberOrNothing { // Now that we reduced the problem to computing the minimum of two integers, let's do that. fn min_i32(a: i32, b: i32) -> i32 { if a < b { - return a; + return a; /*@*/ } else { - return b; + return b; /*@*/ } } @@ -88,9 +88,9 @@ fn read_vec() -> Vec { // Of course Rust can print numbers, but after calling `vec_min`, we have a `NumberOrNothing`. // So let's write a small helper function that prints such values. -// `println!` is again a macro, where the first argument is a *format string*. For -// now, you just need to know that `{}` is the placeholder for a value, and that Rust -// will check at compile-time that you supplied the right number of arguments. +//@ `println!` is again a macro, where the first argument is a *format string*. For +//@ now, you just need to know that `{}` is the placeholder for a value, and that Rust +//@ will check at compile-time that you supplied the right number of arguments. fn print_number_or_nothing(n: NumberOrNothing) { match n { Nothing => println!("The number is: "), @@ -107,8 +107,8 @@ pub fn main() { // Now try `cargo run` on the console to run above code. -// Yay, it said "1"! That's actually the right answer. Okay, we could have -// computed that ourselves, but that's besides the point. More importantly: -// You completed the first part of the course. +//@ Yay, it said "1"! That's actually the right answer. Okay, we could have +//@ computed that ourselves, but that's besides the point. More importantly: +//@ You completed the first part of the course. // [index](main.html) | previous | [next](part01.html) diff --git a/workspace/src/main.rs b/workspace/src/main.rs index 2722589..2db7819 100644 --- a/workspace/src/main.rs +++ b/workspace/src/main.rs @@ -1,10 +1,13 @@ +#![allow(dead_code, unused_imports, unused_variables, unused_mut)] + +// Only the files imported here will be compiled. Remember to add or enable new +// parts here as you progress through the course. mod part00; -mod part01; -mod part02; -mod part03; +// mod part01; +// mod part02; +// mod part03; -#[cfg(not(test))] /* If you get warnings about functions not being used on "crate test", adding this attribute will fix them. - It says that the function is only to be compiled if we are *not* compiling for tests. */ +// This decides which part is actually run. fn main() { part00::main(); } diff --git a/workspace/src/part00.rs b/workspace/src/part00.rs deleted file mode 100644 index 8d1c8b6..0000000 --- a/workspace/src/part00.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/workspace/src/part01.rs b/workspace/src/part01.rs deleted file mode 100644 index 8d1c8b6..0000000 --- a/workspace/src/part01.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/workspace/src/part02.rs b/workspace/src/part02.rs deleted file mode 100644 index 8d1c8b6..0000000 --- a/workspace/src/part02.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/workspace/src/part03.rs b/workspace/src/part03.rs deleted file mode 100644 index 8d1c8b6..0000000 --- a/workspace/src/part03.rs +++ /dev/null @@ -1 +0,0 @@ - -- 2.30.2