Browse Source

Proper imports, split prelude into multiple files

David Peter 2 years ago
parent
commit
92c057666c

+ 1 - 0
modules/core/scalar.nbt

@@ -0,0 +1 @@
+dimension Scalar = 1

+ 22 - 0
modules/math/constants.nbt

@@ -0,0 +1,22 @@
+### Mathematical constants
+
+let pi = 3.14159265358979323846264338327950288
+let π = pi
+let e = 2.71828182845904523536028747135266250
+
+### Named numbers
+
+let hundred =  100
+let thousand =  1000
+let million =  1000000
+let billion =  10^9
+let trillion =  10^12
+let quadrillion =  10^15
+let quintillion =  10^18
+
+let googol =  10^100
+
+### Misc
+
+let ½ = 1/2
+let ¼ = 1/4

+ 36 - 0
modules/math/functions.nbt

@@ -0,0 +1,36 @@
+### Mathematical functions
+
+fn abs<T>(x: T) -> T
+fn round<T>(x: T) -> T
+fn floor<T>(x: T) -> T
+fn ceil<T>(x: T) -> T
+
+fn sin(x: Scalar) -> Scalar
+fn cos(x: Scalar) -> Scalar
+fn tan(x: Scalar) -> Scalar
+fn asin(x: Scalar) -> Scalar
+fn acos(x: Scalar) -> Scalar
+fn atan(x: Scalar) -> Scalar
+fn atan2<T>(y: T, x: T) -> Scalar
+
+fn sinh(x: Scalar) -> Scalar
+fn cosh(x: Scalar) -> Scalar
+fn tanh(x: Scalar) -> Scalar
+fn asinh(x: Scalar) -> Scalar
+fn acosh(x: Scalar) -> Scalar
+fn atanh(x: Scalar) -> Scalar
+
+fn exp(x: Scalar) -> Scalar
+fn ln(x: Scalar) -> Scalar
+fn log(x: Scalar) -> Scalar = ln(x)
+fn log10(x: Scalar) -> Scalar
+fn log2(x: Scalar) -> Scalar
+
+fn sqrt<D>(x: D^2) -> D = x^(1/2)
+fn sqr<D>(x: D) -> D^2 = x^2
+
+### Statistics functions
+
+fn mean<D>(xs: D…) -> D
+fn maximum<D>(xs: D…) -> D
+fn minimum<D>(xs: D…) -> D

+ 8 - 0
modules/physics/constants.nbt

@@ -0,0 +1,8 @@
+### Physics constants
+
+# TODO: add more constants, split into multiple modules
+
+let gravity = 9.80665 m / s^2
+let speed_of_light = 299792458 m / s
+let planck_constant = 6.62607015e-34 J / Hz
+let ℏ = planck_constant / 2π

+ 27 - 0
modules/physics/dimensions.nbt

@@ -0,0 +1,27 @@
+### Physical dimensions
+
+dimension Length
+dimension Area = Length^2
+dimension Volume = Length^3
+
+dimension Time
+dimension Frequency = 1 / Time
+dimension Speed = Length / Time
+dimension Acceleration = Length / Time^2
+
+dimension Mass
+dimension Momentum = Mass × Speed
+dimension Force = Mass × Acceleration = Momentum / Time
+dimension Energy = Momentum^2 / Mass = Mass × Speed^2 = Force × Length
+dimension Power = Energy / Time = Force × Speed
+dimension Pressure = Force / Area = Energy / Volume
+dimension Action = Energy × Time
+dimension MassDensity = Mass / Length^3
+
+dimension Current
+dimension Charge = Current × Time
+dimension Voltage = Energy / Charge = Power / Current
+dimension Capacitance = Charge / Voltage
+dimension Resistance = Voltage / Current
+
+dimension Temperature

+ 12 - 0
modules/physics/temperature.nbt

