浏览代码

Add examples to the documentation

David Peter 2 年之前
父节点
当前提交
e157883a81

+ 40 - 0
book/build.sh

@@ -0,0 +1,40 @@
+#!/bin/bash
+
+set -euo pipefail
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+
+generate_example() {
+    filename="$1"
+    title="$2"
+    strip_asserts="$3"
+
+    out_file="$SCRIPT_DIR/src/example-${filename}.md"
+
+    (
+        echo "<!-- This file is autogenerated! Do not modify it -->"
+        echo
+        echo "# ${title}"
+        echo
+        echo "\`\`\` numbat"
+        if [ "$strip_asserts" = true ]; then
+            grep -v assert_eq "../examples/${filename}.nbt"
+        else
+            cat "../examples/${filename}.nbt"
+        fi
+        echo "\`\`\`"
+    ) > "$out_file"
+}
+
+generate_example medication_dosage "Medication dosage" true
+generate_example workhours "Workhours" true
+generate_example barometric_formula "Barometric formula" true
+generate_example molarity "Molarity" true
+generate_example musical_note_frequency "Musical note frequency" true
+generate_example body_mass_index "Body mass index" true
+generate_example pipe_flow_rate "Flow rate in a pipe" true
+generate_example recipe "Recipe" true
+
+generate_example numbat_syntax "Syntax overview" false
+
+mdbook build

+ 1 - 1
book/numbat.js

