mirror of
				https://github.com/rm-dr/daisy
				synced 2025-10-30 14:04:47 -07:00 
			
		
		
		
	Added constant generation
This commit is contained in:
		
							
								
								
									
										1
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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 | ||||
|  | ||||
							
								
								
									
										110
									
								
								buildscript/constants.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								buildscript/constants.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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::<Table>().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<Constant> {{\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(); | ||||
| } | ||||
							
								
								
									
										76
									
								
								buildscript/constants.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								buildscript/constants.toml
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| @ -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<Value>) { | ||||
| 	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<Value>) { | ||||
| 	writeln!(file, | ||||
| 		concat!( | ||||
| 			"impl WholeUnit {{\n", | ||||
| 			"\tfn base_factor(&self) -> Option<Quantity> {{\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<Value>) { | ||||
| 	writeln!(file, | ||||
| 		concat!( | ||||
| 			"#[inline(always)]\n", | ||||
| 			"fn freeunit_from_string(s: &str) -> Option<FreeUnit> {{\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::<Table>().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(()); | ||||
| } | ||||
							
								
								
									
										264
									
								
								buildscript/units.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								buildscript/units.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<Value>) { | ||||
| 	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<Value>) { | ||||
| 	writeln!(file, | ||||
| 		concat!( | ||||
| 			"impl WholeUnit {{\n", | ||||
| 			"\tfn base_factor(&self) -> Option<Quantity> {{\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<Value>) { | ||||
| 	writeln!(file, | ||||
| 		concat!( | ||||
| 			"#[inline(always)]\n", | ||||
| 			"fn freeunit_from_string(s: &str) -> Option<FreeUnit> {{\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::<Table>().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); | ||||
| } | ||||
| @ -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<Token, EvalError> { | ||||
| 	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) | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
| @ -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<Token, EvalError> { | ||||
| 	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<Token, EvalError> { | ||||
| 				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<Token, EvalError> { | ||||
| 		} | ||||
|  | ||||
|  | ||||
|  | ||||
| 		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; | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| @ -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())); | ||||
|  | ||||
| @ -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") } | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
|  | ||||
| use super::parse; | ||||
| include!(concat!(env!("OUT_DIR"), "/constants.rs")); | ||||
		Reference in New Issue
	
	Block a user