@@ -0,0 +1,12 @@
+### Temperature conversion functions K <-> °C and K <-> °F
+
+let offset_celsius = 273.15
+
+fn fromCelsius(t_celsius: Scalar) -> Temperature = (t_celsius + offset_celsius) kelvin
+fn toCelsius(t_kelvin: Temperature) -> Scalar = t_kelvin / kelvin - offset_celsius
+
+let offset_fahrenheit = 459.67
+let scale_fahrenheit = 5 / 9
+
+fn fromFahrenheit(t_fahrenheit: Scalar) -> Temperature = ((t_fahrenheit + offset_fahrenheit) × scale_fahrenheit) kelvin
+fn toFahrenheit(t_kelvin: Temperature) -> Scalar = (t_kelvin / kelvin) / scale_fahrenheit - offset_fahrenheit

+ 37 - 0
modules/physics/units/misc.nbt

@@ -0,0 +1,37 @@
+### Other units
+
+dimension Bit
+
+@metric_prefixes
+@binary_prefixes
+@aliases(bit: both, bits: both)
+unit bit: Bit
+
+@metric_prefixes
+@binary_prefixes
+@aliases(B: short, byte: both, bytes: both, octet, octets)
+unit byte: Bit = 8 bit
+
+@metric_prefixes
+@aliases(eV: short)
+unit electronvolt = 1.602176634e-19 J
+
+@metric_prefixes
+@aliases(Wh: short)
+unit Wh = W h
+
+@aliases(knots)
+unit knot: Speed = 463 m / 900 s
+
+### Useful 'dummy' units
+
+dimension Pixel
+
+@metric_prefixes
+@aliases(pixels, px: short)
+unit pixel: Pixel
+
+### Abbreviations
+
+unit mph = miles per hour
+unit kph = kilometer per hour

+ 70 - 0
modules/physics/units/si.nbt

@@ -0,0 +1,70 @@
+### Base SI units
+
+@metric_prefixes
+@aliases(meters, metre, metres, m: short)
+unit meter: Length
+
+@metric_prefixes
+@aliases(seconds, s: short, sec: none)
+unit second: Time
+
+@metric_prefixes
+@aliases(grams, gramme, grammes, g: short)
+unit gram: Mass
+
+@metric_prefixes
+@aliases(Hz: short)
+unit hertz: Frequency = 1 / second
+
+@metric_prefixes
+@aliases(newtons, N: short)
+unit newton: Force = kilogram meter / second^2
+
+@metric_prefixes
+@aliases(joules, J: short)
+unit joule: Energy = newton meter
+
+@metric_prefixes
+@aliases(watts, W: short)
+unit watt: Power = joule / second
+
+@metric_prefixes
+@aliases(pascals, Pa: short)
+unit pascal: Pressure = newton / meter^2
+
+@metric_prefixes
+@aliases(amperes, A: short)
+unit ampere: Current
+
+@metric_prefixes
+@aliases(coulombs, C: short)
+unit coulomb: Charge = ampere second
+
+@metric_prefixes
+@aliases(volts, V: short)
+unit volt: Voltage = kilogram meter^2 / (second^3 ampere)
+
+@metric_prefixes
+@aliases(farads, F: short)
+unit farad: Capacitance = coulomb / volt
+
+@metric_prefixes
+@aliases(ohms, Ω: short)
+unit ohm: Resistance = volt / ampere
+
+@metric_prefixes
+@aliases(kelvins, K: short)
+unit kelvin: Temperature
+
+### SI accepted units
+
+@metric_prefixes
+@aliases(radians, rad: short)
+unit radian = meter / meter
+
+@aliases(degrees, deg, °: short)
+unit degree = π radian / 180
+
+@metric_prefixes
+@aliases(litres, liter, liters, l: short, L: short)
+unit litre = 1 dm^3

+ 19 - 0
modules/physics/units/time.nbt

@@ -0,0 +1,19 @@
+### Time units
+
+@aliases(minutes, min: short)
+unit minute: Time = 60 seconds
+
+@aliases(hours, h: short)
+unit hour: Time = 60 minutes
+
+@aliases(days)
+unit day: Time = 24 hours
+
+@aliases(weeks)
+unit week: Time = 7 days
+
+@aliases(years)
+unit year: Time = 365.2425 days
+
+@aliases(months)
+unit month: Time = year / 12

+ 26 - 0
modules/physics/units/us_customary.nbt

