use crate::Constant::*;
use crate::FunctionIdentifier::*;
use crate::LexerKeyword::*;
use crate::NamedNumber::*;
use crate::Operator::*;
use crate::TextOperator::*;
use crate::Token;
use crate::UnaryOperator::*;
use crate::currency::currency_code_to_unit;
use crate::units::Unit::*;
use fastnum::D128;
use fastnum::decimal::Context;
use std::iter::Peekable;
use unicode_segmentation::{GraphemeIndices, UnicodeSegmentation};

fn is_word_char_str(input: &str) -> bool {
	match input {
		"A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O"
		| "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" => true,
		"a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o"
		| "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" => true,
		"Ω" | "Ω" | "µ" | "μ" => true,
		_ => false,
	}
}

fn is_numeric_str(input: &str) -> bool {
	matches!(
		input,
		"." | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
	)
}

/// For example parse a hyphen with no whitespace before or after it
fn read_immediate_grapheme(infix: &str, lexer: &mut Lexer) -> bool {
	if let Some((_i, grapheme)) = lexer.graphemes.peek() {
		if *grapheme == infix {
			lexer.graphemes.next();
			return true;
		}
	}
	false
}

/// Read next characters as a word, otherwise return empty string.
/// Returns an empty string if there's leading whitespace.
fn read_immediate_word(lexer: &mut Lexer) -> String {
	let graphemes = &mut lexer.graphemes;

	let mut word = String::new();
	while let Some((_i, grapheme)) = graphemes.peek() {
		if is_word_char_str(grapheme) {
			word += graphemes.next().unwrap().1;
		} else {
			break;
		}
	}
	word
}

/// Read next as a word, otherwise return empty string.
/// Leading whitespace is ignored. A trailing digit may be included.
fn read_word(lexer: &mut Lexer) -> String {
	let graphemes = &mut lexer.graphemes;
	// skip whitespace
	while let Some((_i, grapheme)) = graphemes.peek() {
		if grapheme.trim_start().is_empty() {
			graphemes.next();
		} else {
			break;
		}
	}
	let mut word = "".to_string();
	while let Some((_i, grapheme)) = graphemes.peek() {
		if is_word_char_str(grapheme) {
			word += graphemes.next().unwrap().1;
		} else {
			break;
		}
	}
	if !word.is_empty() {
		match *graphemes.peek().map(|(_i, g)| g).unwrap_or(&"") {
			"2" | "²" => {
				word += "2";
				graphemes.next();
			}
			"3" | "³" => {
				word += "3";
				graphemes.next();
			}
			_ => {}
		}
	}
	word
}

fn lex_token(lexer: &mut Lexer) -> Result<(), String> {
	let (start_i, first_grapheme) = match lexer.graphemes.peek() {
		Some(c) => *c,
		None => return Ok(()),
	};
	let token = match first_grapheme.to_ascii_lowercase().as_str() {
		grapheme if grapheme.trim_start().is_empty() => {
			lexer.graphemes.next();
			return Ok(());
		}
		grapheme if is_word_char_str(grapheme) => {
			lex_word(read_word(lexer).as_str(), lexer)?;
			return Ok(());
		}
		grapheme if is_numeric_str(grapheme) => {
			let mut end_i = start_i + grapheme.len();
			lexer.graphemes.next();
			while let Some((_, grapheme)) = lexer.graphemes.peek() {
				if is_numeric_str(grapheme) {
					end_i += grapheme.len();
					lexer.graphemes.next();
				} else {
					break;
				}
			}
			let number_string = &lexer.input[start_i..end_i];
			let token = match D128::from_str(&number_string, Context::default()) {
				Ok(number) => Token::Number(number),
				Err(_e) => {
					return Err(format!("Error lexing d128 number: {}", number_string));
				}
			};
			lexer.tokens.push(token);
			return Ok(());
		}
		"+" => Token::Operator(Plus),
		"-" => Token::Operator(Minus),
		"*" => Token::Operator(Multiply),
		"/" | "÷" => Token::Operator(Divide),
		"%" => Token::LexerKeyword(PercentChar),
		"^" => Token::Operator(Caret),
		"!" => Token::UnaryOperator(Factorial),
		"(" => {
			lexer.left_paren_count += 1;
			Token::Operator(LeftParen)
		}
		")" => {
			lexer.right_paren_count += 1;
			Token::Operator(RightParen)
		}
		"π" => Token::Constant(Pi),
		"'" => Token::unit(Foot),
		"\"" | "“" | "”" | "″" => Token::LexerKeyword(DoubleQuotes),
		grapheme => {
			return Err(format!("Invalid character: {}", grapheme));
		}
	};
	lexer.graphemes.next();
	lexer.tokens.push(token);
	Ok(())
}

fn lex_word_if_non_empty(word: &str, lexer: &mut Lexer) -> Result<(), String> {
	match word {
		"" => Ok(()),
		_ => lex_word(word, lexer),
	}
}