@@ -23,7 +23,7 @@ hljs.registerLanguage('numbat', function(hljs) {
       },
       {
         className: 'title',
-        begin: /[A-Z][a-zA-Z0-9_]+/,
+        begin: /(?<=(\)\s*(->|[→➞])|[:=<\/\*×·])\s*)\b[A-Z][a-zA-Z0-9_]*\b/,
       },
       {
         className: 'operator',

+ 10 - 1
book/src/SUMMARY.md

@@ -4,6 +4,15 @@
 
 - [Introduction](./introduction.md)
 - [Tutorial](./tutorial.md)
+- [Example programs](./examples.md)
+    - [Medication dosage](./example-medication_dosage.md)
+    - [Workhours](./example-workhours.md)
+    - [Barometric formula](./example-barometric_formula.md)
+    - [Molarity](./example-molarity.md)
+    - [Musical note frequency](./example-musical_note_frequency.md)
+    - [Body mass index](./example-body_mass_index.md)
+    - [Flow rate in a pipe](./example-pipe_flow_rate.md)
+    - [Recipe](./example-recipe.md)
 
 # Numbat language reference
 
@@ -17,7 +26,7 @@
 - [Advanced](./advanced.md)
   - [Unit definitions](./unit-definitions.md)
   - [Dimension definitions](./dimension-definitions.md)
-  - [Syntax overview](./syntax-overview.md)
+  - [Syntax overview](./example-numbat_syntax.md)
 
 # Standard library reference
 

+ 1 - 1
book/src/dimension-definitions.md

@@ -27,7 +27,7 @@ unit dot: Dot
 
 unit dpi = dots / inch
 
-fn inter_dot_spacing(resolution: Dot/Length) -> Length = 1 dot / resolution
+fn inter_dot_spacing(resolution: Dot / Length) -> Length = 1 dot / resolution
 
 inter_dot_spacing(72 dpi) -> µm  # 353 µm
 ```

+ 18 - 0
book/src/example-barometric_formula.md

@@ -0,0 +1,18 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Barometric formula
+
+``` numbat
+# This script calculates the air pressure at a specified
+# height above sea level using the barometric formula.
+
+let p0: Pressure = 1 atm
+let t0: Temperature = 288.15 K
+
+let temperature_gradient = 0.65 K / 100 m
+
+fn air_pressure(height: Length) -> Pressure =
+    p0 · (1 - temperature_gradient · height / t0)^5.255
+
+print(air_pressure(1500 m))
+```

+ 15 - 0
book/src/example-body_mass_index.md

@@ -0,0 +1,15 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Body mass index
+
+``` numbat
+# This script calculates the Body Mass Index (BMI) based on
+# the provided mass and height values.
+
+unit BMI: Mass / Length^2 = kg / m^2
+
+fn body_mass_index(mass: Mass, height: Length) =
+    mass / height² -> BMI
+
+print(body_mass_index(70 kg, 1.75 m))
+```

+ 21 - 0
book/src/example-medication_dosage.md

@@ -0,0 +1,21 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Medication dosage
+
+``` numbat
+# This script calculates the total daily dose and per intake
+# dose of a medication based on a person's body weight.
+
+@aliases(takings)
+unit taking
+
+let body_weight = 75 kg
+let dosage = (60 mg / kg) / day
+let frequency = 3 takings / day
+
+let total_daily_dose = dosage * body_weight -> mg / day
+print(total_daily_dose)
+
+let dose = total_daily_dose / frequency -> mg / taking
+print(dose)
+```

+ 19 - 0
book/src/example-molarity.md

@@ -0,0 +1,19 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Molarity
+
+``` numbat
+# This script calculates and prints the molarity of a salt
+# water solution, given a fixed mass of salt (NaCl) and a
+# volume of water.
+
+let molar_mass_NaCl = 58.44 g / mol
+
+fn molarity(mass: Mass, volume: Volume) -> Molarity =
+    (mass / molar_mass_NaCl) / volume
+
+let salt_mass = 9 g
+let water_volume = 1 L
+
+print(molarity(salt_mass, water_volume) -> mmol / l)
+```

+ 15 - 0
book/src/example-musical_note_frequency.md

@@ -0,0 +1,15 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Musical note frequency
+
+``` numbat
+# Musical note frequencies in the 12 equal temperament system
+
+let frequency_A4: Frequency = 440 Hz  # the A above middle C, A4
+
+fn note_frequency(n: Scalar) -> Frequency = frequency_A4 * 2^(n / 12)
+
+print(note_frequency(12))  # one octave higher up (A5), 880 Hz
+print(note_frequency(7))   # E4
+print(note_frequency(-3))  # C4
+```

+ 91 - 0
book/src/example-numbat_syntax.md

@@ -0,0 +1,91 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Syntax overview
+
+``` numbat
+# This is a line comment. It can span over
+# multiple lines
+
+# 1. Imports
+
+use prelude       # This is not necessary. The 'prelude'
+                  # module will always be loaded upon startup
+
+use physics::temperature_conversion   # Load a specific module
+
+# 2. Numbers
+
+12345       # integer notation
+12_345      # optional decimal separators
+
+0.234       # floating point notation
+.234        # without the leading zero
+
+1.234e15    # scientific notation
+1.234e+15
+1e-9
+1.0e-9
+
+0x2A        # hexadecimal
+0o52        # octal
+0b101010    # binary
+
+# 3. Simple expressions
+
+3 + (4 - 3)       # Addition and subtraction
+
+1920 / 16 * 9     # Multiplication, division
+1920 ÷ 16 × 9     # Unicode-style, '·' is also multiplication
+2 pi              # Whitespace is implicit multiplication
+meter per second  # 'per' keyword can be used for division
+
+2^3               # Exponentiation
+2**3              # Python-style
+2³                # Unicode exponents
+
+17 % 4            # Modulo
+
+3 in -> cm        # Unit conversion, can also be → or ➞
+3 in to cm        # Unit conversion with the 'to' keyword
+
+cos(pi/3)         # Call mathematical functions
+
+# 4. Variable definitions
+
+let x = 4                          # Simple numerical variable
+let y1 = 2 m/s                     # Right hand side can be any expression
+let y2: Speed = 2 m/s              # With optional type annotation
+let y3: Length / Time = 2 m/s      # more complex type annotation
+
+# 5. Function definitions
+
+fn foo(x: Scalar) -> Scalar = 2 * x + 3         # A simple function
+fn speed(s: Length, t: Time) -> Speed = s / t   # Two parameters
+fn my_sqrt<T>(x: T^2) -> T = x^(1/2)            # A generic function
+
+# 6. Dimension definitions
+
+dimension Fame                            # A new base dimension
+dimension Deceleration = Length / Time^2  # A new derived dimension
+
+# 7. Unit definitions
+
+@aliases(quorks)                 # Optional aliases-decorator
+unit quork = 0.35 meter          # A new derived unit
+
+@metric_prefixes                 # Optional decorator to allow 'milliclonk', etc.
+@aliases(ck: short)              # short aliases can be used with short prefixes (mck)
+unit clonk: Time = 0.2 seconds   # Optional type annotation
+
+@metric_prefixes
+@aliases(wh: short)
+unit warhol: Fame                # New base unit for phys. dimension "Fame"
+
+unit thing                       # New base unit with automatically generated
+                                 # base dimension "Thing"
+
+# 8. Procedures
+
+print(2 kilowarhol)              # Print a quantity
+assert_eq(1 ft, 12 in)           # Assert that two quantities are equal
+```

+ 25 - 0
book/src/example-pipe_flow_rate.md

@@ -0,0 +1,25 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Flow rate in a pipe
+
+``` numbat
+# This script calculates and prints the flow rate in a pipe
+# using the Hagen-Poiseuille equation. It assumes the dynamic
+# viscosity of water and allows for inputs of pipe radius,
+# pipe length, and pressure difference.
+
+dimension DynamicViscosity = Pressure × Time
+dimension FlowRate = Volume / Time
+
+let μ_water: DynamicViscosity = 1 mPa·s
+
+fn flow_rate(radius: Length, length: Length, Δp: Pressure) -> FlowRate =
+    π × radius^4 × Δp / (8 μ_water × length)
+
+let pipe_radius = 1 cm
+let pipe_length = 10 m
+let Δp = 0.1 bar
+
+let Q: FlowRate = flow_rate(pipe_radius, pipe_length, Δp) -> L/s
+print(Q)
+```

+ 28 - 0
book/src/example-recipe.md

@@ -0,0 +1,28 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Recipe
+
+``` numbat
+# Scale ingredient quantities based on desired servings.
+
+@aliases(servings)
+unit serving
+
+let original_recipe_servings = 2 servings
+let desired_servings = 3 servings
+
+fn scale<D>(quantity: D) -> D =
+    quantity × desired_servings / original_recipe_servings
+
+let milk = 500 ml
+print(scale(milk))
+
+let flour = 250 g
+print(scale(flour))
+
+let sugar = 2 cups
+print(scale(sugar))
+
+let baking_powder = 4 tablespoons
+print(scale(baking_powder))
+```

+ 27 - 0
book/src/example-workhours.md

@@ -0,0 +1,27 @@
+<!-- This file is autogenerated! Do not modify it -->
+
+# Workhours
+
+``` numbat
+# This script shows how to set up practical units for
+# doing workhour and employee-count computations.
+
+@aliases(workhours)
+unit workhour
+
+@aliases(workdays)
+unit workday = 8 workhours
+
+@aliases(workyears)
+unit workyear = 200 workdays
+
+unit FTE = 1 workyear per year
+
+let rate = 1000 € / workday
+print(0.5 million € / rate)
+
+let budget = 0.5 million €
+let duration = 0.5 year
+print(3 FTE * 0.5 years -> workdays)
+print(budget / rate / duration -> FTE)
+```

+ 4 - 0
book/src/examples.md

@@ -0,0 +1,4 @@
+# Examples
+
+This chapter shows some exemplary Numbat programs from various scientific
+and engineering disciplines.

+ 4 - 0
examples/barometric_formula.nbt

@@ -1,3 +1,6 @@
+# This script calculates the air pressure at a specified
+# height above sea level using the barometric formula.
+
 let p0: Pressure = 1 atm
 let t0: Temperature = 288.15 K
 
@@ -6,4 +9,5 @@ let temperature_gradient = 0.65 K / 100 m
 fn air_pressure(height: Length) -> Pressure =
     p0 · (1 - temperature_gradient · height / t0)^5.255
 
+print(air_pressure(1500 m))
 assert_eq(air_pressure(1500 m), 845.586 hPa, 0.1 hPa)

+ 6 - 1
examples/body_mass_index.nbt

@@ -1,5 +1,10 @@
+# This script calculates the Body Mass Index (BMI) based on
+# the provided mass and height values.
+
 unit BMI: Mass / Length^2 = kg / m^2
 
-fn body_mass_index(mass: Mass, height: Length) = mass / height²
+fn body_mass_index(mass: Mass, height: Length) =
+    mass / height² -> BMI
 
+print(body_mass_index(70 kg, 1.75 m))
 assert_eq(body_mass_index(70 kg, 1.75 m), 22.86 BMI, 0.01 BMI)

+ 5 - 2
examples/medication_dosage.nbt

@@ -1,14 +1,17 @@
+# This script calculates the total daily dose and per intake
+# dose of a medication based on a person's body weight.
+
 @aliases(takings)
 unit taking
 
 let body_weight = 75 kg
-
 let dosage = (60 mg / kg) / day
-
 let frequency = 3 takings / day
 
 let total_daily_dose = dosage * body_weight -> mg / day
+print(total_daily_dose)
 assert_eq(total_daily_dose, 4500 mg/day)
 
 let dose = total_daily_dose / frequency -> mg / taking
+print(dose)
 assert_eq(dose, 1500 mg/taking)

+ 7 - 2
examples/molarity.nbt

@@ -1,9 +1,14 @@
+# This script calculates and prints the molarity of a salt
+# water solution, given a fixed mass of salt (NaCl) and a
+# volume of water.
+
 let molar_mass_NaCl = 58.44 g / mol
 
-fn molarity(mass: Mass, volume: Volume) -> Molarity = (mass / molar_mass_NaCl) / volume
+fn molarity(mass: Mass, volume: Volume) -> Molarity =
+    (mass / molar_mass_NaCl) / volume
 
-# Isotonic saline
 let salt_mass = 9 g
 let water_volume = 1 L
 
+print(molarity(salt_mass, water_volume) -> mmol / l)
 assert_eq(molarity(salt_mass, water_volume), 154 mmol/l, 0.1 mmol/l)

+ 6 - 3
examples/musical_note_frequency.nbt

@@ -4,6 +4,9 @@ let frequency_A4: Frequency = 440 Hz  # the A above middle C, A4
 
 fn note_frequency(n: Scalar) -> Frequency = frequency_A4 * 2^(n / 12)
 
-assert_eq(note_frequency(12), 2 * frequency_A4)  # one octave higher up (A5), double the frequency
-assert_eq(note_frequency(7),  659.255 Hz, 1 mHz)  # E4
-assert_eq(note_frequency(-3), 369.994 Hz, 1 mHz)  # C4
+print(note_frequency(12))  # one octave higher up (A5), 880 Hz
+print(note_frequency(7))   # E4
+print(note_frequency(-3))  # C4
+assert_eq(note_frequency(12), 2 * frequency_A4)
+assert_eq(note_frequency(7),  659.255 Hz, 1 mHz)
+assert_eq(note_frequency(-3), 369.994 Hz, 1 mHz)

+ 6 - 2
examples/pipe_flow_rate.nbt

@@ -1,9 +1,13 @@
+# This script calculates and prints the flow rate in a pipe
+# using the Hagen-Poiseuille equation. It assumes the dynamic
+# viscosity of water and allows for inputs of pipe radius,
+# pipe length, and pressure difference.
+
 dimension DynamicViscosity = Pressure × Time
 dimension FlowRate = Volume / Time
 
 let μ_water: DynamicViscosity = 1 mPa·s
 
-# Hagen-Poiseuille
 fn flow_rate(radius: Length, length: Length, Δp: Pressure) -> FlowRate =
     π × radius^4 × Δp / (8 μ_water × length)
 
@@ -12,5 +16,5 @@ let pipe_length = 10 m
 let Δp = 0.1 bar
 
 let Q: FlowRate = flow_rate(pipe_radius, pipe_length, Δp) -> L/s
-
+print(Q)
 assert_eq(Q, 3.93 L/s, 0.01 L/s)

+ 22 - 0
examples/recipe.nbt

@@ -0,0 +1,22 @@
+# Scale ingredient quantities based on desired servings.
+
+@aliases(servings)
+unit serving
+
+let original_recipe_servings = 2 servings
+let desired_servings = 3 servings
+
+fn scale<D>(quantity: D) -> D =
+    quantity × desired_servings / original_recipe_servings
+
+let milk = 500 ml
+print(scale(milk))
+
+let flour = 250 g
+print(scale(flour))
+
+let sugar = 2 cups
+print(scale(sugar))
+
+let baking_powder = 4 tablespoons
+print(scale(baking_powder))

+ 0 - 14
examples/servings.nbt

@@ -1,14 +0,0 @@
-@aliases(servings)
-unit serving
-
-fn scale_recipe<D>(quantity: D, original_servings: Serving, desired_servings: Serving) -> D =
-    quantity × desired_servings / original_servings
-
-let milk = 500 ml
-let flour = 250 g
-let original_recipe_servings = 2 servings
-
-let desired_servings = 3 servings
-
-assert_eq(scale_recipe(milk,  original_recipe_servings, desired_servings), 750 ml)
-assert_eq(scale_recipe(flour, original_recipe_servings, desired_servings), 375 g)

+ 7 - 2
examples/workhours.nbt

@@ -1,3 +1,6 @@
+# This script shows how to set up practical units for
+# doing workhour and employee-count computations.
+
 @aliases(workhours)
 unit workhour
 
@@ -10,10 +13,12 @@ unit workyear = 200 workdays
 unit FTE = 1 workyear per year
 
 let rate = 1000 € / workday
+print(0.5 million € / rate)
 assert_eq(0.5 million € / rate, 500 workday)
 
-assert_eq(3 FTE * 0.5 years -> workdays, 300 workday)
-
 let budget = 0.5 million €
 let duration = 0.5 year
+print(3 FTE * 0.5 years -> workdays)
+print(budget / rate / duration -> FTE)
+assert_eq(3 FTE * 0.5 years -> workdays, 300 workday)
 assert_eq(budget / rate / duration -> FTE, 5 FTE)