@@ -0,0 +1,26 @@
+### US customary units
+
+@aliases(inches, in: short)
+unit inch: Length = 0.0254 meter
+
+@aliases(feet, ft: short)
+unit foot: Length = 0.3048 meter
+assert_eq(1 foot, 12 inches, 0.001 inch)  # TODO: move those assertions somewhere else
+
+@aliases(yards, yd: short)
+unit yard: Length = 0.9144 meter
+assert_eq(1 yard, 3 feet, 0.001 foot)
+
+@aliases(miles, mi: short)
+unit mile: Length = 1609.344 meter
+assert_eq(1 mile, 1760 yard, 0.001 yard)
+
+@aliases(gallons, gal: short)
+unit gallon: Volume = 0.003785411784 meter^3
+assert_eq(1 gallon, 231 inch^3, 0.001 inch^3)
+
+@aliases(fathoms)
+unit fathom: Length = 2 yard
+
+@aliases(furlongs)
+unit furlong: Length = 220 yard

+ 12 - 0
modules/prelude.nbt

@@ -0,0 +1,12 @@
+use core::scalar
+
+use math::constants
+use math::functions
+
+use physics::dimensions
+use physics::units::si
+use physics::units::time
+use physics::units::us_customary
+use physics::units::misc
+use physics::constants
+use physics::temperature

+ 5 - 3
numbat-cli/src/main.rs