fn lex_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
	let token = match word.to_ascii_lowercase().as_str() {
		"to" => Token::TextOperator(To),
		"of" => Token::TextOperator(Of),

		"hundred" => Token::NamedNumber(Hundred),
		"thousand" => Token::NamedNumber(Thousand),
		"mil" | "mill" | "million" => Token::NamedNumber(Million),
		"bil" | "bill" | "billion" => Token::NamedNumber(Billion),
		"tri" | "tril" | "trillion" => Token::NamedNumber(Trillion),
		"quadrillion" => Token::NamedNumber(Quadrillion),
		"quintillion" => Token::NamedNumber(Quintillion),
		"sextillion" => Token::NamedNumber(Sextillion),
		"septillion" => Token::NamedNumber(Septillion),
		"octillion" => Token::NamedNumber(Octillion),
		"nonillion" => Token::NamedNumber(Nonillion),
		"decillion" => Token::NamedNumber(Decillion),
		"undecillion" => Token::NamedNumber(Undecillion),
		"duodecillion" => Token::NamedNumber(Duodecillion),
		"tredecillion" => Token::NamedNumber(Tredecillion),
		"quattuordecillion" => Token::NamedNumber(Quattuordecillion),
		"quindecillion" => Token::NamedNumber(Quindecillion),
		"sexdecillion" => Token::NamedNumber(Sexdecillion),
		"septendecillion" => Token::NamedNumber(Septendecillion),
		"octodecillion" => Token::NamedNumber(Octodecillion),
		"novemdecillion" => Token::NamedNumber(Novemdecillion),
		"vigintillion" => Token::NamedNumber(Vigintillion),
		"centillion" => Token::NamedNumber(Centillion),
		"googol" => Token::NamedNumber(Googol),

		"pi" => Token::Constant(Pi),
		"e" => Token::Constant(E),

		"plus" => Token::Operator(Plus),
		"minus" => Token::Operator(Minus),
		"times" => Token::Operator(Multiply),
		"multiplied" => match read_word(lexer).as_str() {
			"by" => Token::Operator(Multiply),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"divided" => match read_word(lexer).as_str() {
			"by" => Token::Operator(Divide),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"mod" => Token::Operator(Modulo),

		"sqrt" => Token::FunctionIdentifier(Sqrt),
		"cbrt" => Token::FunctionIdentifier(Cbrt),

		"log" => Token::FunctionIdentifier(Log),
		"ln" => Token::FunctionIdentifier(Ln),
		"exp" => Token::FunctionIdentifier(Exp),

		"round" | "rint" => Token::FunctionIdentifier(Round),
		"ceil" => Token::FunctionIdentifier(Ceil),
		"floor" => Token::FunctionIdentifier(Floor),
		"abs" | "fabs" => Token::FunctionIdentifier(Abs),

		"sin" => Token::FunctionIdentifier(Sin),
		"cos" => Token::FunctionIdentifier(Cos),
		"tan" => Token::FunctionIdentifier(Tan),

		"per" => Token::TextOperator(Per),
		"hg" => Token::LexerKeyword(Hg), // can be hectogram or mercury

		"ns" | "nanosec" | "nanosecs" | "nanosecond" | "nanoseconds" => Token::unit(Nanosecond),
		// µ and μ are two different characters
		"µs" | "μs" | "microsec" | "microsecs" | "microsecond" | "microseconds" => {
			Token::unit(Microsecond)
		}
		"ms" | "millisec" | "millisecs" | "millisecond" | "milliseconds" => {
			Token::unit(Millisecond)
		}
		"s" | "sec" | "secs" | "second" | "seconds" => Token::unit(Second),
		"min" | "mins" | "minute" | "minutes" => Token::unit(Minute),
		"h" | "hr" | "hrs" | "hour" | "hours" => Token::unit(Hour),
		"day" | "days" => Token::unit(Day),
		"wk" | "wks" | "week" | "weeks" => Token::unit(Week),
		"mo" | "mos" | "month" | "months" => Token::unit(Month),
		"q" | "quarter" | "quarters" => Token::unit(Quarter),
		"yr" | "yrs" | "year" | "years" => Token::unit(Year),
		"decade" | "decades" => Token::unit(Decade),
		"century" | "centuries" => Token::unit(Century),
		"millenium" | "millenia" | "milleniums" => Token::unit(Millennium),

		"mm" | "millimeter" | "millimeters" | "millimetre" | "millimetres" => {
			Token::unit(Millimeter)
		}
		"cm" | "centimeter" | "centimeters" | "centimetre" | "centimetres" => {
			Token::unit(Centimeter)
		}
		"dm" | "decimeter" | "decimeters" | "decimetre" | "decimetres" => Token::unit(Decimeter),
		"m" | "meter" | "meters" | "metre" | "metres" => Token::unit(Meter),
		"km" | "kilometer" | "kilometers" | "kilometre" | "kilometres" => Token::unit(Kilometer),
		"in" => Token::LexerKeyword(In),
		"inch" | "inches" => Token::unit(Inch),
		"ft" | "foot" | "feet" => Token::unit(Foot),
		"yd" | "yard" | "yards" => Token::unit(Yard),
		"mi" | "mile" | "miles" => Token::unit(Mile),
		"marathon" | "marathons" => Token::unit(Marathon),
		"nmi" => Token::unit(NauticalMile),
		"nautical" => match read_word(lexer).as_str() {
			"mile" | "miles" => Token::unit(NauticalMile),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"ly" | "lightyear" | "lightyears" => Token::unit(LightYear),
		"lightsec" | "lightsecs" | "lightsecond" | "lightseconds" => Token::unit(LightSecond),
		"light" => match read_word(lexer).as_str() {
			"yr" | "yrs" | "year" | "years" => Token::unit(LightYear),
			"sec" | "secs" | "second" | "seconds" => Token::unit(LightSecond),
			string => return Err(format!("Invalid string: {}", string)),
		},

		"sqmm" | "mm2" | "millimeter2" | "millimeters2" | "millimetre2" | "millimetres2" => {
			Token::unit(SquareMillimeter)
		}
		"sqcm" | "cm2" | "centimeter2" | "centimeters2" | "centimetre2" | "centimetres2" => {
			Token::unit(SquareCentimeter)
		}
		"sqdm" | "dm2" | "decimeter2" | "decimeters2" | "decimetre2" | "decimetres2" => {
			Token::unit(SquareDecimeter)
		}
		"sqm" | "m2" | "meter2" | "meters2" | "metre2" | "metres2" => Token::unit(SquareMeter),
		"sqkm" | "km2" | "kilometer2" | "kilometers2" | "kilometre2" | "kilometres2" => {
			Token::unit(SquareKilometer)
		}
		"sqin" | "in2" | "inch2" | "inches2" => Token::unit(SquareInch),
		"sqft" | "ft2" | "foot2" | "feet2" => Token::unit(SquareFoot),
		"sqyd" | "yd2" | "yard2" | "yards2" => Token::unit(SquareYard),
		"sqmi" | "mi2" | "mile2" | "miles2" => Token::unit(SquareMile),
		"sq" | "square" => match read_word(lexer).as_str() {
			"mm" | "millimeter" | "millimeters" | "millimetre" | "millimetres" => {
				Token::unit(SquareMillimeter)
			}
			"cm" | "centimeter" | "centimeters" | "centimetre" | "centimetres" => {
				Token::unit(SquareCentimeter)
			}
			"dm" | "decimeter" | "decimeters" | "decimetre" | "decimetres" => {
				Token::unit(SquareDecimeter)
			}
			"m" | "meter" | "meters" | "metre" | "metres" => Token::unit(SquareMeter),
			"km" | "kilometer" | "kilometers" | "kilometre" | "kilometres" => {
				Token::unit(SquareKilometer)
			}
			"in" | "inch" | "inches" => Token::unit(SquareInch),
			"ft" | "foot" | "feet" => Token::unit(SquareFoot),
			"yd" | "yard" | "yards" => Token::unit(SquareYard),
			"mi" | "mile" | "miles" => Token::unit(SquareMile),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"are" | "ares" => Token::unit(Are),
		"decare" | "decares" => Token::unit(Decare),
		"ha" | "hectare" | "hectares" => Token::unit(Hectare),
		"acre" | "acres" => Token::unit(Acre),

		"mm3" | "millimeter3" | "millimeters3" | "millimetre3" | "millimetres3" => {
			Token::unit(CubicMillimeter)
		}
		"cm3" | "centimeter3" | "centimeters3" | "centimetre3" | "centimetres3" => {
			Token::unit(CubicCentimeter)
		}
		"dm3" | "decimeter3" | "decimeters3" | "decimetre3" | "decimetres3" => {
			Token::unit(CubicDecimeter)
		}
		"m3" | "meter3" | "meters3" | "metre3" | "metres3" => Token::unit(CubicMeter),
		"km3" | "kilometer3" | "kilometers3" | "kilometre3" | "kilometres3" => {
			Token::unit(CubicKilometer)
		}
		"inc3" | "inch3" | "inches3" => Token::unit(CubicInch),
		"ft3" | "foot3" | "feet3" => Token::unit(CubicFoot),
		"yd3" | "yard3" | "yards3" => Token::unit(CubicYard),
		"mi3" | "mile3" | "miles3" => Token::unit(CubicMile),
		"cubic" => match read_word(lexer).as_str() {
			"mm" | "millimeter" | "millimeters" | "millimetre" | "millimetres" => {
				Token::unit(CubicMillimeter)
			}
			"cm" | "centimeter" | "centimeters" | "centimetre" | "centimetres" => {
				Token::unit(CubicCentimeter)
			}
			"dm" | "decimeter" | "decimeters" | "decimetre" | "decimetres" => {
				Token::unit(CubicDecimeter)
			}
			"m" | "meter" | "meters" | "metre" | "metres" => Token::unit(CubicMeter),
			"km" | "kilometer" | "kilometers" | "kilometre" | "kilometres" => {
				Token::unit(CubicKilometer)
			}
			"in" | "inch" | "inches" => Token::unit(CubicInch),
			"ft" | "foot" | "feet" => Token::unit(CubicFoot),
			"yd" | "yard" | "yards" => Token::unit(CubicYard),
			"mi" | "mile" | "miles" => Token::unit(CubicMile),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"ml" | "milliliter" | "milliliters" | "millilitre" | "millilitres" => {
			Token::unit(Milliliter)
		}
		"cl" | "centiliter" | "centiliters" | "centilitre" | "centilitres" => {
			Token::unit(Centiliter)
		}
		"dl" | "deciliter" | "deciliters" | "decilitre" | "decilitres" => Token::unit(Deciliter),
		"l" | "liter" | "liters" | "litre" | "litres" => Token::unit(Liter),
		"ts" | "tsp" | "tspn" | "tspns" | "teaspoon" | "teaspoons" => Token::unit(Teaspoon),
		"tbs" | "tbsp" | "tablespoon" | "tablespoons" => Token::unit(Tablespoon),
		"floz" => Token::unit(FluidOunce),
		"fl" | "fluid" => match read_word(lexer).as_str() {
			"oz" | "ounce" | "ounces" => Token::unit(FluidOunce),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"cup" | "cups" => Token::unit(Cup),
		"pt" | "pint" | "pints" => Token::unit(Pint),
		"qt" | "quart" | "quarts" => Token::unit(Quart),
		"gal" | "gallon" | "gallons" => Token::unit(Gallon),
		"bbl" => Token::unit(OilBarrel),
		"oil" => match read_word(lexer).as_str() {
			"barrel" | "barrels" => Token::unit(OilBarrel),
			string => return Err(format!("Invalid string: {}", string)),
		},

		"metric" => match read_word(lexer).as_str() {
			"ton" | "tons" | "tonne" | "tonnes" => Token::unit(MetricTon),
			"hp" | "hps" | "horsepower" | "horsepowers" => Token::unit(MetricHorsepower),
			string => return Err(format!("Invalid string: {}", string)),
		},

		"mg" | "milligram" | "milligrams" => Token::unit(Milligram),
		"g" | "gram" | "grams" => Token::unit(Gram),
		"hectogram" | "hectograms" => Token::unit(Hectogram),
		"kg" | "kilo" | "kilos" | "kilogram" | "kilograms" => Token::unit(Kilogram),
		"t" | "tonne" | "tonnes" => Token::unit(MetricTon),
		"oz" | "ounces" => Token::unit(Ounce),
		"lb" | "lbs" => Token::unit(Pound),
		"pound" | "pounds" => match read_immediate_grapheme("-", lexer) {
			true => match lexer.read_immediate_word().as_str() {
				"force" => Token::LexerKeyword(PoundForce),
				other => {
					lexer.tokens.push(Token::unit(Pound));
					lexer.tokens.push(Token::Operator(Minus));
					lex_word_if_non_empty(other, lexer)?;
					return Ok(());
				}
			},
			false => Token::unit(Pound),
		},
		"stone" | "stones" => Token::unit(Stone),
		"st" | "ton" | "tons" => Token::unit(ShortTon),
		"short" => match read_word(lexer).as_str() {
			"ton" | "tons" | "tonne" | "tonnes" => Token::unit(ShortTon),
			string => return Err(format!("Invalid string: {}", string)),
		},
		"lt" => Token::unit(LongTon),
		"long" => match read_word(lexer).as_str() {
			"ton" | "tons" | "tonne" | "tonnes" => Token::unit(LongTon),
			string => return Err(format!("Invalid string: {}", string)),
		},

		"bit" | "bits" => Token::unit(Bit),
		"kbit" | "kilobit" | "kilobits" => Token::unit(Kilobit),
		"mbit" | "megabit" | "megabits" => Token::unit(Megabit),
		"gbit" | "gigabit" | "gigabits" => Token::unit(Gigabit),
		"tbit" | "terabit" | "terabits" => Token::unit(Terabit),
		"pbit" | "petabit" | "petabits" => Token::unit(Petabit),
		"ebit" | "exabit" | "exabits" => Token::unit(Exabit),
		"zbit" | "zettabit" | "zettabits" => Token::unit(Zettabit),
		"ybit" | "yottabit" | "yottabits" => Token::unit(Yottabit),
		"kibit" | "kibibit" | "kibibits" => Token::unit(Kibibit),
		"mibit" | "mebibit" | "mebibits" => Token::unit(Mebibit),
		"gibit" | "gibibit" | "gibibits" => Token::unit(Gibibit),
		"tibit" | "tebibit" | "tebibits" => Token::unit(Tebibit),
		"pibit" | "pebibit" | "pebibits" => Token::unit(Pebibit),
		"eibit" | "exbibit" | "exbibits" => Token::unit(Exbibit),
		"zibit" | "zebibit" | "zebibits" => Token::unit(Zebibit),
		"yibit" | "yobibit" | "yobibits" => Token::unit(Yobibit),
		"byte" | "bytes" => Token::unit(Byte),
		"kb" | "kilobyte" | "kilobytes" => Token::unit(Kilobyte),
		"mb" | "megabyte" | "megabytes" => Token::unit(Megabyte),
		"gb" | "gigabyte" | "gigabytes" => Token::unit(Gigabyte),
		"tb" | "terabyte" | "terabytes" => Token::unit(Terabyte),
		"pb" | "petabyte" | "petabytes" => Token::unit(Petabyte),
		"eb" | "exabyte" | "exabytes" => Token::unit(Exabyte),
		"zb" | "zettabyte" | "zettabytes" => Token::unit(Zettabyte),
		"yb" | "yottabyte" | "yottabytes" => Token::unit(Yottabyte),
		"kib" | "kibibyte" | "kibibytes" => Token::unit(Kibibyte),
		"mib" | "mebibyte" | "mebibytes" => Token::unit(Mebibyte),
		"gib" | "gibibyte" | "gibibytes" => Token::unit(Gibibyte),
		"tib" | "tebibyte" | "tebibytes" => Token::unit(Tebibyte),
		"pib" | "pebibyte" | "pebibytes" => Token::unit(Pebibyte),
		"eib" | "exbibyte" | "exbibytes" => Token::unit(Exbibyte),
		"zib" | "zebibyte" | "zebibytes" => Token::unit(Zebibyte),
		"yib" | "yobibyte" | "yobibytes" => Token::unit(Yobibyte),

		"bps" if word.as_bytes()[0] == b'B' => Token::unit(BytesPerSecond),
		"kbps" if word.as_bytes()[1] == b'B' => Token::unit(KilobytesPerSecond),
		"mbps" if word.as_bytes()[1] == b'B' => Token::unit(MegabytesPerSecond),
		"gbps" if word.as_bytes()[1] == b'B' => Token::unit(GigabytesPerSecond),
		"tbps" if word.as_bytes()[1] == b'B' => Token::unit(TerabytesPerSecond),
		"pbps" if word.as_bytes()[1] == b'B' => Token::unit(PetabytesPerSecond),
		"ebps" if word.as_bytes()[1] == b'B' => Token::unit(ExabytesPerSecond),
		"zbps" if word.as_bytes()[1] == b'B' => Token::unit(ZettabytesPerSecond),
		"ybps" if word.as_bytes()[1] == b'B' => Token::unit(YottabytesPerSecond),

		"bps" => Token::unit(BitsPerSecond),
		"kbps" => Token::unit(KilobitsPerSecond),
		"mbps" => Token::unit(MegabitsPerSecond),
		"gbps" => Token::unit(GigabitsPerSecond),
		"tbps" => Token::unit(TerabitsPerSecond),
		"pbps" => Token::unit(PetabitsPerSecond),
		"ebps" => Token::unit(ExabitsPerSecond),
		"zbps" => Token::unit(ZettabitsPerSecond),
		"ybps" => Token::unit(YottabitsPerSecond),

		"flop" => Token::unit(Flop),
		"kflop" | "kiloflop" => Token::unit(KiloFlop),
		"mflop" | "megaflop" => Token::unit(MegaFlop),
		"gflop" | "gigaflop" => Token::unit(GigaFlop),
		"tflop" | "teraflop" => Token::unit(TeraFlop),
		"pflop" | "petaflop" => Token::unit(PetaFlop),
		"eflop" | "exaflop" => Token::unit(ExaFlop),
		"zflop" | "zettaflop" => Token::unit(ZettaFlop),
		"yflop" | "yottaflop" => Token::unit(YottaFlop),
		"rflop" | "ronnaflop" => Token::unit(RonnaFlop),
		"qflop" | "quettaflop" => Token::unit(QuettaFlop),

		"flops" => Token::unit(FlopPerSecond),
		"kflops" | "kiloflops" => Token::unit(KiloFlopPerSecond),
		"mflops" | "megaflops" => Token::unit(MegaFlopPerSecond),
		"gflops" | "gigaflops" => Token::unit(GigaFlopPerSecond),
		"tflops" | "teraflops" => Token::unit(TeraFlopPerSecond),
		"pflops" | "petaflops" => Token::unit(PetaFlopPerSecond),
		"eflops" | "exaflops" => Token::unit(ExaFlopPerSecond),
		"zflops" | "zettaflops" => Token::unit(ZettaFlopPerSecond),
		"yflops" | "yottaflops" => Token::unit(YottaFlopPerSecond),
		"rflops" | "ronnaflops" => Token::unit(RonnaFlopPerSecond),
		"qflops" | "quettaflops" => Token::unit(QuettaFlopPerSecond),

		"millijoule" | "millijoules" => Token::unit(Millijoule),
		"j" | "joule" | "joules" => Token::unit(Joule),
		"nm" => Token::unit(NewtonMeter),
		"newton" => match read_immediate_grapheme("-", lexer) {
			true => match lexer.read_immediate_word().as_str() {
				"meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter),
				string => return Err(format!("Invalid string: {}", string)),
			},
			false => match lexer.read_word().as_str() {
				"meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter),
				string => return Err(format!("Invalid string: {}", string)),
			},
		},
		"kj" | "kilojoule" | "kilojoules" => Token::unit(Kilojoule),
		"mj" | "megajoule" | "megajoules" => Token::unit(Megajoule),
		"gj" | "gigajoule" | "gigajoules" => Token::unit(Gigajoule),
		"tj" | "terajoule" | "terajoules" => Token::unit(Terajoule),
		"cal" | "calorie" | "calories" => Token::unit(Calorie),
		"kcal" | "kilocalorie" | "kilocalories" => Token::unit(KiloCalorie),
		"btu" => Token::unit(BritishThermalUnit),
		"british" => match read_word(lexer).as_str() {
			"thermal" => match read_word(lexer).as_str() {
				"unit" | "units" => Token::unit(BritishThermalUnit),
				string => return Err(format!("Invalid string: {}", string)),
			},
			string => return Err(format!("Invalid string: {}", string)),
		},
		"wh" => Token::unit(WattHour),
		"kwh" => Token::unit(KilowattHour),
		"mwh" => Token::unit(MegawattHour),
		"gwh" => Token::unit(GigawattHour),
		"twh" => Token::unit(TerawattHour),
		"pwh" => Token::unit(PetawattHour),

		"milliwatt" | "milliwatts" => Token::unit(Milliwatt),
		"w" | "watts" => Token::unit(Watt),
		"kw" | "kilowatts" => Token::unit(Kilowatt),
		"mw" | "megawatts" => Token::unit(Megawatt),
		"gw" | "gigawatts" => Token::unit(Gigawatt),
		"tw" | "terawatts" => Token::unit(Terawatt),
		"pw" | "petawatts" => Token::unit(Petawatt),
		"hp" | "hps" | "horsepower" | "horsepowers" => Token::unit(Horsepower),
		"mhp" | "hpm" => Token::unit(MetricHorsepower),

		"watt" => match read_word(lexer).as_str() {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(WattHour),
			other => {
				lexer.tokens.push(Token::unit(Watt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},
		"kilowatt" => match read_word(lexer).as_str() {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(KilowattHour),
			other => {
				lexer.tokens.push(Token::unit(Kilowatt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},
		"megawatt" => match read_word(lexer).as_str() {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(MegawattHour),
			other => {
				lexer.tokens.push(Token::unit(Megawatt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},
		"gigawatt" => match read_word(lexer).as_str() {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(GigawattHour),
			other => {
				lexer.tokens.push(Token::unit(Gigawatt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},
		"terawatt" => match read_word(lexer).as_str() {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(TerawattHour),
			other => {
				lexer.tokens.push(Token::unit(Terawatt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},
		"petawatt" => match &*read_word(lexer) {
			"hr" | "hrs" | "hour" | "hours" => Token::unit(PetawattHour),
			other => {
				lexer.tokens.push(Token::unit(Petawatt));
				lex_word_if_non_empty(other, lexer)?;
				return Ok(());
			}
		},

		"ma" | "milliamp" | "milliamps" | "milliampere" | "milliamperes" => {
			Token::unit(Milliampere)
		}
		"a" | "amp" | "amps" | "ampere" | "amperes" => Token::unit(Ampere),
		"ka" | "kiloamp" | "kiloamps" | "kiloampere" | "kiloamperes" => Token::unit(Kiloampere),
		"bi" | "biot" | "biots" | "aba" | "abampere" | "abamperes" => Token::unit(Abampere),

		"mΩ" | "mΩ" | "milliohm" | "milliohms" => Token::unit(Milliohm),
		"Ω" | "Ω" | "ohm" | "ohms" => Token::unit(Ohm),
		"kΩ" | "kΩ" | "kiloohm" | "kiloohms" => Token::unit(Kiloohm),

		"mv" | "millivolt" | "millivolts" => Token::unit(Millivolt),
		"v" | "volt" | "volts" => Token::unit(Volt),
		"kv" | "kilovolt" | "kilovolts" => Token::unit(Kilovolt),

		// for pound-force per square inch
		"lbf" => Token::LexerKeyword(PoundForce),
		"force" => Token::LexerKeyword(Force),

		"pa" | "pascal" | "pascals" => Token::unit(Pascal),
		"kpa" | "kilopascal" | "kilopascals" => Token::unit(Kilopascal),
		"atm" | "atms" | "atmosphere" | "atmospheres" => Token::unit(Atmosphere),
		"mbar" | "mbars" | "millibar" | "millibars" => Token::unit(Millibar),
		"bar" | "bars" => Token::unit(Bar),
		"inhg" => Token::unit(InchOfMercury),
		"mercury" => Token::LexerKeyword(Mercury),
		"psi" => Token::unit(PoundsPerSquareInch),
		"torr" | "torrs" => Token::unit(Torr),

		"hz" | "hertz" => Token::unit(Hertz),
		"khz" | "kilohertz" => Token::unit(Kilohertz),
		"mhz" | "megahertz" => Token::unit(Megahertz),
		"ghz" | "gigahertz" => Token::unit(Gigahertz),
		"thz" | "terahertz" => Token::unit(Terahertz),
		"phz" | "petahertz" => Token::unit(Petahertz),
		"rpm" => Token::unit(RevolutionsPerMinute),
		"r" | "rev" | "revolution" | "revolutions" => Token::LexerKeyword(Revolution),

		"kph" | "kmh" => Token::unit(KilometersPerHour),
		"mps" => Token::unit(MetersPerSecond),
		"mph" => Token::unit(MilesPerHour),
		"fps" => Token::unit(FeetPerSecond),
		"kn" | "kt" | "knot" | "knots" => Token::unit(Knot),

		"k" | "kelvin" | "kelvins" => Token::unit(Kelvin),
		"c" | "celsius" => Token::unit(Celsius),
		"f" | "fahrenheit" | "fahrenheits" => Token::unit(Fahrenheit),

		"AU$" => Token::unit(AUD),
		"R$" => Token::unit(BRL),
		"CA$" | "C$" => Token::unit(CAD),
		"€" | "euro" | "euros" => Token::unit(EUR),
		"£" => Token::unit(GBP),
		"HK$" => Token::unit(HKD),
		"₹" | "Rs" => Token::unit(INR),
		"₪" => Token::unit(ILS),
		"¥" => Token::unit(JPY),
		"₩" => Token::unit(KRW),
		"MX$" => Token::unit(MXN),
		"NZ$" => Token::unit(NZD),
		"₱" => Token::unit(PHP),
		"zl" | "zł" => Token::unit(PLN),
		"S$" => Token::unit(SGD),
		"฿" => Token::unit(THB),
		"tl" | "₺" => Token::unit(TRY),
		"₴" => Token::unit(UAH),
		"$" | "US$" => Token::unit(USD),
		"₫" => Token::unit(VND),

		string if let Ok(unit) = currency_code_to_unit(string) => Token::unit(unit),
		string => {
			return Err(format!("Invalid string: {}", string));
		}
	};
	lexer.tokens.push(token);
	Ok(())
}

struct Lexer<'a> {
	left_paren_count: u16,
	right_paren_count: u16,
	input: &'a str,
	graphemes: Peekable<GraphemeIndices<'a>>,
	tokens: Vec<Token>,
}
impl<'a> Lexer<'a> {
	fn read_word(&mut self) -> String {
		read_word(self)
	}
	fn read_immediate_word(&mut self) -> String {
		read_immediate_word(self)
	}
	fn lex_token(&mut self) -> Result<(), String> {
		lex_token(self)
	}
}

/// Lex an input string and returns [`Token`]s
pub fn lex(input: &str, remove_trailing_operator: bool) -> Result<Vec<Token>, String> {
	let mut input = input.replace(',', "");

	if remove_trailing_operator {
		match &input.chars().last().unwrap_or('x') {
			'+' | '-' | '*' | '/' | '^' | '(' => {
				input.pop();
			}
			_ => {}
		}
	}

	let mut lexer = Lexer {
		left_paren_count: 0,
		right_paren_count: 0,
		input: &input,
		graphemes: UnicodeSegmentation::grapheme_indices(input.as_str(), true).peekable(),
		tokens: Vec::new(),
	};

	while let Some(_) = lexer.graphemes.peek() {
		lexer.lex_token()?;
	}
	let tokens = &mut lexer.tokens;
	// auto insert missing parentheses in first and last position
	if lexer.left_paren_count > lexer.right_paren_count {
		let missing_right_parens = lexer.left_paren_count - lexer.right_paren_count;
		for _ in 0..missing_right_parens {
			tokens.push(Token::Operator(RightParen));
		}
	} else if lexer.left_paren_count < lexer.right_paren_count {
		let missing_left_parens = lexer.right_paren_count - lexer.left_paren_count;
		for _ in 0..missing_left_parens {
			tokens.insert(0, Token::Operator(LeftParen));
		}
	}

	if tokens.is_empty() {
		return Err("Input was empty".to_string());
	}

	let mut token_index = 0;
	loop {
		match tokens[token_index] {
			// decide if % is percent or modulo
			Token::LexerKeyword(PercentChar) => {
				match tokens.get(token_index + 1) {
					Some(Token::TextOperator(Of)) => {
						// "10% of 1km" should be percentage
						tokens[token_index] = Token::UnaryOperator(Percent);
					}
					Some(Token::Operator(operator)) => {
						match operator {
							LeftParen => {
								// "10%(2)" should be modulo
								tokens[token_index] = Token::Operator(Modulo);
							}
							_ => {
								// "10%*2" should be a percentage
								tokens[token_index] = Token::UnaryOperator(Percent);
							}
						}
					}
					Some(Token::UnaryOperator(_)) => {
						// "10%!" should be a percentage
						tokens[token_index] = Token::UnaryOperator(Percent);
					}
					Some(Token::LexerKeyword(PercentChar)) => {
						// "10%%" should be a percentage
						tokens[token_index] = Token::UnaryOperator(Percent);
					}
					None => {
						// percent if there's no element afterwards
						tokens[token_index] = Token::UnaryOperator(Percent);
					}
					_ => {
						// everything else should be modulo, for example if the % is
						// before a number, function or constants
						tokens[token_index] = Token::Operator(Modulo);
					}
				}
			}
			// decide if " is 'inch' or 'inch of mercury'
			Token::LexerKeyword(DoubleQuotes) => {
				match tokens.get(token_index + 1) {
					Some(Token::LexerKeyword(Hg)) => {
						// "hg should be inch of mercury
						tokens[token_index] = Token::unit(InchOfMercury);
						tokens.remove(token_index + 1);
					}
					_ => {
						// otherwise, Inch
						tokens[token_index] = Token::unit(Inch);
					}
				}
			}
			// if hg wasn't already turned into inch of mercury, it's hectogram
			Token::LexerKeyword(Hg) => {
				tokens[token_index] = Token::unit(Hectogram);
			}
			// decide if "in" is Inch or To
			Token::LexerKeyword(In) => {
				match tokens.get(token_index + 1) {
					Some(Token::Unit(_)) => {
						// "in" should be To
						tokens[token_index] = Token::TextOperator(To);
					}
					_ => {
						// otherwise, Inch
						tokens[token_index] = Token::unit(Inch);
					}
				}
			}
			_ => {}
		}
		if token_index == tokens.len() - 1 {
			break;
		} else {
			token_index += 1;
		}
	}

	Ok(lexer.tokens)
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::numtok;
	use regex::Regex;

	#[test]
	fn test_lex() {
		let strip_operator_spacing = Regex::new(r" ([+\-*/]) ").unwrap();
		let strip_afterdigit_spacing = Regex::new(r"(\d) ").unwrap();
		let nonplural_data_units = Regex::new(r"(bit|byte)s").unwrap();

		let run_lex = |input: &str, expected_tokens: Vec<Token>| {
			let tokens = match lex(input, false) {
				Ok(tokens) => tokens,
				Err(e) => {
					panic!("lex error: {}\nrun_lex input: {}", e, input);
				}
			};
			let info_msg = format!(
				"run_lex assertion failed.\n input: {}\n  left: {:?}\n right: {:?}",
				input, expected_tokens, tokens
			);
			assert!(tokens == expected_tokens, "{info_msg}");

			// Prove we can handle multiple spaces wherever we handle a single space
			let input_extra_spaces = input.replace(" ", "   ");
			let tokens_extra_spaces = lex(&input_extra_spaces, false).unwrap();
			assert!(tokens_extra_spaces == expected_tokens, "{info_msg}");

			// Prove we don't need spaces around operators
			let input_stripped_spaces = strip_operator_spacing.replace_all(input, "$1");
			let tokens_stripped_spaces = lex(&input_stripped_spaces, false).unwrap();
			assert!(tokens_stripped_spaces == expected_tokens, "{info_msg}");

			// Prove we don't need a space after a digit
			let input_afterdigit_stripped_spaces =
				strip_afterdigit_spacing.replace_all(input, "$1");
			let tokens_afterdigit_stripped_spaces =
				lex(&input_afterdigit_stripped_spaces, false).unwrap();
			assert!(
				tokens_afterdigit_stripped_spaces == expected_tokens,
				"{info_msg}"
			);
		};

		let run_datarate_lex = |input: &str, expected_tokens: Vec<Token>| {
			run_lex(input, (*expected_tokens).to_vec());

			// Prove plural and non-plural data units behave identically
			let input_nonplural_units = nonplural_data_units.replace_all(input, "$1");
			let tokens_nonplural_units = lex(&input_nonplural_units, false).unwrap();
			let info_msg = format!(
				"run_datarate_lex input: {}\n  left: {:?}\n right: {:?}",
				input, expected_tokens, tokens_nonplural_units
			);
			assert!(tokens_nonplural_units == expected_tokens, "{info_msg}");
		};

		run_lex(
			"88 kilometres * 2",
			vec![
				numtok!(88),
				Token::unit(Kilometer),
				Token::Operator(Multiply),
				numtok!(2),
			],
		);
		run_lex("0.5 marathon", vec![numtok!(0.5), Token::unit(Marathon)]);
		run_lex("100 nmi", vec![numtok!(100), Token::unit(NauticalMile)]);
		run_lex(
			"101 nautical miles",
			vec![numtok!(101), Token::unit(NauticalMile)],
		);
		run_lex("2 lightyears", vec![numtok!(2), Token::unit(LightYear)]);
		run_lex("1 light year", vec![numtok!(1), Token::unit(LightYear)]);
		run_lex("10 lightsec", vec![numtok!(10), Token::unit(LightSecond)]);
		run_lex("12 light secs", vec![numtok!(12), Token::unit(LightSecond)]);
		run_lex(
			"33.3 square meters",
			vec![numtok!(33.3), Token::unit(SquareMeter)],
		);
		run_lex("54 m2", vec![numtok!(54), Token::unit(SquareMeter)]);
		run_lex("87 sq miles", vec![numtok!(87), Token::unit(SquareMile)]);
		run_lex("500 feet2", vec![numtok!(500), Token::unit(SquareFoot)]);
		run_lex("500 feet²", vec![numtok!(500), Token::unit(SquareFoot)]);
		run_lex("4 cubic metres", vec![numtok!(4), Token::unit(CubicMeter)]);
		run_lex(
			"34 cubic feet + 23 cubic yards",
			vec![
				numtok!(34),
				Token::unit(CubicFoot),
				Token::Operator(Plus),
				numtok!(23),
				Token::unit(CubicYard),
			],
		);
		run_lex(
			"66 inches3 + 65 millimetre³",
			vec![
				numtok!(66),
				Token::unit(CubicInch),
				Token::Operator(Plus),
				numtok!(65),
				Token::unit(CubicMillimeter),
			],
		);
		run_lex(
			"66 inches³ + 65 millimetre3",
			vec![
				numtok!(66),
				Token::unit(CubicInch),
				Token::Operator(Plus),
				numtok!(65),
				Token::unit(CubicMillimeter),
			],
		);
		run_lex("42 millilitres", vec![numtok!(42), Token::unit(Milliliter)]);
		run_lex("3 tbs", vec![numtok!(3), Token::unit(Tablespoon)]);
		run_lex("6 floz", vec![numtok!(6), Token::unit(FluidOunce)]);
		run_lex("6 fl oz", vec![numtok!(6), Token::unit(FluidOunce)]);
		run_lex("6 fluid ounces", vec![numtok!(6), Token::unit(FluidOunce)]);
		run_lex("3 oil barrels", vec![numtok!(3), Token::unit(OilBarrel)]);
		run_lex("67 kg", vec![numtok!(67), Token::unit(Kilogram)]);
		run_lex("34 oz", vec![numtok!(34), Token::unit(Ounce)]);
		run_lex("34 ounces", vec![numtok!(34), Token::unit(Ounce)]);
		run_lex("210 lb", vec![numtok!(210), Token::unit(Pound)]);
		run_lex("210 lbs", vec![numtok!(210), Token::unit(Pound)]);
		run_lex("210 pound", vec![numtok!(210), Token::unit(Pound)]);
		run_lex("210 pounds", vec![numtok!(210), Token::unit(Pound)]);
		run_lex(
			"210 pounds-force",
			vec![numtok!(210), Token::LexerKeyword(PoundForce)],
		);
		run_lex("3 ton", vec![numtok!(3), Token::unit(ShortTon)]);
		run_lex("3 short tons", vec![numtok!(3), Token::unit(ShortTon)]);
		run_lex("4 lt", vec![numtok!(4), Token::unit(LongTon)]);
		run_lex("4 long tonnes", vec![numtok!(4), Token::unit(LongTon)]);
		run_datarate_lex("1 bit", vec![numtok!(1), Token::unit(Bit)]);
		run_datarate_lex("8 bits", vec![numtok!(8), Token::unit(Bit)]);
		run_datarate_lex("63 kilobits", vec![numtok!(63), Token::unit(Kilobit)]);
		run_datarate_lex("32 megabits", vec![numtok!(32), Token::unit(Megabit)]);
		run_datarate_lex("3.5 gigabits", vec![numtok!(3.5), Token::unit(Gigabit)]);
		run_datarate_lex("2.1 terabits", vec![numtok!(2.1), Token::unit(Terabit)]);
		run_datarate_lex("1.08 petabits", vec![numtok!(1.08), Token::unit(Petabit)]);
		run_datarate_lex("0.73 exabits", vec![numtok!(0.73), Token::unit(Exabit)]);
		run_datarate_lex("0.49 zettabits", vec![numtok!(0.49), Token::unit(Zettabit)]);
		run_datarate_lex("0.23 yottabits", vec![numtok!(0.23), Token::unit(Yottabit)]);
		run_datarate_lex("63 kibibits", vec![numtok!(63), Token::unit(Kibibit)]);
		run_datarate_lex("32 mebibits", vec![numtok!(32), Token::unit(Mebibit)]);
		run_datarate_lex("3.5 gibibits", vec![numtok!(3.5), Token::unit(Gibibit)]);
		run_datarate_lex("2.1 tebibits", vec![numtok!(2.1), Token::unit(Tebibit)]);
		run_datarate_lex("1.08 pebibits", vec![numtok!(1.08), Token::unit(Pebibit)]);
		run_datarate_lex("0.73 exbibits", vec![numtok!(0.73), Token::unit(Exbibit)]);
		run_datarate_lex("0.49 zebibits", vec![numtok!(0.49), Token::unit(Zebibit)]);
		run_datarate_lex("0.23 yobibits", vec![numtok!(0.23), Token::unit(Yobibit)]);
		run_datarate_lex("1 byte", vec![numtok!(1), Token::unit(Byte)]);
		run_datarate_lex("3 bytes", vec![numtok!(3), Token::unit(Byte)]);
		run_datarate_lex("63 kilobytes", vec![numtok!(63), Token::unit(Kilobyte)]);
		run_datarate_lex("32 megabytes", vec![numtok!(32), Token::unit(Megabyte)]);
		run_datarate_lex("3.5 gigabytes", vec![numtok!(3.5), Token::unit(Gigabyte)]);
		run_datarate_lex("2.1 terabytes", vec![numtok!(2.1), Token::unit(Terabyte)]);
		run_datarate_lex("1.08 petabytes", vec![numtok!(1.08), Token::unit(Petabyte)]);
		run_datarate_lex("0.73 exabytes", vec![numtok!(0.73), Token::unit(Exabyte)]);
		run_datarate_lex(
			"0.49 zettabytes",
			vec![numtok!(0.49), Token::unit(Zettabyte)],
		);
		run_datarate_lex(
			"0.23 yottabytes",
			vec![numtok!(0.23), Token::unit(Yottabyte)],
		);
		run_datarate_lex("63 kibibytes", vec![numtok!(63), Token::unit(Kibibyte)]);
		run_datarate_lex("32 mebibytes", vec![numtok!(32), Token::unit(Mebibyte)]);
		run_datarate_lex("3.5 gibibytes", vec![numtok!(3.5), Token::unit(Gibibyte)]);
		run_datarate_lex("2.1 tebibytes", vec![numtok!(2.1), Token::unit(Tebibyte)]);
		run_datarate_lex("1.08 pebibytes", vec![numtok!(1.08), Token::unit(Pebibyte)]);
		run_datarate_lex("0.73 exbibytes", vec![numtok!(0.73), Token::unit(Exbibyte)]);
		run_datarate_lex("0.49 zebibytes", vec![numtok!(0.49), Token::unit(Zebibyte)]);
		run_datarate_lex("0.23 yobibytes", vec![numtok!(0.23), Token::unit(Yobibyte)]);
		run_lex("432 Bps", vec![numtok!(432), Token::unit(BytesPerSecond)]);
		run_lex(
			"56 kBps",
			vec![numtok!(56), Token::unit(KilobytesPerSecond)],
		);
		run_lex("432 bps", vec![numtok!(432), Token::unit(BitsPerSecond)]);
		run_lex("56 kbps", vec![numtok!(56), Token::unit(KilobitsPerSecond)]);
		run_lex("12 mbps", vec![numtok!(12), Token::unit(MegabitsPerSecond)]);
		run_lex(
			"4.2 gbps",
			vec![numtok!(4.2), Token::unit(GigabitsPerSecond)],
		);
		run_lex(
			"2.2 tbps",
			vec![numtok!(2.2), Token::unit(TerabitsPerSecond)],
		);
		run_lex(
			"1.7 pbps",
			vec![numtok!(1.7), Token::unit(PetabitsPerSecond)],
		);
		run_lex(
			"0.99 ebps",
			vec![numtok!(0.99), Token::unit(ExabitsPerSecond)],
		);
		run_lex(
			"0.64 zbps",
			vec![numtok!(0.64), Token::unit(ZettabitsPerSecond)],
		);
		run_lex(
			"0.278 ybps",
			vec![numtok!(0.278), Token::unit(YottabitsPerSecond)],
		);
		run_datarate_lex(
			"4 bits per second",
			vec![
				numtok!(4),
				Token::unit(Bit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"5 kilobits per second",
			vec![
				numtok!(5),
				Token::unit(Kilobit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"6 megabits per second",
			vec![
				numtok!(6),
				Token::unit(Megabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"7 gigabits per second",
			vec![
				numtok!(7),
				Token::unit(Gigabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"8 terabits per second",
			vec![
				numtok!(8),
				Token::unit(Terabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"9 petabits per second",
			vec![
				numtok!(9),
				Token::unit(Petabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"10 exabits per second",
			vec![
				numtok!(10),
				Token::unit(Exabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"11 zettabits per second",
			vec![
				numtok!(11),
				Token::unit(Zettabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"12 yottabits per second",
			vec![
				numtok!(12),
				Token::unit(Yottabit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"13 kibibits per second",
			vec![
				numtok!(13),
				Token::unit(Kibibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"14 mebibits per second",
			vec![
				numtok!(14),
				Token::unit(Mebibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"15 gibibits per second",
			vec![
				numtok!(15),
				Token::unit(Gibibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"16 tebibits per second",
			vec![
				numtok!(16),
				Token::unit(Tebibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"17 pebibits per second",
			vec![
				numtok!(17),
				Token::unit(Pebibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"18 exbibits per second",
			vec![
				numtok!(18),
				Token::unit(Exbibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"19 zebibits per second",
			vec![
				numtok!(19),
				Token::unit(Zebibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"20 yobibits per second",
			vec![
				numtok!(20),
				Token::unit(Yobibit),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"4 bytes per second",
			vec![
				numtok!(4),
				Token::unit(Byte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"5 kilobytes per second",
			vec![
				numtok!(5),
				Token::unit(Kilobyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"6 megabytes per second",
			vec![
				numtok!(6),
				Token::unit(Megabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"7 gigabytes per second",
			vec![
				numtok!(7),
				Token::unit(Gigabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"8 terabytes per second",
			vec![
				numtok!(8),
				Token::unit(Terabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"9 petabytes per second",
			vec![
				numtok!(9),
				Token::unit(Petabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"10 exabytes per second",
			vec![
				numtok!(10),
				Token::unit(Exabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"11 zettabytes per second",
			vec![
				numtok!(11),
				Token::unit(Zettabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"12 yottabytes per second",
			vec![
				numtok!(12),
				Token::unit(Yottabyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"13 kibibytes per second",
			vec![
				numtok!(13),
				Token::unit(Kibibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"14 mebibytes per second",
			vec![
				numtok!(14),
				Token::unit(Mebibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"15 gibibytes per second",
			vec![
				numtok!(15),
				Token::unit(Gibibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"16 tebibytes per second",
			vec![
				numtok!(16),
				Token::unit(Tebibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"17 pebibytes per second",
			vec![
				numtok!(17),
				Token::unit(Pebibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"18 exbibytes per second",
			vec![
				numtok!(18),
				Token::unit(Exbibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"19 zebibytes per second",
			vec![
				numtok!(19),
				Token::unit(Zebibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_datarate_lex(
			"20 yobibytes per second",
			vec![
				numtok!(20),
				Token::unit(Yobibyte),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex("1 flop", vec![numtok!(1), Token::unit(Flop)]);
		run_lex("2 kflop", vec![numtok!(2), Token::unit(KiloFlop)]);
		run_lex("3 mflop", vec![numtok!(3), Token::unit(MegaFlop)]);
		run_lex("4 gflop", vec![numtok!(4), Token::unit(GigaFlop)]);
		run_lex("5 tflop", vec![numtok!(5), Token::unit(TeraFlop)]);
		run_lex("6 pflop", vec![numtok!(6), Token::unit(PetaFlop)]);
		run_lex("7 eflop", vec![numtok!(7), Token::unit(ExaFlop)]);
		run_lex("8 zflop", vec![numtok!(8), Token::unit(ZettaFlop)]);
		run_lex("9 yflop", vec![numtok!(9), Token::unit(YottaFlop)]);
		run_lex("10 rflop", vec![numtok!(10), Token::unit(RonnaFlop)]);
		run_lex("11 qflop", vec![numtok!(11), Token::unit(QuettaFlop)]);
		run_lex(
			"1 flop/s",
			vec![
				numtok!(1),
				Token::unit(Flop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"2 kflop/s",
			vec![
				numtok!(2),
				Token::unit(KiloFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"3 mflop/s",
			vec![
				numtok!(3),
				Token::unit(MegaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"4 gflop/s",
			vec![
				numtok!(4),
				Token::unit(GigaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"5 tflop/s",
			vec![
				numtok!(5),
				Token::unit(TeraFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"6 pflop/s",
			vec![
				numtok!(6),
				Token::unit(PetaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"7 eflop/s",
			vec![
				numtok!(7),
				Token::unit(ExaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"8 zflop/s",
			vec![
				numtok!(8),
				Token::unit(ZettaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"9 yflop/s",
			vec![
				numtok!(9),
				Token::unit(YottaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"10 rflop/s",
			vec![
				numtok!(10),
				Token::unit(RonnaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"11 qflop/s",
			vec![
				numtok!(11),
				Token::unit(QuettaFlop),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"1 flop per second",
			vec![
				numtok!(1),
				Token::unit(Flop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"2 kflop per second",
			vec![
				numtok!(2),
				Token::unit(KiloFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"3 mflop per second",
			vec![
				numtok!(3),
				Token::unit(MegaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"4 gflop per second",
			vec![
				numtok!(4),
				Token::unit(GigaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"5 tflop per second",
			vec![
				numtok!(5),
				Token::unit(TeraFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"6 pflop per second",
			vec![
				numtok!(6),
				Token::unit(PetaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"7 eflop per second",
			vec![
				numtok!(7),
				Token::unit(ExaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"8 zflop per second",
			vec![
				numtok!(8),
				Token::unit(ZettaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"9 yflop per second",
			vec![
				numtok!(9),
				Token::unit(YottaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"10 rflop per second",
			vec![
				numtok!(10),
				Token::unit(RonnaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"11 qflop per second",
			vec![
				numtok!(11),
				Token::unit(QuettaFlop),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex("1 flops", vec![numtok!(1), Token::unit(FlopPerSecond)]);
		run_lex("2 kflops", vec![numtok!(2), Token::unit(KiloFlopPerSecond)]);
		run_lex("3 mflops", vec![numtok!(3), Token::unit(MegaFlopPerSecond)]);
		run_lex("4 gflops", vec![numtok!(4), Token::unit(GigaFlopPerSecond)]);
		run_lex("5 tflops", vec![numtok!(5), Token::unit(TeraFlopPerSecond)]);
		run_lex("6 pflops", vec![numtok!(6), Token::unit(PetaFlopPerSecond)]);
		run_lex("7 eflops", vec![numtok!(7), Token::unit(ExaFlopPerSecond)]);
		run_lex(
			"8 zflops",
			vec![numtok!(8), Token::unit(ZettaFlopPerSecond)],
		);
		run_lex(
			"9 yflops",
			vec![numtok!(9), Token::unit(YottaFlopPerSecond)],
		);
		run_lex(
			"10 rflops",
			vec![numtok!(10), Token::unit(RonnaFlopPerSecond)],
		);
		run_lex(
			"11 qflops",
			vec![numtok!(11), Token::unit(QuettaFlopPerSecond)],
		);
		run_lex("234 wh", vec![numtok!(234), Token::unit(WattHour)]);
		run_lex("1 w", vec![numtok!(1), Token::unit(Watt)]);
		run_lex("1 watt", vec![numtok!(1), Token::unit(Watt)]);
		run_lex("1 watts", vec![numtok!(1), Token::unit(Watt)]);
		run_lex("1 watt hour", vec![numtok!(1), Token::unit(WattHour)]);
		run_lex(
			"0 watt + 1 watts",
			vec![
				numtok!(0),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(1),
				Token::unit(Watt),
			],
		);
		run_lex(
			"0 watt * 1",
			vec![
				numtok!(0),
				Token::unit(Watt),
				Token::Operator(Multiply),
				numtok!(1),
			],
		);
		run_lex(
			"2 watts + 3 watts",
			vec![
				numtok!(2),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 watts * 3",
			vec![
				numtok!(2),
				Token::unit(Watt),
				Token::Operator(Multiply),
				numtok!(3),
			],
		);
		run_lex(
			"4 watt plus 5 watts",
			vec![
				numtok!(4),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Watt),
			],
		);
		run_lex(
			"4 watt times 5",
			vec![
				numtok!(4),
				Token::unit(Watt),
				Token::Operator(Multiply),
				numtok!(5),
			],
		);
		run_lex(
			"6 watts plus 7 watts",
			vec![
				numtok!(6),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Watt),
			],
		);
		run_lex(
			"6 watts times 7",
			vec![
				numtok!(6),
				Token::unit(Watt),
				Token::Operator(Multiply),
				numtok!(7),
			],
		);
		run_lex("2.3 kwh", vec![numtok!(2.3), Token::unit(KilowattHour)]);
		run_lex("1 kw", vec![numtok!(1), Token::unit(Kilowatt)]);
		run_lex("1 kilowatt", vec![numtok!(1), Token::unit(Kilowatt)]);
		run_lex("1 kilowatts", vec![numtok!(1), Token::unit(Kilowatt)]);
		run_lex(
			"1 kilowatt hour",
			vec![numtok!(1), Token::unit(KilowattHour)],
		);
		run_lex(
			"2 kilowatt + 3 watt",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatt * 4",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Multiply),
				numtok!(4),
			],
		);
		run_lex(
			"2 kilowatt times 4",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Multiply),
				numtok!(4),
			],
		);
		run_lex(
			"2 kilowatt + 3 watts",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatts + 3 watt",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatts + 3 watts",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatt plus 3 watt",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatt plus 3 watts",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatts plus 3 watt",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 kilowatts plus 3 watts",
			vec![
				numtok!(2),
				Token::unit(Kilowatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"6.6 watts + 4 kilowatts",
			vec![
				numtok!(6.6),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(4),
				Token::unit(Kilowatt),
			],
		);
		run_lex(
			"6.6 watts plus 4 kilowatts",
			vec![
				numtok!(6.6),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(4),
				Token::unit(Kilowatt),
			],
		);
		run_lex("2.3 mwh", vec![numtok!(2.3), Token::unit(MegawattHour)]);
		run_lex("1 mw", vec![numtok!(1), Token::unit(Megawatt)]);
		run_lex("1 megawatt", vec![numtok!(1), Token::unit(Megawatt)]);
		run_lex(
			"1 megawatt hour",
			vec![numtok!(1), Token::unit(MegawattHour)],
		);
		run_lex(
			"2 megawatt + 3 watt",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatt * 6",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Multiply),
				numtok!(6),
			],
		);
		run_lex(
			"2 megawatt times 6",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Multiply),
				numtok!(6),
			],
		);
		run_lex(
			"2 megawatt + 3 watts",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatts + 3 watt",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatts + 3 watts",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatt plus 3 watt",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatt plus 3 watts",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatts plus 3 watt",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"2 megawatts plus 3 watts",
			vec![
				numtok!(2),
				Token::unit(Megawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Watt),
			],
		);
		run_lex(
			"6.6 watts + 4 megawatts",
			vec![
				numtok!(6.6),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(4),
				Token::unit(Megawatt),
			],
		);
		run_lex(
			"6.6 watts plus 4 megawatts",
			vec![
				numtok!(6.6),
				Token::unit(Watt),
				Token::Operator(Plus),
				numtok!(4),
				Token::unit(Megawatt),
			],
		);
		run_lex("234 gwh", vec![numtok!(234), Token::unit(GigawattHour)]);
		run_lex("1 gw", vec![numtok!(1), Token::unit(Gigawatt)]);
		run_lex("1 gigawatt", vec![numtok!(1), Token::unit(Gigawatt)]);
		run_lex("1 gigawatts", vec![numtok!(1), Token::unit(Gigawatt)]);
		run_lex(
			"1 gigawatt hour",
			vec![numtok!(1), Token::unit(GigawattHour)],
		);
		run_lex(
			"0 gigawatt + 1 gigawatts",
			vec![
				numtok!(0),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(1),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"0 gigawatt * 1",
			vec![
				numtok!(0),
				Token::unit(Gigawatt),
				Token::Operator(Multiply),
				numtok!(1),
			],
		);
		run_lex(
			"2 gigawatts + 3 gigawatts",
			vec![
				numtok!(2),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(3),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"2 gigawatts * 3",
			vec![
				numtok!(2),
				Token::unit(Gigawatt),
				Token::Operator(Multiply),
				numtok!(3),
			],
		);
		run_lex(
			"4 gigawatt plus 5 watt",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Watt),
			],
		);
		run_lex(
			"4 gigawatt plus 5 megawatt",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Megawatt),
			],
		);
		run_lex(
			"4 gigawatt plus 5 gigawatt",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"4 gigawatt plus 5 watts",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Watt),
			],
		);
		run_lex(
			"4 gigawatt plus 5 megawatts",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Megawatt),
			],
		);
		run_lex(
			"4 gigawatt plus 5 gigawatts",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(5),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"4 gigawatt times 5",
			vec![
				numtok!(4),
				Token::unit(Gigawatt),
				Token::Operator(Multiply),
				numtok!(5),
			],
		);
		run_lex(
			"6 gigawatts plus 7 watt",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Watt),
			],
		);
		run_lex(
			"6 gigawatts plus 7 megawatt",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Megawatt),
			],
		);
		run_lex(
			"6 gigawatts plus 7 gigawatt",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"6 gigawatts plus 7 watts",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Watt),
			],
		);
		run_lex(
			"6 gigawatts plus 7 megawatts",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Megawatt),
			],
		);
		run_lex(
			"6 gigawatts plus 7 gigawatts",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Plus),
				numtok!(7),
				Token::unit(Gigawatt),
			],
		);
		run_lex(
			"6 gigawatts times 7",
			vec![
				numtok!(6),
				Token::unit(Gigawatt),
				Token::Operator(Multiply),
				numtok!(7),
			],
		);
		run_lex(
			"88 mw * 3",
			vec![
				numtok!(88),
				Token::unit(Megawatt),
				Token::Operator(Multiply),
				numtok!(3),
			],
		);
		run_lex(
			"88 mw times 3",
			vec![
				numtok!(88),
				Token::unit(Megawatt),
				Token::Operator(Multiply),
				numtok!(3),
			],
		);
		run_lex("999 kb", vec![numtok!(999), Token::unit(Kilobyte)]);
		run_lex(
			"200 gb - 100 mb",
			vec![
				numtok!(200),
				Token::unit(Gigabyte),
				Token::Operator(Minus),
				numtok!(100),
				Token::unit(Megabyte),
			],
		);
		run_lex("999 kib", vec![numtok!(999), Token::unit(Kibibyte)]);
		run_lex(
			"200 gib - 100 mib",
			vec![
				numtok!(200),
				Token::unit(Gibibyte),
				Token::Operator(Minus),
				numtok!(100),
				Token::unit(Mebibyte),
			],
		);
		run_lex("45 btu", vec![numtok!(45), Token::unit(BritishThermalUnit)]);
		run_lex(
			"45.5 british thermal unit",
			vec![numtok!(45.5), Token::unit(BritishThermalUnit)],
		);
		run_lex(
			"46 british thermal units",
			vec![numtok!(46), Token::unit(BritishThermalUnit)],
		);
		run_lex(
			"5432 newton metres",
			vec![numtok!(5432), Token::unit(NewtonMeter)],
		);
		run_lex(
			"2345 newton-meters",
			vec![numtok!(2345), Token::unit(NewtonMeter)],
		);
		run_lex("20 lbf", vec![numtok!(20), Token::LexerKeyword(PoundForce)]);
		run_lex("60 hz", vec![numtok!(60), Token::unit(Hertz)]);
		run_lex(
			"1100 rpm",
			vec![numtok!(1100), Token::unit(RevolutionsPerMinute)],
		);
		run_lex(
			"1150 revolutions per minute",
			vec![
				numtok!(1150),
				Token::LexerKeyword(Revolution),
				Token::TextOperator(Per),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1 revolution per min",
			vec![
				numtok!(1),
				Token::LexerKeyword(Revolution),
				Token::TextOperator(Per),
				Token::unit(Minute),
			],
		);
		run_lex(
			"4 revolution / mins",
			vec![
				numtok!(4),
				Token::LexerKeyword(Revolution),
				Token::Operator(Divide),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1250 r / min",
			vec![
				numtok!(1250),
				Token::LexerKeyword(Revolution),
				Token::Operator(Divide),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1300 rev / min",
			vec![
				numtok!(1300),
				Token::LexerKeyword(Revolution),
				Token::Operator(Divide),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1350 rev / minute",
			vec![
				numtok!(1350),
				Token::LexerKeyword(Revolution),
				Token::Operator(Divide),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1250 r per min",
			vec![
				numtok!(1250),
				Token::LexerKeyword(Revolution),
				Token::TextOperator(Per),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1300 rev per min",
			vec![
				numtok!(1300),
				Token::LexerKeyword(Revolution),
				Token::TextOperator(Per),
				Token::unit(Minute),
			],
		);
		run_lex(
			"1350 rev per minute",
			vec![
				numtok!(1350),
				Token::LexerKeyword(Revolution),
				Token::TextOperator(Per),
				Token::unit(Minute),
			],
		);
		run_lex(
			"100 kph",
			vec![numtok!(100), Token::unit(KilometersPerHour)],
		);
		run_lex(
			"100 kmh",
			vec![numtok!(100), Token::unit(KilometersPerHour)],
		);
		run_lex(
			"100 kilometers per hour",
			vec![
				numtok!(100),
				Token::unit(Kilometer),
				Token::TextOperator(Per),
				Token::unit(Hour),
			],
		);
		run_lex(
			"100 kilometre / hrs",
			vec![
				numtok!(100),
				Token::unit(Kilometer),
				Token::Operator(Divide),
				Token::unit(Hour),
			],
		);
		run_lex("3.6 mps", vec![numtok!(3.6), Token::unit(MetersPerSecond)]);
		run_lex(
			"3.6 meters per second",
			vec![
				numtok!(3.6),
				Token::unit(Meter),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"3.6 metre / secs",
			vec![
				numtok!(3.6),
				Token::unit(Meter),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex("60 mph", vec![numtok!(60), Token::unit(MilesPerHour)]);
		run_lex(
			"60 miles per hour",
			vec![
				numtok!(60),
				Token::unit(Mile),
				Token::TextOperator(Per),
				Token::unit(Hour),
			],
		);
		run_lex(
			"60 mile / hr",
			vec![
				numtok!(60),
				Token::unit(Mile),
				Token::Operator(Divide),
				Token::unit(Hour),
			],
		);
		run_lex("35 fps", vec![numtok!(35), Token::unit(FeetPerSecond)]);
		run_lex(
			"35 ft / sec",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"35 ft per seconds",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"35 foot / secs",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"35 foot per seconds",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex(
			"35 feet / sec",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::Operator(Divide),
				Token::unit(Second),
			],
		);
		run_lex(
			"35 feet per second",
			vec![
				numtok!(35),
				Token::unit(Foot),
				Token::TextOperator(Per),
				Token::unit(Second),
			],
		);
		run_lex("30 pa", vec![numtok!(30), Token::unit(Pascal)]);
		run_lex(
			"23 celsius + 4 celsius",
			vec![
				numtok!(23),
				Token::unit(Celsius),
				Token::Operator(Plus),
				numtok!(4),
				Token::unit(Celsius),
			],
		);
		run_lex(
			"54 f - 1.5 fahrenheit",
			vec![
				numtok!(54),
				Token::unit(Fahrenheit),
				Token::Operator(Minus),
				numtok!(1.5),
				Token::unit(Fahrenheit),
			],
		);
		run_lex(
			"50 metric tonnes",
			vec![numtok!(50), Token::unit(MetricTon)],
		);
		run_lex(
			"77 metric hps",
			vec![numtok!(77), Token::unit(MetricHorsepower)],
		);

		run_lex(
			"100 + 99",
			vec![numtok!(100), Token::Operator(Plus), numtok!(99)],
		);
		run_lex(
			"100 plus 99",
			vec![numtok!(100), Token::Operator(Plus), numtok!(99)],
		);
		run_lex(
			"12 - 4",
			vec![numtok!(12), Token::Operator(Minus), numtok!(4)],
		);
		run_lex(
			"12 minus 4",
			vec![numtok!(12), Token::Operator(Minus), numtok!(4)],
		);
		run_lex(
			"50.5 * 2",
			vec![numtok!(50.5), Token::Operator(Multiply), numtok!(2)],
		);
		run_lex(
			"50.5 times 2",
			vec![numtok!(50.5), Token::Operator(Multiply), numtok!(2)],
		);
		run_lex(
			"50.5 multiplied by 2",
			vec![numtok!(50.5), Token::Operator(Multiply), numtok!(2)],
		);
		run_lex(
			"6 / 3",
			vec![numtok!(6), Token::Operator(Divide), numtok!(3)],
		);
		run_lex(
			"50 / 10",
			vec![numtok!(50), Token::Operator(Divide), numtok!(10)],
		);
		run_lex(
			"52 ÷ 12",
			vec![numtok!(52), Token::Operator(Divide), numtok!(12)],
		);
		run_lex(
			"6 divided by 3",
			vec![numtok!(6), Token::Operator(Divide), numtok!(3)],
		);
		run_lex(
			"7 mod 5",
			vec![numtok!(7), Token::Operator(Modulo), numtok!(5)],
		);

		run_lex(
			"(2 + 3) * 4",
			vec![
				Token::Operator(LeftParen),
				numtok!(2),
				Token::Operator(Plus),
				numtok!(3),
				Token::Operator(RightParen),
				Token::Operator(Multiply),
				numtok!(4),
			],
		);
		run_lex(
			"52 weeks * (12 hrs + 12 hours)",
			vec![
				numtok!(52),
				Token::unit(Week),
				Token::Operator(Multiply),
				Token::Operator(LeftParen),
				numtok!(12),
				Token::unit(Hour),
				Token::Operator(Plus),
				numtok!(12),
				Token::unit(Hour),
				Token::Operator(RightParen),
			],
		);
		run_lex(
			"12 pound+",
			vec![numtok!(12), Token::unit(Pound), Token::Operator(Plus)],
		);

		run_lex(
			"5 π m",
			vec![numtok!(5), Token::Constant(Pi), Token::unit(Meter)],
		);
		run_lex(
			"5 Ω + 2 mΩ",
			vec![
				numtok!(5),
				Token::unit(Ohm),
				Token::Operator(Plus),
				numtok!(2),
				Token::unit(Milliohm),
			],
		);
	}
}
