diff --git a/TODO.md b/TODO.md index 076d365..f178de0 100644 --- a/TODO.md +++ b/TODO.md @@ -60,7 +60,6 @@ - Show base units on error ## Units - - Abbreviations: kWh, bps, fps, dot/in, px/in, parts-per-x(ppm, ppb, ppt, ppq), percent(pct) - Selective prefixes: tonne (k M G), Byte (TiB, etc), calorie (kcal) - Print units with powers instead of / - HMS for degrees diff --git a/buildscript/constants.rs b/buildscript/constants.rs new file mode 100644 index 0000000..add75c0 --- /dev/null +++ b/buildscript/constants.rs @@ -0,0 +1,110 @@ +use std::io::Write; +use std::fs::OpenOptions; +use std::path::Path; +use toml::Table; + + + +pub fn write(target: &Path) { + let constants = include_str!("constants.toml").parse::().unwrap(); + let toml::Value::Array(constants) = &constants["constant"] else {panic!()}; + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(target) + .unwrap(); + + + writeln!(file, + concat!( + "#[derive(Debug)]\n", + "#[derive(Copy, Clone)]\n", + "pub enum Constant {{" + ) + ).unwrap(); + + for c in constants { + writeln!(file, + "\t{},", + c["enum_name"].as_str().unwrap() + ).unwrap(); + } + + writeln!(file, "}}\n").unwrap(); + + // ToString + writeln!(file, + concat!( + "impl ToString for Constant {{\n", + "\tfn to_string(&self) -> String {{\n", + "\t\tString::from(match self {{" + ) + ).unwrap(); + + for c in constants { + if c["strings"].is_array() { + writeln!(file, + "\t\t\tConstant::{e} => \"{s}\",", + e = c["enum_name"].as_str().unwrap(), + s = c["strings"].as_array().unwrap()[0].as_str().unwrap() + ).unwrap(); + } else { + writeln!(file, + "\t\t\tConstant::{e} => \"{s}\",", + e = c["enum_name"].as_str().unwrap(), + s = c["strings"].as_str().unwrap() + ).unwrap(); + } + } + + writeln!(file, "\t\t}})\n\t}}\n}}\n").unwrap(); + + + writeln!(file, + concat!( + "impl Constant {{\n", + "\tpub fn from_string(s: &str) -> Option {{\n", + "\t\tmatch s {{" + ) + ).unwrap(); + + for c in constants { + if c["strings"].is_array() { + for s in c["strings"].as_array().unwrap() { + writeln!(file, + "\t\t\t\"{s}\" => Some(Constant::{e}),", + e = c["enum_name"].as_str().unwrap(), + s = s.as_str().unwrap() + ).unwrap(); + } + } else { + writeln!(file, + "\t\t\t\"{s}\" => Some(Constant::{e}),", + e = c["enum_name"].as_str().unwrap(), + s = c["strings"].as_str().unwrap() + ).unwrap(); + } + } + + writeln!(file, "\t\t\t_ => None\n\t\t}}\n\t}}\n").unwrap(); + + + writeln!(file, + concat!( + "\tpub fn value(&self) -> Token {{\n", + "\t\tmatch self {{" + ) + ).unwrap(); + + for c in constants { + writeln!(file, + "\t\t\tConstant::{e} => parse(&String::from(\"{s}\")).unwrap(),", + e = c["enum_name"].as_str().unwrap(), + s = c["value"].as_str().unwrap() + ).unwrap(); + } + + writeln!(file, "\t\t}}\n\t}}\n}}").unwrap(); +} \ No newline at end of file diff --git a/buildscript/constants.toml b/buildscript/constants.toml new file mode 100644 index 0000000..3bfa992 --- /dev/null +++ b/buildscript/constants.toml @@ -0,0 +1,76 @@ + +[[constant]] +enum_name = "Pi" +strings = ["π", "pi"] +value = "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067" + +[[constant]] +enum_name = "Phi" +strings = ["φ", "phi"] +value = "1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137" + +[[constant]] +enum_name = "Euler" +strings = "e" +value = "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427" + +[[constant]] +enum_name = "MPG" +strings = "mpg" +value = "mile/gallon" +unit = true + + +[[constant]] +enum_name = "MPH" +strings = "mph" +value = "mile/hour" +unit = true + +[[constant]] +enum_name = "DPI" +strings = "dpi" +value = "dot/inch" +unit = true + +[[constant]] +enum_name = "PPI" +strings = "ppi" +value = "pixel/inch" +unit = true + +[[constant]] +enum_name = "FPS" +strings = "fps" +value = "frame/second" +unit = true + +[[constant]] +enum_name = "PCT" +strings = "pct" +value = "0.01" +unit = true + +[[constant]] +enum_name = "PPM" +strings = "ppm" +value = "1e-6" +unit = true + +[[constant]] +enum_name = "PPB" +strings = "ppb" +value = "1e-9" +unit = true + +[[constant]] +enum_name = "PPT" +strings = "ppt" +value = "1e-12" +unit = true + +[[constant]] +enum_name = "PPQ" +strings = "ppq" +value = "1e-15" +unit = true \ No newline at end of file diff --git a/buildscript/main.rs b/buildscript/main.rs index 579cb84..6c9e40f 100644 --- a/buildscript/main.rs +++ b/buildscript/main.rs @@ -1,278 +1,21 @@ use std::env; -use std::io::Write; -use std::fs::OpenOptions; -use std::fs::File; use std::path::Path; -use toml::Table; -use toml::Value; - - - - -/// Create WholeUnit enum with -/// basic impls. Should only be run once. -fn write_wholeunit_main(mut file: &File, units: &Vec) { - writeln!(file, - concat!( - "#[derive(Hash)]\n", - "#[derive(Debug)]\n", - "#[derive(Copy, Clone)]\n", - "#[derive(Eq, PartialEq)]\n", - "pub enum WholeUnit {{" - ) - ).unwrap(); - - for u in units { - writeln!(file, - "\t{},", - u["enum_name"].as_str().unwrap() - ).unwrap(); - } - - writeln!(file, "}}\n").unwrap(); - - // ToString - writeln!(file, - concat!( - "impl ToString for WholeUnit {{\n", - "\tfn to_string(&self) -> String {{\n", - "\t\tString::from(match self {{" - ) - ).unwrap(); - - for u in units { - writeln!(file, - "\t\t\tWholeUnit::{e} => \"{s}\",", - s = u["print"].as_str().unwrap(), - e = u["enum_name"].as_str().unwrap() - ).unwrap(); - } - - writeln!(file, "\t\t}})\n\t}}\n}}\n").unwrap(); - - - // Properties - writeln!(file, - concat!( - "impl WholeUnit {{\n", - "\tfn no_space(&self) -> bool {{\n", - "\t\tmatch self {{" - ) - ).unwrap(); - - for u in units { - if u.as_table().unwrap().contains_key("no_space") { - if u.as_table().unwrap()["no_space"].as_bool().unwrap() { - writeln!(file, - "\t\t\tWholeUnit::{} => true,", - u["enum_name"].as_str().unwrap() - ).unwrap(); - } - } - } - - writeln!(file, "\t\t\t_ => false\n\t\t}}\n\t}}\n}}").unwrap(); -} - - -/// Create WholeUnit::base_factor(). -/// Should only be run once. -fn write_wholeunit_base_factor(mut file: &File, units: &Vec) { - writeln!(file, - concat!( - "impl WholeUnit {{\n", - "\tfn base_factor(&self) -> Option {{\n", - "\t\tmatch self {{" - ) - ).unwrap(); - - for u in units { - - - if { // Base units should return None - u.as_table().unwrap().contains_key("base") && - u["base"].as_bool().unwrap() - } { - writeln!(file, - "\t\t\tWholeUnit::{} => None,", - u["enum_name"].as_str().unwrap() - ).unwrap(); - continue - } - - - writeln!(file, - "\t\t\tWholeUnit::{} => Some(Quantity{{", - u["enum_name"].as_str().unwrap() - ).unwrap(); - - match u["base_value_type"].as_str().unwrap() { - "exact" => { - writeln!(file, - "\t\t\t\tscalar: Scalar::new_rational_from_string(\"{}\").unwrap(),", - u["base_value"].as_str().unwrap(), - ).unwrap(); - }, - - "fract" => { - writeln!(file, - "\t\t\t\tscalar: Scalar::new_rational_from_frac({}, {}).unwrap(),", - u["base_value"].as_array().unwrap()[0].as_integer().unwrap(), - u["base_value"].as_array().unwrap()[1].as_integer().unwrap(), - ).unwrap(); - }, - - "approx" => { - writeln!(file, - "\t\t\t\tscalar: Scalar::new_float_from_string(\"{}\").unwrap(),", - u["base_value"].as_str().unwrap(), - ).unwrap(); - }, - - _ => panic!() - }; - - writeln!(file, - concat!( - "\t\t\t\tunit: Unit::from_array(&[\n", - "\t\t\t\t\t(FreeUnit{{whole: WholeUnit::{}, prefix: Prefix::None}}, Scalar::new_rational(-1f64).unwrap()),", - ), - u["enum_name"].as_str().unwrap() - ).unwrap(); - - for b in u["base_units"].as_array().unwrap() { - writeln!(file, - "\t\t\t\t\t(FreeUnit{{whole: WholeUnit::{u}, prefix: Prefix::None}}, Scalar::new_rational({p}f64).unwrap()),", - u = b.as_table().unwrap()["u"].as_str().unwrap(), - p = b.as_table().unwrap()["p"].as_integer().unwrap(), - ).unwrap(); - } - - writeln!(file, - concat!( - "\t\t\t\t])\n", - "\t\t\t}})," - ), - ).unwrap(); - } - - writeln!(file, "\t\t}}\n\t}}\n}}").unwrap(); -} - - -/// Write all SI prefixes. -/// Used inside freeunit_from_string(). -fn prefix_si(mut file: &File, enum_name: &str, s: &str) { - writeln!(file, - concat!( - "\t\t", "\"{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::None}}),\n", - "\t\t", "\"Q{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Quetta}}),\n", - "\t\t", "\"R{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Ronna}}),\n", - "\t\t", "\"Y{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Yotta}}),\n", - "\t\t", "\"Z{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Zetta}}),\n", - "\t\t", "\"E{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Exa}}),\n", - "\t\t", "\"P{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Peta}}),\n", - "\t\t", "\"T{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Tera}}),\n", - "\t\t", "\"G{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Giga}}),\n", - "\t\t", "\"M{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Mega}}),\n", - "\t\t", "\"k{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Kilo}}),\n", - "\t\t", "\"h{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Hecto}}),\n", - "\t\t", "\"da{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Deka}}),\n", - "\t\t", "\"d{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Deci}}),\n", - "\t\t", "\"c{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Centi}}),\n", - "\t\t", "\"m{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Milli}}),\n", - "\t\t", "\"u{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Micro}}),\n", - "\t\t", "\"n{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Nano}}),\n", - "\t\t", "\"p{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Pico}}),\n", - "\t\t", "\"f{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Femto}}),\n", - "\t\t", "\"a{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Atto}}),\n", - "\t\t", "\"z{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Zepto}}),\n", - "\t\t", "\"y{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Yocto}}),\n", - "\t\t", "\"r{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Ronto}}),\n", - "\t\t", "\"q{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Quecto}}),", - ), - e = enum_name, - s = s - ).unwrap(); -} - - -/// Create freeunit_from_string(). -/// Should only be run once. -fn write_freeunit_from_string(mut file: &File, units: &Vec) { - writeln!(file, - concat!( - "#[inline(always)]\n", - "fn freeunit_from_string(s: &str) -> Option {{\n", - "\tmatch s {{" - ), - ).unwrap(); - - for u in units { - if u.as_table().unwrap().contains_key("parse") { - for s in u["parse"].as_array().unwrap() { - writeln!(file, - "\t\t\"{}\" => Some(FreeUnit{{whole: WholeUnit::{}, prefix: Prefix::None}}),", - s.as_str().unwrap(), - u["enum_name"].as_str().unwrap() - ).unwrap(); - } - } - - - if u.as_table().unwrap().contains_key("parse_with_prefix") { - if u.as_table().unwrap()["parse_with_prefix"].is_array() { - for p in u["parse_with_prefix"].as_array().unwrap() { - prefix_si( - &file, - u["enum_name"].as_str().unwrap(), - p.as_str().unwrap() - ); - } - } else { - prefix_si( - &file, - u["enum_name"].as_str().unwrap(), - u["parse_with_prefix"].as_str().unwrap() - ); - } - } - - writeln!(file, "").unwrap(); - } - - writeln!(file, "\t\t_ => None\n\t}}\n}}").unwrap(); -} - +mod units; +mod constants; fn main() -> Result<(), ()>{ let out_dir = env::var_os("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("units.rs"); println!("cargo:rerun-if-changed=buildscript/build.rs"); + println!("cargo:rerun-if-changed=buildscript/constants.rs"); + println!("cargo:rerun-if-changed=buildscript/units.rs"); println!("cargo:rerun-if-changed=buildscript/units.toml"); + println!("cargo:rerun-if-changed=buildscript/constants.toml"); - let units = include_str!("units.toml").parse::
().unwrap(); - let toml::Value::Array(units) = &units["unit"] else {panic!()}; - - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(dest_path) - .unwrap(); - - - - write_wholeunit_main(&file, units); - writeln!(file, "\n\n").unwrap(); - - write_wholeunit_base_factor(&file, units); - writeln!(file, "\n\n").unwrap(); - - write_freeunit_from_string(&file, units); + units::write(&Path::new(&out_dir).join("units.rs")); + constants::write(&Path::new(&out_dir).join("constants.rs")); return Ok(()); } \ No newline at end of file diff --git a/buildscript/units.rs b/buildscript/units.rs new file mode 100644 index 0000000..3c73721 --- /dev/null +++ b/buildscript/units.rs @@ -0,0 +1,264 @@ +use std::path::Path; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Write; +use toml::Table; +use toml::Value; + + + +/// Create WholeUnit enum with +/// basic impls. Should only be run once. +fn write_wholeunit_main(mut file: &File, units: &Vec) { + writeln!(file, + concat!( + "#[derive(Hash)]\n", + "#[derive(Debug)]\n", + "#[derive(Copy, Clone)]\n", + "#[derive(Eq, PartialEq)]\n", + "pub enum WholeUnit {{" + ) + ).unwrap(); + + for u in units { + writeln!(file, + "\t{},", + u["enum_name"].as_str().unwrap() + ).unwrap(); + } + + writeln!(file, "}}\n").unwrap(); + + // ToString + writeln!(file, + concat!( + "impl ToString for WholeUnit {{\n", + "\tfn to_string(&self) -> String {{\n", + "\t\tString::from(match self {{" + ) + ).unwrap(); + + for u in units { + writeln!(file, + "\t\t\tWholeUnit::{e} => \"{s}\",", + s = u["print"].as_str().unwrap(), + e = u["enum_name"].as_str().unwrap() + ).unwrap(); + } + + writeln!(file, "\t\t}})\n\t}}\n}}\n").unwrap(); + + + // Properties + writeln!(file, + concat!( + "impl WholeUnit {{\n", + "\tfn no_space(&self) -> bool {{\n", + "\t\tmatch self {{" + ) + ).unwrap(); + + for u in units { + if u.as_table().unwrap().contains_key("no_space") { + if u.as_table().unwrap()["no_space"].as_bool().unwrap() { + writeln!(file, + "\t\t\tWholeUnit::{} => true,", + u["enum_name"].as_str().unwrap() + ).unwrap(); + } + } + } + + writeln!(file, "\t\t\t_ => false\n\t\t}}\n\t}}\n}}").unwrap(); +} + + +/// Create WholeUnit::base_factor(). +/// Should only be run once. +fn write_wholeunit_base_factor(mut file: &File, units: &Vec) { + writeln!(file, + concat!( + "impl WholeUnit {{\n", + "\tfn base_factor(&self) -> Option {{\n", + "\t\tmatch self {{" + ) + ).unwrap(); + + for u in units { + + + if { // Base units should return None + u.as_table().unwrap().contains_key("base") && + u["base"].as_bool().unwrap() + } { + writeln!(file, + "\t\t\tWholeUnit::{} => None,", + u["enum_name"].as_str().unwrap() + ).unwrap(); + continue + } + + + writeln!(file, + "\t\t\tWholeUnit::{} => Some(Quantity{{", + u["enum_name"].as_str().unwrap() + ).unwrap(); + + match u["base_value_type"].as_str().unwrap() { + "exact" => { + writeln!(file, + "\t\t\t\tscalar: Scalar::new_rational_from_string(\"{}\").unwrap(),", + u["base_value"].as_str().unwrap(), + ).unwrap(); + }, + + "fract" => { + writeln!(file, + "\t\t\t\tscalar: Scalar::new_rational_from_frac({}, {}).unwrap(),", + u["base_value"].as_array().unwrap()[0].as_integer().unwrap(), + u["base_value"].as_array().unwrap()[1].as_integer().unwrap(), + ).unwrap(); + }, + + "approx" => { + writeln!(file, + "\t\t\t\tscalar: Scalar::new_float_from_string(\"{}\").unwrap(),", + u["base_value"].as_str().unwrap(), + ).unwrap(); + }, + + _ => panic!() + }; + + writeln!(file, + concat!( + "\t\t\t\tunit: Unit::from_array(&[\n", + "\t\t\t\t\t(FreeUnit{{whole: WholeUnit::{}, prefix: Prefix::None}}, Scalar::new_rational(-1f64).unwrap()),", + ), + u["enum_name"].as_str().unwrap() + ).unwrap(); + + for b in u["base_units"].as_array().unwrap() { + writeln!(file, + "\t\t\t\t\t(FreeUnit{{whole: WholeUnit::{u}, prefix: Prefix::None}}, Scalar::new_rational({p}f64).unwrap()),", + u = b.as_table().unwrap()["u"].as_str().unwrap(), + p = b.as_table().unwrap()["p"].as_integer().unwrap(), + ).unwrap(); + } + + writeln!(file, + concat!( + "\t\t\t\t])\n", + "\t\t\t}})," + ), + ).unwrap(); + } + + writeln!(file, "\t\t}}\n\t}}\n}}").unwrap(); +} + + +/// Write all SI prefixes. +/// Used inside freeunit_from_string(). +fn prefix_si(mut file: &File, enum_name: &str, s: &str) { + writeln!(file, + concat!( + "\t\t", "\"{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::None}}),\n", + "\t\t", "\"Q{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Quetta}}),\n", + "\t\t", "\"R{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Ronna}}),\n", + "\t\t", "\"Y{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Yotta}}),\n", + "\t\t", "\"Z{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Zetta}}),\n", + "\t\t", "\"E{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Exa}}),\n", + "\t\t", "\"P{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Peta}}),\n", + "\t\t", "\"T{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Tera}}),\n", + "\t\t", "\"G{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Giga}}),\n", + "\t\t", "\"M{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Mega}}),\n", + "\t\t", "\"k{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Kilo}}),\n", + "\t\t", "\"h{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Hecto}}),\n", + "\t\t", "\"da{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Deka}}),\n", + "\t\t", "\"d{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Deci}}),\n", + "\t\t", "\"c{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Centi}}),\n", + "\t\t", "\"m{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Milli}}),\n", + "\t\t", "\"u{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Micro}}),\n", + "\t\t", "\"n{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Nano}}),\n", + "\t\t", "\"p{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Pico}}),\n", + "\t\t", "\"f{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Femto}}),\n", + "\t\t", "\"a{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Atto}}),\n", + "\t\t", "\"z{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Zepto}}),\n", + "\t\t", "\"y{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Yocto}}),\n", + "\t\t", "\"r{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Ronto}}),\n", + "\t\t", "\"q{s}\" => Some(FreeUnit{{whole: WholeUnit::{e}, prefix: Prefix::Quecto}}),", + ), + e = enum_name, + s = s + ).unwrap(); +} + + +/// Create freeunit_from_string(). +/// Should only be run once. +fn write_freeunit_from_string(mut file: &File, units: &Vec) { + writeln!(file, + concat!( + "#[inline(always)]\n", + "fn freeunit_from_string(s: &str) -> Option {{\n", + "\tmatch s {{" + ), + ).unwrap(); + + for u in units { + if u.as_table().unwrap().contains_key("parse") { + for s in u["parse"].as_array().unwrap() { + writeln!(file, + "\t\t\"{}\" => Some(FreeUnit{{whole: WholeUnit::{}, prefix: Prefix::None}}),", + s.as_str().unwrap(), + u["enum_name"].as_str().unwrap() + ).unwrap(); + } + } + + + if u.as_table().unwrap().contains_key("parse_with_prefix") { + if u.as_table().unwrap()["parse_with_prefix"].is_array() { + for p in u["parse_with_prefix"].as_array().unwrap() { + prefix_si( + &file, + u["enum_name"].as_str().unwrap(), + p.as_str().unwrap() + ); + } + } else { + prefix_si( + &file, + u["enum_name"].as_str().unwrap(), + u["parse_with_prefix"].as_str().unwrap() + ); + } + } + + writeln!(file, "").unwrap(); + } + + writeln!(file, "\t\t_ => None\n\t}}\n}}").unwrap(); +} + +pub fn write(target: &Path) { + let units = include_str!("units.toml").parse::
().unwrap(); + let toml::Value::Array(units) = &units["unit"] else {panic!()}; + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(target) + .unwrap(); + + + write_wholeunit_main(&file, units); + writeln!(file, "\n\n").unwrap(); + + write_wholeunit_base_factor(&file, units); + writeln!(file, "\n\n").unwrap(); + + write_freeunit_from_string(&file, units); +} diff --git a/src/evaluate/constant.rs b/src/evaluate/constant.rs deleted file mode 100644 index 539eefc..0000000 --- a/src/evaluate/constant.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::parser::Token; -use crate::parser::Constant; -use crate::quantity::Quantity; -use crate::quantity::Unit; - -use super::EvalError; - -pub fn eval_constant(c: &Constant) -> Result { - Ok(match c { - // Mathematical constants - // 100 digits of each. - Constant::Pi => { Token::Quantity(Quantity::new_float_from_string( - "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067" - ).unwrap())}, - - Constant::E => { Token::Quantity(Quantity::new_float_from_string( - "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427" - ).unwrap()) }, - - Constant::Phi => { Token::Quantity(Quantity::new_float_from_string( - "1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137" - ).unwrap()) }, - - Constant::MPG => { - let mut q = Quantity::new_float_from_string("1").unwrap(); - q.set_unit( - ( - Unit::from_string("mile").unwrap() / - Unit::from_string("gallon").unwrap() - ).unit - ); - Token::Quantity(q) - }, - - Constant::MPH => { - let mut q = Quantity::new_float_from_string("1").unwrap(); - q.set_unit( - ( - Unit::from_string("mile").unwrap() / - Unit::from_string("hour").unwrap() - ).unit - ); - Token::Quantity(q) - }, - }) -} \ No newline at end of file diff --git a/src/evaluate/evaluate.rs b/src/evaluate/evaluate.rs index bc0503b..ccc117d 100644 --- a/src/evaluate/evaluate.rs +++ b/src/evaluate/evaluate.rs @@ -2,7 +2,6 @@ use crate::parser::Token; use crate::parser::Operator; use super::operator::eval_operator; -use super::constant::eval_constant; use super::function::eval_function; use super::EvalError; @@ -13,8 +12,8 @@ pub fn evaluate(t: &Token) -> Result { coords.push(0); 'outer: loop { - let mut h = &mut g; + for t in coords.iter() { let inner = h.get_args_mut(); @@ -32,7 +31,7 @@ pub fn evaluate(t: &Token) -> Result { loop { e = match e { Token::Quantity(_) => { break; }, - Token::Constant(c) => { eval_constant(&c)? } + Token::Constant(c) => { evaluate(&c.value()).unwrap() } Token::Operator(Operator::Function(f), v) => { eval_function(&f, &v)? } Token::Operator(o, v) => { eval_operator(&o, &v)? } }; @@ -50,22 +49,13 @@ pub fn evaluate(t: &Token) -> Result { } - match h { - Token::Operator(_,_) => { - coords.push(0); - continue 'outer; - }, - - Token::Constant(_) => { - coords.push(0); - continue 'outer; - }, + Token::Operator(_,_) => { coords.push(0); }, + Token::Constant(_) => { coords.push(0); }, Token::Quantity(_) => { let l = coords.pop().unwrap(); coords.push(l + 1); - continue 'outer; } }; } diff --git a/src/parser/pretoken.rs b/src/parser/pretoken.rs index 6247db4..26d9453 100644 --- a/src/parser/pretoken.rs +++ b/src/parser/pretoken.rs @@ -74,14 +74,7 @@ impl PreToken { }, PreToken::PreWord(l, s) => { - let c = match &s[..] { - "π"|"pi" => { Some(Constant::Pi)}, - "e" => { Some(Constant::E) }, - "phi"|"φ" => { Some(Constant::Phi) }, - "mpg" => { Some(Constant::MPG) }, - "mph" => { Some(Constant::MPH) }, - _ => { None } - }; + let c = Constant::from_string(&s); if c.is_some() { return Ok(Token::Constant(c.unwrap())); diff --git a/src/parser/token/constant.rs b/src/parser/token/constant.rs deleted file mode 100644 index f303cd4..0000000 --- a/src/parser/token/constant.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[derive(Debug)] -#[derive(Clone)] -pub enum Constant { - // Fake units - MPG, - MPH, - - // Mathematics - Pi, - Phi, - E, -} - -impl Constant { - pub fn to_string(&self) -> String { - match self { - // Fake units - Constant::MPG => { String::from("mpg") }, - Constant::MPH => { String::from("mph") }, - - // Mathematics - Constant::Pi => { String::from("π") }, - Constant::Phi => { String::from("φ") }, - Constant::E => { String::from("e") } - } - } -} \ No newline at end of file diff --git a/src/parser/token/mod.rs b/src/parser/token/mod.rs index 564b412..0aec541 100644 --- a/src/parser/token/mod.rs +++ b/src/parser/token/mod.rs @@ -1,9 +1,11 @@ mod operator; mod function; mod token; -mod constant; pub use self::operator::Operator; pub use self::function::Function; pub use self::token::Token; -pub use self::constant::Constant; \ No newline at end of file + + +use super::parse; +include!(concat!(env!("OUT_DIR"), "/constants.rs")); \ No newline at end of file