@@ -72,15 +72,17 @@ struct NumbatHelper {
 
 struct Cli {
     args: Args,
-    numbat: Context,
+    context: Context,
     current_filename: Option<PathBuf>,
 }
 
 impl Cli {
     fn new() -> Self {
         let args = Args::parse();
+        let mut context = Context::new_without_prelude(args.debug);
+        context.add_module_path("/home/ped1st/software/numbat/modules"); // TODO
         Self {
-            numbat: Context::new_without_prelude(args.debug),
+            context,
             args,
             current_filename: None,
         }
@@ -201,7 +203,7 @@ impl Cli {
         execution_mode: ExecutionMode,
         pretty_print: bool,
     ) -> ControlFlow {
-        let result = self.numbat.interpret(input);
+        let result = self.context.interpret(input);
 
         match result {
             Ok((statements, interpreter_result)) => {

+ 20 - 6
numbat/src/lib.rs

@@ -25,10 +25,14 @@ mod unit;
 mod unit_registry;
 mod vm;
 
+use std::path::Path;
+use std::path::PathBuf;
+
 use bytecode_interpreter::BytecodeInterpreter;
 use interpreter::{Interpreter, RuntimeError};
 use name_resolution::NameResolutionError;
 use prefix_transformer::Transformer;
+use resolver::FileSystemImporter;
 use resolver::NullImporter;
 use resolver::Resolver;
 use resolver::ResolverError;
@@ -60,6 +64,7 @@ pub struct Context {
     prefix_transformer: Transformer,
     typechecker: TypeChecker,
     interpreter: BytecodeInterpreter,
+    module_paths: Vec<PathBuf>,
 }
 
 impl Context {
@@ -68,21 +73,30 @@ impl Context {
             prefix_transformer: Transformer::new(),
             typechecker: TypeChecker::default(),
             interpreter: BytecodeInterpreter::new(debug),
+            module_paths: vec![],
         }
     }
 
     pub fn new(debug: bool) -> Self {
-        let mut numbat = Self::new_without_prelude(debug);
-        assert!(numbat
-            .interpret(include_str!("../../prelude.nbt"))
+        let mut context = Self::new_without_prelude(debug);
+
+        let module_path = Path::new("/home/ped1st/software/numbat/modules");
+        context.add_module_path(module_path); // TODO
+
+        assert!(context
+            .interpret(&std::fs::read_to_string(module_path.join("prelude.nbt")).unwrap())
             .expect("Error while running prelude")
             .1
-            .is_success()); // TODO: read prelude dynamically, error handling
-        numbat
+            .is_success()); // TODO: error handling
+        context
+    }
+
+    pub fn add_module_path<P: AsRef<Path>>(&mut self, path: P) {
+        self.module_paths.push(path.as_ref().to_path_buf());
     }
 
     pub fn interpret(&mut self, code: &str) -> Result<(Vec<Statement>, InterpreterResult)> {
-        let importer = NullImporter {};
+        let importer = FileSystemImporter::new(&self.module_paths[..]);
         let resolver = Resolver::new(&importer);
 
         let statements = resolver.resolve(code).map_err(|e| match e {

+ 40 - 1
numbat/src/resolver.rs

@@ -1,4 +1,9 @@
-use crate::{ast::Statement, parser::parse, ParseError};
+use std::{
+    fs,
+    path::{Path, PathBuf},
+};
+
+use crate::{ast::Statement, name_resolution::NameResolutionError, parser::parse, ParseError};
 
 use thiserror::Error;
 
@@ -85,6 +90,40 @@ impl ModuleImporter for NullImporter {
     }
 }
 
+pub struct FileSystemImporter {
+    root_paths: Vec<PathBuf>,
+}
+
+impl FileSystemImporter {
+    pub fn new<P: AsRef<Path>>(root_paths: &[P]) -> Self {
+        Self {
+            root_paths: root_paths
+                .iter()
+                .map(|p| p.as_ref().to_path_buf())
+                .collect(),
+        }
+    }
+}
+
+impl ModuleImporter for FileSystemImporter {
+    fn import(&self, module_path: &ModulePath) -> Option<String> {
+        for path in &self.root_paths {
+            let mut path = path.clone();
+            for part in &module_path.0 {
+                path = path.join(part);
+            }
+
+            path.set_extension("nbt");
+
+            if let Ok(code) = fs::read_to_string(path) {
+                return Some(code);
+            }
+        }
+
+        None
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::{

+ 7 - 18
numbat/tests/prelude_and_examples.rs

@@ -4,7 +4,7 @@ use std::ffi::OsStr;
 use std::fs;
 
 fn assert_typechecks_and_runs(code: &str) {
-    let result = Context::new_without_prelude(false).interpret(code);
+    let result = Context::new(false).interpret(code);
     assert!(result.is_ok());
     assert!(matches!(
         result.unwrap().1,
@@ -14,40 +14,33 @@ fn assert_typechecks_and_runs(code: &str) {
 
 fn assert_parse_error(code: &str) {
     assert!(matches!(
-        Context::new_without_prelude(false).interpret(code),
+        Context::new(false).interpret(code),
         Err(NumbatError::ParseError(_))
     ));
 }
 
 fn assert_name_resolution_error(code: &str) {
     assert!(matches!(
-        Context::new_without_prelude(false).interpret(code),
+        Context::new(false).interpret(code),
         Err(NumbatError::NameResolutionError(_))
     ));
 }
 
 fn assert_typecheck_error(code: &str) {
     assert!(matches!(
-        Context::new_without_prelude(false).interpret(code),
+        Context::new(false).interpret(code),
         Err(NumbatError::TypeCheckError(_))
     ));
 }
 
 fn assert_runtime_error(code: &str) {
     assert!(matches!(
-        Context::new_without_prelude(false).interpret(code),
+        Context::new(false).interpret(code),
         Err(NumbatError::RuntimeError(_))
     ));
 }
 
-#[test]
-fn prelude_can_be_parsed_and_interpreted() {
-    let prelude_code = fs::read_to_string("../prelude.nbt").unwrap();
-    assert_typechecks_and_runs(&prelude_code);
-}
-
 fn run_for_each_numbat_file_in(folder: &str, f: impl Fn(&str)) {
-    let prelude_code = fs::read_to_string("../prelude.nbt").unwrap();
     for entry in fs::read_dir(folder).unwrap() {
         let path = entry.unwrap().path();
         if path.extension() != Some(OsStr::new("nbt")) {
@@ -56,12 +49,8 @@ fn run_for_each_numbat_file_in(folder: &str, f: impl Fn(&str)) {
 
         println!("Testing example {example:?}", example = path);
         let example_code = fs::read_to_string(path).unwrap();
-        f(&format!(
-            "{prelude}\n\
-             {example}",
-            prelude = prelude_code,
-            example = example_code
-        ));
+
+        f(&example_code);
     }
 }
 

+ 0 - 263
prelude.nbt

@@ -1,263 +0,0 @@
-### Mathematical constants
-
-let pi = 3.14159265358979323846264338327950288
-let π = pi
-let e = 2.71828182845904523536028747135266250
-let ½ = 1/2
-let ¼ = 1/4
-
-### Named numbers
-
-let hundred =  100
-let thousand =  1000
-let million =  1000000
-let billion =  10^9
-let trillion =  10^12
-let quadrillion =  10^15
-let quintillion =  10^18
-
-let googol =  10^100
-
-
-### Physical dimensions
-
-dimension Scalar = 1
-
-dimension Length
-dimension Area = Length^2
-dimension Volume = Length^3
-
-dimension Time
-dimension Frequency = 1 / Time
-dimension Speed = Length / Time
-dimension Acceleration = Length / Time^2
-
-dimension Mass
-dimension Momentum = Mass × Speed
-dimension Force = Mass × Acceleration = Momentum / Time
-dimension Energy = Momentum^2 / Mass = Mass × Speed^2 = Force × Length
-dimension Power = Energy / Time = Force × Speed
-dimension Pressure = Force / Area = Energy / Volume
-dimension Action = Energy × Time
-dimension MassDensity = Mass / Length^3
-
-dimension Current
-dimension Charge = Current × Time
-dimension Voltage = Energy / Charge = Power / Current
-dimension Capacitance = Charge / Voltage
-dimension Resistance = Voltage / Current
-
-dimension Temperature
-
-### SI Units
-
-@metric_prefixes
-@aliases(meters, metre, metres, m: short)
-unit meter: Length
-
-@metric_prefixes
-@aliases(seconds, s: short, sec: none)
-unit second: Time
-
-@metric_prefixes
-@aliases(grams, gramme, grammes, g: short)
-unit gram: Mass
-
-@metric_prefixes
-@aliases(Hz: short)
-unit hertz: Frequency = 1 / second
-
-@metric_prefixes
-@aliases(newtons, N: short)
-unit newton: Force = kilogram meter / second^2
-
-@metric_prefixes
-@aliases(joules, J: short)
-unit joule: Energy = newton meter
-
-@metric_prefixes
-@aliases(watts, W: short)
-unit watt: Power = joule / second
-
-@metric_prefixes
-@aliases(pascals, Pa: short)
-unit pascal: Pressure = newton / meter^2
-
-@metric_prefixes
-@aliases(amperes, A: short)
-unit ampere: Current
-
-@metric_prefixes
-@aliases(coulombs, C: short)
-unit coulomb: Charge = ampere second
-
-@metric_prefixes
-@aliases(volts, V: short)
-unit volt: Voltage = kilogram meter^2 / (second^3 ampere)
-
-@metric_prefixes
-@aliases(farads, F: short)
-unit farad: Capacitance = coulomb / volt
-
-@metric_prefixes
-@aliases(ohms, Ω: short)
-unit ohm: Resistance = volt / ampere
-
-@metric_prefixes
-@aliases(kelvins, K: short)
-unit kelvin: Temperature
-
-### SI accepted units
-
-@metric_prefixes
-@aliases(radians, rad: short)
-unit radian = meter / meter
-
-@aliases(degrees, deg, °: short)
-unit degree = π radian / 180
-
-@metric_prefixes
-@aliases(litres, liter, liters, l: short, L: short)
-unit litre = 1 dm^3
-
-### Time units
-
-@aliases(minutes, min: short)
-unit minute: Time = 60 seconds
-
-@aliases(hours, h: short)
-unit hour: Time = 60 minutes
-
-@aliases(days)
-unit day: Time = 24 hours
-
-@aliases(weeks)
-unit week: Time = 7 days
-
-@aliases(years)
-unit year: Time = 365.2425 days
-
-@aliases(months)
-unit month: Time = year / 12
-
-### Other units
-
-dimension Bit
-
-@metric_prefixes
-@binary_prefixes
-@aliases(bit: both, bits: both)
-unit bit: Bit
-
-@metric_prefixes
-@binary_prefixes
-@aliases(B: short, byte: both, bytes: both, octet, octets)
-unit byte: Bit = 8 bit
-
-@metric_prefixes
-@aliases(eV: short)
-unit electronvolt = 1.602176634e-19 J
-
-@metric_prefixes
-@aliases(Wh: short)
-unit Wh = W h
-
-@aliases(knots)
-unit knot: Speed = 463 m / 900 s
-
-### Useful 'dummy' units
-
-dimension Pixel
-
-@metric_prefixes
-@aliases(pixels, px: short)
-unit pixel: Pixel
-
-### Imperial units
-
-@aliases(inches, in: short)
-unit inch: Length = 0.0254 meter
-
-@aliases(feet, ft: short)
-unit foot: Length = 0.3048 meter
-assert_eq(1 foot, 12 inches, 0.001 inch)  # TODO: move those assertions somewhere else
-
-@aliases(yards, yd: short)
-unit yard: Length = 0.9144 meter
-assert_eq(1 yard, 3 feet, 0.001 foot)
-
-@aliases(miles, mi: short)
-unit mile: Length = 1609.344 meter
-assert_eq(1 mile, 1760 yard, 0.001 yard)
-
-@aliases(gallons, gal: short)
-unit gallon: Volume = 0.003785411784 meter^3
-assert_eq(1 gallon, 231 inch^3, 0.001 inch^3)
-
-@aliases(fathoms)
-unit fathom: Length = 2 yard
-
-@aliases(furlongs)
-unit furlong: Length = 220 yard
-
-### Commonly used non-standard abbreviations
-
-unit mph = miles per hour
-unit kph = kilometer per hour
-
-### Physical constants
-
-let gravity = 9.80665 m / s^2
-let speed_of_light = 299792458 m / s
-let planck_constant = 6.62607015e-34 J / Hz
-let ℏ = planck_constant / 2π
-
-### Mathematical functions
-
-fn abs<T>(x: T) -> T
-fn round<T>(x: T) -> T
-fn floor<T>(x: T) -> T
-fn ceil<T>(x: T) -> T
-
-fn sin(x: Scalar) -> Scalar
-fn cos(x: Scalar) -> Scalar
-fn tan(x: Scalar) -> Scalar
-fn asin(x: Scalar) -> Scalar
-fn acos(x: Scalar) -> Scalar
-fn atan(x: Scalar) -> Scalar
-fn atan2<T>(y: T, x: T) -> Scalar
-
-fn sinh(x: Scalar) -> Scalar
-fn cosh(x: Scalar) -> Scalar
-fn tanh(x: Scalar) -> Scalar
-fn asinh(x: Scalar) -> Scalar
-fn acosh(x: Scalar) -> Scalar
-fn atanh(x: Scalar) -> Scalar
-
-fn exp(x: Scalar) -> Scalar
-fn ln(x: Scalar) -> Scalar
-fn log(x: Scalar) -> Scalar = ln(x)
-fn log10(x: Scalar) -> Scalar
-fn log2(x: Scalar) -> Scalar
-
-fn sqrt<D>(x: D^2) -> D = x^(1/2)
-fn sqr<D>(x: D) -> D^2 = x^2
-
-### Statistics functions
-
-fn mean<D>(xs: D…) -> D
-fn maximum<D>(xs: D…) -> D
-fn minimum<D>(xs: D…) -> D
-
-### Physics functions
-
-let offset_celsius = 273.15
-
-fn fromCelsius(t_celsius: Scalar) -> Temperature = (t_celsius + offset_celsius) kelvin
-fn toCelsius(t_kelvin: Temperature) -> Scalar = t_kelvin / kelvin - offset_celsius
-
-let offset_fahrenheit = 459.67
-let scale_fahrenheit = 5 / 9
-
-fn fromFahrenheit(t_fahrenheit: Scalar) -> Temperature = ((t_fahrenheit + offset_fahrenheit) × scale_fahrenheit) kelvin
-fn toFahrenheit(t_kelvin: Temperature) -> Scalar = (t_kelvin / kelvin) / scale_fahrenheit - offset_fahrenheit