diff --git a/build.rs b/build.rs index 2ef0bdd..1536e62 100644 --- a/build.rs +++ b/build.rs @@ -154,7 +154,7 @@ fn tokenize(line: &str) -> StrResult { Line::ModuleEnd } else if let Some(rest) = head.strip_prefix('.') { for part in rest.split('.') { - validate_ident(part)?; + validate_ident(part.strip_suffix('?').unwrap_or(part))?; } let value = decode_value(tail.ok_or("missing char")?)?; Line::Variant(ModifierSet::from_raw_dotted(rest), value) diff --git a/src/lib.rs b/src/lib.rs index 0c11a5a..6872cd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ impl Symbol { match self { Self::Single(c) => modifs.is_empty().then_some((*c, None)), Self::Multi(list) => { - modifs.best_match_in(list.iter().copied().map(|(m, c, d)| (m, (c, d)))) + modifs.best_match_in(list.iter().copied().map(|(ms, c, d)| (ms, (c, d)))) } } } @@ -107,7 +107,7 @@ impl Symbol { /// Possible modifiers for this symbol. pub fn modifiers(&self) -> impl Iterator + '_ { self.variants() - .flat_map(|(m, _, _)| m.into_iter()) + .flat_map(|(ms, _, _)| ms.into_iter().map(|m| m.name())) .collect::>() .into_iter() } @@ -170,14 +170,17 @@ mod test { }; let variants = s .variants() - .map(|(m, v, _)| (m.into_iter().collect::>(), v)) + .map(|(ms, v, _)| { + (ms.into_iter().map(|m| m.as_str()).collect::>(), v) + }) .collect::>(); let control = control .iter() - .map(|&(m, v)| { + .map(|&(ms, v)| { ( - ModifierSet::from_raw_dotted(m) + ModifierSet::from_raw_dotted(ms) .into_iter() + .map(|m| m.as_str()) .collect::>(), v, ) @@ -187,4 +190,98 @@ mod test { assert_eq!(variants, control); } } + + #[test] + fn no_overlap() { + recur("", ROOT); + + /// Iterate over all symbols in a module, recursing into submodules. + fn recur(prefix: &str, m: Module) { + for (name, b) in m.iter() { + match b.def { + Def::Module(m) => { + let new_prefix = if prefix.is_empty() { + name.to_string() + } else { + prefix.to_string() + "." + name + }; + recur(&new_prefix, m); + } + Def::Symbol(s) => check_symbol(prefix, name, s), + } + } + } + + /// Check the no overlap rule for a single symbol + fn check_symbol(prefix: &str, name: &str, sym: Symbol) { + // maximum number of modifiers per variant (we don't need to check more than this). + let max_modifs = + sym.variants().map(|(ms, ..)| ms.iter().count()).max().unwrap(); + let modifs = sym.modifiers().collect::>(); + let max_index = modifs.len().saturating_sub(1); + + for k in 0..=max_modifs { + let mut indices = (0..k).collect::>(); + loop { + let mset = indices.iter().map(|i| modifs[*i]).fold( + ModifierSet::::default(), + |mut res, m| { + res.insert_raw(m); + res + }, + ); + + if sym.variants().filter(|(ms, ..)| mset.is_candidate(*ms)).count() + > 1 + { + panic!( + "Overlap in symbol {prefix}.{name} for modifiers {}", + mset.as_str() + ); + } + + if next_subseq(&mut indices, max_index).is_none() { + break; + } + } + } + } + + /// Produces the (lexicographically) next strictly increasing array of numbers + /// less than or equal to `max_index`. + /// + /// Example: + /// ```text + /// [0,1,2], [0,1,3], [0,1,4], [0,2,3], [0,2,4], [0,3,4], [1,2,3], [1,2,4], [1,3,4], [2,3,4] + /// ``` + /// + /// Invariants: + /// - `indices` is strictly increasing + /// - All elements of `indices` are `<= max_index` + /// - `indices.len() <= max_index + 1` (this is already implied by the previous two) + fn next_subseq(indices: &mut [usize], max_index: usize) -> Option<()> { + match indices { + [] => None, + [single] => { + if *single < max_index { + *single += 1; + Some(()) + } else { + None + } + } + [left @ .., last] => { + assert_ne!(max_index, 0); + assert_ne!(left.len(), 0); + if *last < max_index { + *last += 1; + } else { + next_subseq(left, max_index - 1)?; + *last = left.last().unwrap() + 1; + } + Some(()) + } + } + } + } } diff --git a/src/modules/emoji.txt b/src/modules/emoji.txt index 697b637..61b5dea 100644 --- a/src/modules/emoji.txt +++ b/src/modules/emoji.txt @@ -17,34 +17,34 @@ anchor โš“ anger ๐Ÿ’ข ant ๐Ÿœ apple - .green ๐Ÿ + .green? ๐Ÿ .red ๐ŸŽ arm - .mech ๐Ÿฆพ + .mech? ๐Ÿฆพ .muscle ๐Ÿ’ช .selfie ๐Ÿคณ arrow - .r.filled โžก - .r.hook โ†ช - .r.soon ๐Ÿ”œ - .l.filled โฌ… + .r?.filled? โžก + .r?.hook โ†ช + .r?.soon ๐Ÿ”œ + .l.filled? โฌ… .l.hook โ†ฉ - .l.back ๐Ÿ”™ - .l.end ๐Ÿ”š - .t.filled โฌ† - .t.curve โคด - .t.top ๐Ÿ” - .b.filled โฌ‡ + .l?.back ๐Ÿ”™ + .l?.end ๐Ÿ”š + .t.filled? โฌ† + .t?.curve โคด + .t?.top ๐Ÿ” + .b.filled? โฌ‡ .b.curve โคต .l.r โ†” - .l.r.on ๐Ÿ”› + .l?.r?.on ๐Ÿ”› .t.b โ†• .bl โ†™ .br โ†˜ .tl โ†– .tr โ†— arrows - .cycle ๐Ÿ”„ + .cycle? ๐Ÿ”„ ast * .box โœณ atm ๐Ÿง @@ -65,7 +65,7 @@ baggageclaim ๐Ÿ›„ baguette ๐Ÿฅ– balloon ๐ŸŽˆ ballot - .check โ˜‘ + .check? โ˜‘ ballotbox ๐Ÿ—ณ banana ๐ŸŒ banjo ๐Ÿช• @@ -82,7 +82,7 @@ bathtub ๐Ÿ›€ battery ๐Ÿ”‹ .low ๐Ÿชซ beach - .palm ๐Ÿ + .palm? ๐Ÿ .umbrella ๐Ÿ– beads ๐Ÿ“ฟ beans ๐Ÿซ˜ @@ -116,7 +116,7 @@ blowfish ๐Ÿก blueberries ๐Ÿซ boar ๐Ÿ— boat - .sail โ›ต + .sail? โ›ต .row ๐Ÿšฃ .motor ๐Ÿ›ฅ .speed ๐Ÿšค @@ -125,7 +125,7 @@ bolt ๐Ÿ”ฉ bomb ๐Ÿ’ฃ bone ๐Ÿฆด book - .red ๐Ÿ“• + .red? ๐Ÿ“• .blue ๐Ÿ“˜ .green ๐Ÿ“— .orange ๐Ÿ“™ @@ -138,7 +138,7 @@ bordercontrol ๐Ÿ›‚ bouquet ๐Ÿ’ bow ๐Ÿน bowl - .spoon ๐Ÿฅฃ + .spoon? ๐Ÿฅฃ .steam ๐Ÿœ bowling ๐ŸŽณ boxing ๐ŸฅŠ @@ -148,26 +148,26 @@ bread ๐Ÿž brick ๐Ÿงฑ bride ๐Ÿ‘ฐ bridge - .fog ๐ŸŒ + .fog? ๐ŸŒ .night ๐ŸŒ‰ briefcase ๐Ÿ’ผ briefs ๐Ÿฉฒ brightness - .high ๐Ÿ”† + .high? ๐Ÿ”† .low ๐Ÿ”… broccoli ๐Ÿฅฆ broom ๐Ÿงน brush ๐Ÿ–Œ bubble - .speech.r ๐Ÿ’ฌ - .speech.l ๐Ÿ—จ + .speech?.r? ๐Ÿ’ฌ + .speech?.l ๐Ÿ—จ .thought ๐Ÿ’ญ - .anger.r ๐Ÿ—ฏ + .anger.r? ๐Ÿ—ฏ bubbles ๐Ÿซง bubbletea ๐Ÿง‹ bucket ๐Ÿชฃ buffalo - .water ๐Ÿƒ + .water? ๐Ÿƒ bug ๐Ÿ› builder ๐Ÿ‘ท burger ๐Ÿ” @@ -183,7 +183,7 @@ button ๐Ÿ”ฒ .alt ๐Ÿ”ณ .radio ๐Ÿ”˜ cabinet - .file ๐Ÿ—„ + .file? ๐Ÿ—„ cablecar ๐Ÿš  .small ๐Ÿšก cactus ๐ŸŒต @@ -199,7 +199,7 @@ camel ๐Ÿซ camera ๐Ÿ“ท .flash ๐Ÿ“ธ .movie ๐ŸŽฅ - .movie.box ๐ŸŽฆ + .movie?.box ๐ŸŽฆ .video ๐Ÿ“น camping ๐Ÿ• can ๐Ÿฅซ @@ -215,37 +215,37 @@ car ๐Ÿš— .rickshaw ๐Ÿ›บ .suv ๐Ÿš™ card - .credit ๐Ÿ’ณ + .credit? ๐Ÿ’ณ .id ๐Ÿชช cardindex ๐Ÿ“‡ carrot ๐Ÿฅ• cart ๐Ÿ›’ cassette ๐Ÿ“ผ castle - .eu ๐Ÿฐ + .eu? ๐Ÿฐ .jp ๐Ÿฏ cat ๐Ÿˆ .face ๐Ÿฑ - .face.angry ๐Ÿ˜พ - .face.cry ๐Ÿ˜ฟ - .face.heart ๐Ÿ˜ป - .face.joy ๐Ÿ˜น - .face.kiss ๐Ÿ˜ฝ - .face.laugh ๐Ÿ˜ธ - .face.shock ๐Ÿ™€ - .face.smile ๐Ÿ˜บ - .face.smirk ๐Ÿ˜ผ + .face?.angry ๐Ÿ˜พ + .face?.cry ๐Ÿ˜ฟ + .face?.heart ๐Ÿ˜ป + .face?.joy ๐Ÿ˜น + .face?.kiss ๐Ÿ˜ฝ + .face?.laugh ๐Ÿ˜ธ + .face?.shock ๐Ÿ™€ + .face?.smile ๐Ÿ˜บ + .face?.smirk ๐Ÿ˜ผ chain ๐Ÿ”— chains โ›“ chair ๐Ÿช‘ champagne ๐Ÿพ chart - .bar ๐Ÿ“Š + .bar? ๐Ÿ“Š .up ๐Ÿ“ˆ .down ๐Ÿ“‰ - .yen.up ๐Ÿ’น + .yen.up? ๐Ÿ’น checkmark - .heavy โœ” + .heavy? โœ” .box โœ… cheese ๐Ÿง€ cherries ๐Ÿ’ @@ -253,8 +253,8 @@ chess โ™Ÿ chestnut ๐ŸŒฐ chicken ๐Ÿ” .baby ๐Ÿฅ - .baby.egg ๐Ÿฃ - .baby.head ๐Ÿค + .baby?.egg ๐Ÿฃ + .baby?.head ๐Ÿค .leg ๐Ÿ— .male ๐Ÿ“ child ๐Ÿง’ @@ -266,7 +266,7 @@ church โ›ช cigarette ๐Ÿšฌ .not ๐Ÿšญ circle - .black โšซ + .black? โšซ .blue ๐Ÿ”ต .brown ๐ŸŸค .green ๐ŸŸข @@ -288,8 +288,8 @@ clip ๐Ÿ“Ž clipboard ๐Ÿ“‹ clips ๐Ÿ–‡ clock - .one ๐Ÿ• - .one.thirty ๐Ÿ•œ + .one? ๐Ÿ• + .one?.thirty ๐Ÿ•œ .two ๐Ÿ•‘ .two.thirty ๐Ÿ• .three ๐Ÿ•’ @@ -328,7 +328,7 @@ coat ๐Ÿงฅ .lab ๐Ÿฅผ cockroach ๐Ÿชณ cocktail - .martini ๐Ÿธ + .martini? ๐Ÿธ .tropical ๐Ÿน coconut ๐Ÿฅฅ coffee โ˜• @@ -366,7 +366,7 @@ crutch ๐Ÿฉผ crystal ๐Ÿ”ฎ cucumber ๐Ÿฅ’ cup - .straw ๐Ÿฅค + .straw? ๐Ÿฅค cupcake ๐Ÿง curling ๐ŸฅŒ curry ๐Ÿ› @@ -375,27 +375,27 @@ customs ๐Ÿ›ƒ cutlery ๐Ÿด cyclone ๐ŸŒ€ dancing - .man ๐Ÿ•บ + .man? ๐Ÿ•บ .woman ๐Ÿ’ƒ .women.bunny ๐Ÿ‘ฏ darts ๐ŸŽฏ dash - .wave.double ใ€ฐ + .wave?.double? ใ€ฐ deer ๐ŸฆŒ desert ๐Ÿœ detective ๐Ÿ•ต diamond - .blue ๐Ÿ”ท - .blue.small ๐Ÿ”น + .blue? ๐Ÿ”ท + .blue?.small ๐Ÿ”น .orange ๐Ÿ”ถ .orange.small ๐Ÿ”ธ .dot ๐Ÿ’  die ๐ŸŽฒ dino - .pod ๐Ÿฆ• + .pod? ๐Ÿฆ• .rex ๐Ÿฆ– disc - .cd ๐Ÿ’ฟ + .cd? ๐Ÿ’ฟ .dvd ๐Ÿ“€ .mini ๐Ÿ’ฝ discoball ๐Ÿชฉ @@ -411,7 +411,7 @@ donkey ๐Ÿซ donut ๐Ÿฉ door ๐Ÿšช dove - .peace ๐Ÿ•Š + .peace? ๐Ÿ•Š dragon ๐Ÿ‰ .face ๐Ÿฒ dress ๐Ÿ‘— @@ -428,7 +428,7 @@ ear ๐Ÿ‘‚ .aid ๐Ÿฆป egg ๐Ÿฅš eighteen - .not ๐Ÿ”ž + .not? ๐Ÿ”ž elephant ๐Ÿ˜ elevator ๐Ÿ›— elf ๐Ÿง @@ -442,9 +442,9 @@ extinguisher ๐Ÿงฏ eye ๐Ÿ‘ eyes ๐Ÿ‘€ face - .grin ๐Ÿ˜€ + .grin? ๐Ÿ˜€ .angry ๐Ÿ˜  - .angry.red ๐Ÿ˜ก + .angry?.red ๐Ÿ˜ก .anguish ๐Ÿ˜ง .astonish ๐Ÿ˜ฒ .bandage ๐Ÿค• @@ -457,7 +457,7 @@ face .cover ๐Ÿคญ .cowboy ๐Ÿค  .cry ๐Ÿ˜ญ - .devil.smile ๐Ÿ˜ˆ + .devil.smile? ๐Ÿ˜ˆ .devil.frown ๐Ÿ‘ฟ .diagonal ๐Ÿซค .disguise ๐Ÿฅธ @@ -465,7 +465,7 @@ face .dizzy ๐Ÿ˜ต .dotted ๐Ÿซฅ .down ๐Ÿ˜ž - .down.sweat ๐Ÿ˜“ + .down?.sweat ๐Ÿ˜“ .drool ๐Ÿคค .explode ๐Ÿคฏ .eyeroll ๐Ÿ™„ @@ -489,7 +489,7 @@ face .kiss ๐Ÿ˜— .kiss.smile ๐Ÿ˜™ .kiss.heart ๐Ÿ˜˜ - .kiss.blush ๐Ÿ˜š + .kiss?.blush ๐Ÿ˜š .lick ๐Ÿ˜‹ .lie ๐Ÿคฅ .mask ๐Ÿ˜ท @@ -520,7 +520,7 @@ face .smile.tear ๐Ÿฅฒ .smirk ๐Ÿ˜ .sneeze ๐Ÿคง - .speak.not ๐Ÿซข + .speak.not? ๐Ÿซข .squint ๐Ÿ˜† .stars ๐Ÿคฉ .straight ๐Ÿ˜‘ @@ -558,7 +558,7 @@ faith .om ๐Ÿ•‰ .orthodox โ˜ฆ .peace โ˜ฎ - .star.dot ๐Ÿ”ฏ + .star.dot? ๐Ÿ”ฏ .worship ๐Ÿ› .yinyang โ˜ฏ falafel ๐Ÿง† @@ -566,14 +566,14 @@ family ๐Ÿ‘ช fax ๐Ÿ“  feather ๐Ÿชถ feeding - .breast ๐Ÿคฑ + .breast? ๐Ÿคฑ fencing ๐Ÿคบ ferriswheel ๐ŸŽก filebox ๐Ÿ—ƒ filedividers ๐Ÿ—‚ film ๐ŸŽž finger - .r ๐Ÿ‘‰ + .r? ๐Ÿ‘‰ .l ๐Ÿ‘ˆ .t ๐Ÿ‘† .t.alt โ˜ @@ -582,7 +582,7 @@ finger .m ๐Ÿ–• fingerprint ๐Ÿซ† fingers - .cross ๐Ÿคž + .cross? ๐Ÿคž .pinch ๐ŸคŒ .snap ๐Ÿซฐ fire ๐Ÿ”ฅ @@ -593,25 +593,25 @@ fish ๐ŸŸ .tropical ๐Ÿ  fishing ๐ŸŽฃ fist - .front ๐Ÿ‘Š + .front? ๐Ÿ‘Š .r ๐Ÿคœ .l ๐Ÿค› .raised โœŠ flag - .black ๐Ÿด + .black? ๐Ÿด .white ๐Ÿณ .goal ๐Ÿ .golf โ›ณ .red ๐Ÿšฉ flags - .jp.crossed ๐ŸŽŒ + .jp?.crossed? ๐ŸŽŒ flamingo ๐Ÿฆฉ flashlight ๐Ÿ”ฆ flatbread ๐Ÿซ“ fleur โšœ floppy ๐Ÿ’พ flower - .hibiscus ๐ŸŒบ + .hibiscus? ๐ŸŒบ .hyacinth ๐Ÿชป .lotus ๐Ÿชท .pink ๐ŸŒธ @@ -637,7 +637,7 @@ free ๐Ÿ†“ fries ๐ŸŸ frisbee ๐Ÿฅ frog - .face ๐Ÿธ + .face? ๐Ÿธ fuelpump โ›ฝ garlic ๐Ÿง„ gear โš™ @@ -648,14 +648,14 @@ ginger ๐Ÿซš giraffe ๐Ÿฆ’ girl ๐Ÿ‘ง glass - .clink ๐Ÿฅ‚ + .clink? ๐Ÿฅ‚ .milk ๐Ÿฅ› .pour ๐Ÿซ— .tumbler ๐Ÿฅƒ glasses ๐Ÿ‘“ .sun ๐Ÿ•ถ globe - .am ๐ŸŒŽ + .am? ๐ŸŒŽ .as.au ๐ŸŒ .eu.af ๐ŸŒ .meridian ๐ŸŒ @@ -668,7 +668,7 @@ goose ๐Ÿชฟ gorilla ๐Ÿฆ grapes ๐Ÿ‡ guard - .man ๐Ÿ’‚ + .man? ๐Ÿ’‚ guitar ๐ŸŽธ gymnastics ๐Ÿคธ haircut ๐Ÿ’‡ @@ -678,10 +678,10 @@ hammer ๐Ÿ”จ .wrench ๐Ÿ›  hamsa ๐Ÿชฌ hamster - .face ๐Ÿน + .face? ๐Ÿน hand - .raised โœ‹ - .raised.alt ๐Ÿคš + .raised? โœ‹ + .raised?.alt ๐Ÿคš .r ๐Ÿซฑ .l ๐Ÿซฒ .t ๐Ÿซด @@ -692,7 +692,7 @@ hand .part ๐Ÿ–– .peace โœŒ .pinch ๐Ÿค - .pushing.l ๐Ÿซท + .pushing.l? ๐Ÿซท .pushing.r ๐Ÿซธ .rock ๐Ÿค˜ .splay ๐Ÿ– @@ -706,7 +706,7 @@ handholding .woman.man ๐Ÿ‘ซ .woman.woman ๐Ÿ‘ญ hands - .folded ๐Ÿ™ + .folded? ๐Ÿ™ .palms ๐Ÿคฒ .clap ๐Ÿ‘ .heart ๐Ÿซถ @@ -716,7 +716,7 @@ hands harp ๐Ÿช‰ hash # hat - .ribbon ๐Ÿ‘’ + .ribbon? ๐Ÿ‘’ .top ๐ŸŽฉ headphone ๐ŸŽง heart โค @@ -746,7 +746,7 @@ hedgehog ๐Ÿฆ” helicopter ๐Ÿš helix ๐Ÿงฌ helmet - .cross โ›‘ + .cross? โ›‘ .military ๐Ÿช– hippo ๐Ÿฆ› hockey ๐Ÿ‘ @@ -755,7 +755,7 @@ honey ๐Ÿฏ hongbao ๐Ÿงง hook ๐Ÿช horn - .postal ๐Ÿ“ฏ + .postal? ๐Ÿ“ฏ horse ๐ŸŽ .carousel ๐ŸŽ  .face ๐Ÿด @@ -833,7 +833,7 @@ label ๐Ÿท lacrosse ๐Ÿฅ ladder ๐Ÿชœ lamp - .diya ๐Ÿช” + .diya? ๐Ÿช” laptop ๐Ÿ’ป a ๐Ÿ…ฐ ab ๐Ÿ†Ž @@ -841,9 +841,9 @@ b ๐Ÿ…ฑ cl ๐Ÿ†‘ o ๐Ÿ…พ leaf - .clover.three โ˜˜ + .clover.three? โ˜˜ .clover.four ๐Ÿ€ - .fall ๐Ÿ‚ + .fall? ๐Ÿ‚ .herb ๐ŸŒฟ .maple ๐Ÿ .wind ๐Ÿƒ @@ -853,7 +853,7 @@ leg ๐Ÿฆต lemon ๐Ÿ‹ leopard ๐Ÿ† letter - .love ๐Ÿ’Œ + .love? ๐Ÿ’Œ liberty ๐Ÿ—ฝ lightbulb ๐Ÿ’ก lightning โšก @@ -875,21 +875,21 @@ lungs ๐Ÿซ mage ๐Ÿง™ magnet ๐Ÿงฒ magnify - .r ๐Ÿ”Ž + .r? ๐Ÿ”Ž .l ๐Ÿ” mahjong - .dragon.red ๐Ÿ€„ + .dragon?.red? ๐Ÿ€„ mail โœ‰ .arrow ๐Ÿ“ฉ mailbox - .closed.empty ๐Ÿ“ช - .closed.full ๐Ÿ“ซ - .open.empty ๐Ÿ“ญ + .closed?.empty? ๐Ÿ“ช + .closed?.full ๐Ÿ“ซ + .open.empty? ๐Ÿ“ญ .open.full ๐Ÿ“ฌ mammoth ๐Ÿฆฃ mango ๐Ÿฅญ map - .world ๐Ÿ—บ + .world? ๐Ÿ—บ .jp ๐Ÿ—พ maracas ๐Ÿช‡ martialarts ๐Ÿฅ‹ @@ -899,7 +899,7 @@ matryoshka ๐Ÿช† meat ๐Ÿฅฉ .bone ๐Ÿ– medal - .first ๐Ÿฅ‡ + .first? ๐Ÿฅ‡ .second ๐Ÿฅˆ .third ๐Ÿฅ‰ .sports ๐Ÿ… @@ -917,7 +917,7 @@ milkyway ๐ŸŒŒ mirror ๐Ÿชž mixer ๐ŸŽ› money - .bag ๐Ÿ’ฐ + .bag? ๐Ÿ’ฐ .dollar ๐Ÿ’ต .euro ๐Ÿ’ถ .pound ๐Ÿ’ท @@ -925,22 +925,22 @@ money .wings ๐Ÿ’ธ monkey ๐Ÿ’ .face ๐Ÿต - .hear.not ๐Ÿ™‰ - .see.not ๐Ÿ™ˆ - .speak.not ๐Ÿ™Š + .hear.not? ๐Ÿ™‰ + .see.not? ๐Ÿ™ˆ + .speak.not? ๐Ÿ™Š moon - .crescent ๐ŸŒ™ + .crescent? ๐ŸŒ™ .full ๐ŸŒ• - .full.face ๐ŸŒ + .full?.face ๐ŸŒ .new ๐ŸŒ‘ .new.face ๐ŸŒš - .wane.one ๐ŸŒ– - .wane.two ๐ŸŒ— - .wane.three.face ๐ŸŒœ - .wane.three ๐ŸŒ˜ - .wax.one ๐ŸŒ’ + .wane.one? ๐ŸŒ– + .wane?.two ๐ŸŒ— + .wane.three?.face ๐ŸŒœ + .wane?.three ๐ŸŒ˜ + .wax.one? ๐ŸŒ’ .wax.two ๐ŸŒ“ - .wax.two.face ๐ŸŒ› + .wax.two?.face ๐ŸŒ› .wax.three ๐ŸŒ” moose ๐ŸซŽ mortarboard ๐ŸŽ“ @@ -962,13 +962,13 @@ museum ๐Ÿ› mushroom ๐Ÿ„ musicalscore ๐ŸŽผ nails - .polish ๐Ÿ’… + .polish? ๐Ÿ’… namebadge ๐Ÿ“› nazar ๐Ÿงฟ necktie ๐Ÿ‘” needle ๐Ÿชก nest - .empty ๐Ÿชน + .empty? ๐Ÿชน .eggs ๐Ÿชบ new ๐Ÿ†• newspaper ๐Ÿ“ฐ @@ -1003,7 +1003,7 @@ page ๐Ÿ“„ .pencil ๐Ÿ“ pager ๐Ÿ“Ÿ pages - .tabs ๐Ÿ“‘ + .tabs? ๐Ÿ“‘ painting ๐Ÿ–ผ palette ๐ŸŽจ pancakes ๐Ÿฅž @@ -1022,7 +1022,7 @@ pear ๐Ÿ pedestrian ๐Ÿšถ .not ๐Ÿšท pen - .ball ๐Ÿ–Š + .ball? ๐Ÿ–Š .fountain ๐Ÿ–‹ pencil โœ penguin ๐Ÿง @@ -1078,9 +1078,9 @@ planet ๐Ÿช plant ๐Ÿชด plaster ๐Ÿฉน plate - .cutlery ๐Ÿฝ + .cutlery? ๐Ÿฝ playback - .down โฌ + .down? โฌ .eject โ .forward โฉ .pause โธ @@ -1096,14 +1096,14 @@ playback .toggle โฏ .up โซ playingcard - .flower ๐ŸŽด + .flower? ๐ŸŽด .joker ๐Ÿƒ plunger ๐Ÿช  policeofficer ๐Ÿ‘ฎ poo ๐Ÿ’ฉ popcorn ๐Ÿฟ post - .eu ๐Ÿค + .eu? ๐Ÿค .jp ๐Ÿฃ postbox ๐Ÿ“ฎ potato ๐Ÿฅ” @@ -1114,12 +1114,12 @@ present ๐ŸŽ pretzel ๐Ÿฅจ printer ๐Ÿ–จ prints - .foot ๐Ÿ‘ฃ + .foot? ๐Ÿ‘ฃ .paw ๐Ÿพ prohibited ๐Ÿšซ projector ๐Ÿ“ฝ pumpkin - .lantern ๐ŸŽƒ + .lantern? ๐ŸŽƒ purse ๐Ÿ‘› quest โ“ .white โ” @@ -1162,7 +1162,7 @@ salad ๐Ÿฅ— salt ๐Ÿง‚ sandwich ๐Ÿฅช santa - .man ๐ŸŽ… + .man? ๐ŸŽ… .woman ๐Ÿคถ satdish ๐Ÿ“ก satellite ๐Ÿ›ฐ @@ -1183,13 +1183,13 @@ seedling ๐ŸŒฑ shark ๐Ÿฆˆ sheep ๐Ÿ‘ shell - .spiral ๐Ÿš + .spiral? ๐Ÿš shield ๐Ÿ›ก ship ๐Ÿšข .cruise ๐Ÿ›ณ .ferry โ›ด shirt - .sports ๐ŸŽฝ + .sports? ๐ŸŽฝ .t ๐Ÿ‘• shoe ๐Ÿ‘ž .ballet ๐Ÿฉฐ @@ -1198,7 +1198,7 @@ shoe ๐Ÿ‘ž .hike ๐Ÿฅพ .ice โ›ธ .roller ๐Ÿ›ผ - .sandal.heel ๐Ÿ‘ก + .sandal.heel? ๐Ÿ‘ก .ski ๐ŸŽฟ .sneaker ๐Ÿ‘Ÿ .tall ๐Ÿ‘ข @@ -1212,7 +1212,7 @@ shrimp ๐Ÿฆ .fried ๐Ÿค shrine โ›ฉ sign - .crossing ๐Ÿšธ + .crossing? ๐Ÿšธ .stop ๐Ÿ›‘ silhouette ๐Ÿ‘ค .double ๐Ÿ‘ฅ @@ -1221,7 +1221,7 @@ silhouette ๐Ÿ‘ค siren ๐Ÿšจ skateboard ๐Ÿ›น skewer - .dango ๐Ÿก + .dango? ๐Ÿก .oden ๐Ÿข skiing โ›ท skull ๐Ÿ’€ @@ -1245,7 +1245,7 @@ sos ๐Ÿ†˜ soup ๐Ÿฒ spaghetti ๐Ÿ sparkle - .box โ‡ + .box? โ‡ sparkler ๐ŸŽ‡ sparkles โœจ speaker ๐Ÿ”ˆ @@ -1259,10 +1259,10 @@ splatter ๐ŸซŸ sponge ๐Ÿงฝ spoon ๐Ÿฅ„ square - .black โฌ› - .black.tiny โ–ช - .black.small โ—พ - .black.medium โ—ผ + .black? โฌ› + .black?.tiny โ–ช + .black?.small โ—พ + .black?.medium โ—ผ .white โฌœ .white.tiny โ–ซ .white.small โ—ฝ @@ -1283,7 +1283,7 @@ star โญ .shoot ๐ŸŒ  stethoscope ๐Ÿฉบ store - .big ๐Ÿฌ + .big? ๐Ÿฌ .small ๐Ÿช strawberry ๐Ÿ“ suit @@ -1328,10 +1328,10 @@ testtube ๐Ÿงช thermometer ๐ŸŒก thread ๐Ÿงต thumb - .up ๐Ÿ‘ + .up? ๐Ÿ‘ .down ๐Ÿ‘Ž ticket - .event ๐ŸŽŸ + .event? ๐ŸŽŸ .travel ๐ŸŽซ tiger ๐Ÿ… .face ๐Ÿฏ @@ -1346,11 +1346,11 @@ tooth ๐Ÿฆท toothbrush ๐Ÿชฅ tornado ๐ŸŒช tower - .tokyo ๐Ÿ—ผ + .tokyo? ๐Ÿ—ผ trackball ๐Ÿ–ฒ tractor ๐Ÿšœ trafficlight - .v ๐Ÿšฆ + .v? ๐Ÿšฆ .h ๐Ÿšฅ train ๐Ÿš† .car ๐Ÿšƒ @@ -1359,7 +1359,7 @@ train ๐Ÿš† .mono ๐Ÿš .mountain ๐Ÿšž .speed ๐Ÿš„ - .speed.bullet ๐Ÿš… + .speed?.bullet ๐Ÿš… .steam ๐Ÿš‚ .stop ๐Ÿš‰ .suspend ๐ŸšŸ @@ -1367,21 +1367,21 @@ train ๐Ÿš† .tram.car ๐Ÿš‹ transgender โšง tray - .inbox ๐Ÿ“ฅ + .inbox? ๐Ÿ“ฅ .mail ๐Ÿ“จ .outbox ๐Ÿ“ค tree - .deciduous ๐ŸŒณ + .deciduous? ๐ŸŒณ .evergreen ๐ŸŒฒ .leafless ๐Ÿชพ .palm ๐ŸŒด .xmas ๐ŸŽ„ triangle - .r โ–ถ + .r? โ–ถ .l โ—€ .t ๐Ÿ”ผ .b ๐Ÿ”ฝ - .t.red ๐Ÿ”บ + .t?.red ๐Ÿ”บ .b.red ๐Ÿ”ป trident ๐Ÿ”ฑ troll ๐ŸงŒ @@ -1395,7 +1395,7 @@ turtle ๐Ÿข tv ๐Ÿ“บ ufo ๐Ÿ›ธ umbrella - .open โ˜‚ + .open? โ˜‚ .closed ๐ŸŒ‚ .rain โ˜” .sun โ›ฑ @@ -1445,7 +1445,7 @@ yarn ๐Ÿงถ yoyo ๐Ÿช€ zebra ๐Ÿฆ“ zodiac - .aquarius โ™’ + .aquarius? โ™’ .aries โ™ˆ .cancer โ™‹ .capri โ™‘ diff --git a/src/modules/sym.txt b/src/modules/sym.txt index 42a7b3d..d62534c 100644 --- a/src/modules/sym.txt +++ b/src/modules/sym.txt @@ -23,53 +23,53 @@ space \u{20} // Delimiters. paren - .l ( - .l.double โฆ… + .l? ( + .l?.double โฆ… .r ) .r.double โฆ† .t โœ .b โ brace - .l \u{7B} - .l.double โฆƒ + .l? \u{7B} + .l?.double โฆƒ .r \u{7D} .r.double โฆ„ .t โž .b โŸ bracket - .l [ - .l.double โŸฆ + .l? [ + .l?.double โŸฆ .r ] .r.double โŸง .t โŽด .b โŽต shell - .l โฒ - .l.double โŸฌ + .l? โฒ + .l?.double โŸฌ .r โณ .r.double โŸญ .t โ  .b โก bar - .v | - .v.double โ€– - .v.triple โฆ€ - .v.broken ยฆ - .v.o โฆถ + .v? | + .v?.double โ€– + .v?.triple โฆ€ + .v?.broken ยฆ + .v?.o โฆถ @deprecated: `bar.v.circle` is deprecated, use `bar.v.o` instead - .v.circle โฆถ + .v?.circle โฆถ .h โ€• fence - .l โง˜ - .l.double โงš + .l? โง˜ + .l?.double โงš .r โง™ .r.double โง› .dotted โฆ™ angle โˆ  .l โŸจ - .l.curly โงผ - .l.dot โฆ‘ - .l.double โŸช + .l?.curly โงผ + .l?.dot โฆ‘ + .l?.double โŸช .r โŸฉ .r.curly โงฝ .r.dot โฆ’ @@ -83,7 +83,7 @@ angle โˆ  .right.rev โฏพ .right.arc โŠพ .right.dot โฆ - .right.sq โฆœ + .right?.sq โฆœ .s โฆž .spatial โŸ€ .spheric โˆข @@ -92,18 +92,18 @@ angle โˆ  @deprecated: `angle.spheric.top` is deprecated, use `angle.spheric.t` instead .spheric.top โฆก ceil - .l โŒˆ + .l? โŒˆ .r โŒ‰ floor - .l โŒŠ + .l? โŒŠ .r โŒ‹ // Punctuation. amp & .inv โ…‹ ast - .op โˆ— - .op.o โŠ› + .op? โˆ— + .op?.o โŠ› .basic * .low โŽ .double โ‘ @@ -137,7 +137,7 @@ dagger โ€  .r โธท .inv โธธ dash - .en โ€“ + .en? โ€“ .em โ€” .em.two โธบ .em.three โธป @@ -149,7 +149,7 @@ dash .circle โŠ .wave.double ใ€ฐ dot - .op โ‹… + .op? โ‹… .basic \u{2E} .c ยท .o โŠ™ @@ -194,13 +194,13 @@ slash / .triple โซป .big โงธ dots - .h.c โ‹ฏ - .h โ€ฆ + .h? โ€ฆ + .h?.c โ‹ฏ .v โ‹ฎ .down โ‹ฑ .up โ‹ฐ tilde - .op โˆผ + .op? โˆผ .basic ~ .dot โฉช .eq โ‰ƒ @@ -225,19 +225,19 @@ diaer ยจ grave ` macron ยฏ quote - .double " + .double? " .single ' - .l.double โ€œ + .l.double? โ€œ .l.single โ€˜ - .r.double โ€ + .r.double? โ€ .r.single โ€™ - .angle.l.double ยซ - .angle.l.single โ€น - .angle.r.double ยป + .angle.l?.double? ยซ + .angle.l?.single โ€น + .angle.r.double? ยป .angle.r.single โ€บ - .high.double โ€Ÿ + .high.double? โ€Ÿ .high.single โ€› - .low.double โ€ž + .low.double? โ€ž .low.single โ€š prime โ€ฒ .rev โ€ต @@ -290,7 +290,7 @@ times ร— @deprecated: `times.circle.big` is deprecated, use `times.o.big` instead .circle.big โจ‚ .div โ‹‡ - .three.l โ‹‹ + .three.l? โ‹‹ .three.r โ‹Œ .l โ‹‰ .r โ‹Š @@ -425,13 +425,13 @@ asymp โ‰ // Set theory. emptyset โˆ… - .arrow.r โฆณ + .arrow.r? โฆณ .arrow.l โฆด .bar โฆฑ .circle โฆฒ .rev โฆฐ nothing โˆ… - .arrow.r โฆณ + .arrow.r? โฆณ .arrow.l โฆด .bar โฆฑ .circle โฆฒ @@ -513,7 +513,7 @@ sum โˆ‘ product โˆ .co โˆ integral โˆซ - .arrow.hook โจ— + .arrow.hook? โจ— .ccw โจ‘ .cont โˆฎ .cont.ccw โˆณ @@ -595,7 +595,7 @@ parallel โˆฅ .equiv โฉจ .not โˆฆ .slanted.eq โงฃ - .slanted.eq.tilde โงค + .slanted?.eq.tilde โงค .slanted.equiv โงฅ .tilde โซณ perp โŸ‚ @@ -627,12 +627,12 @@ join โจ .l โŸ• .l.r โŸ— hourglass - .stroked โง– + .stroked? โง– .filled โง— degree ยฐ smash โจณ power - .standby โป + .standby? โป .on โฝ .off โญ˜ .on.off โผ @@ -668,7 +668,7 @@ pound ยฃ riel แŸ› ruble โ‚ฝ rupee - .indian โ‚น + .indian? โ‚น .generic โ‚จ .tamil เฏน .wancho ๐ž‹ฟ @@ -711,18 +711,18 @@ trademark โ„ข .service โ„  maltese โœ  suit - .club.filled โ™ฃ + .club.filled? โ™ฃ .club.stroked โ™ง - .diamond.filled โ™ฆ + .diamond.filled? โ™ฆ .diamond.stroked โ™ข - .heart.filled โ™ฅ + .heart.filled? โ™ฅ .heart.stroked โ™ก - .spade.filled โ™  + .spade.filled? โ™  .spade.stroked โ™ค // Music. note - .up ๐ŸŽœ + .up? ๐ŸŽœ .down ๐ŸŽ .whole ๐… .half ๐…ž @@ -736,7 +736,7 @@ note .grace ๐†• .grace.slash ๐†” rest - .whole ๐„ป + .whole? ๐„ป .multiple ๐„บ .multiple.measure ๐„ฉ .half ๐„ผ @@ -769,10 +769,10 @@ bullet โ€ข .l โŒ .r โ circle - .stroked โ—‹ - .stroked.tiny โˆ˜ - .stroked.small โšฌ - .stroked.big โ—ฏ + .stroked? โ—‹ + .stroked?.tiny โˆ˜ + .stroked?.small โšฌ + .stroked?.big โ—ฏ .filled โ— .filled.tiny โฆ .filled.small โˆ™ @@ -781,27 +781,27 @@ circle @deprecated: `circle.nested` is deprecated, use `compose.o` instead .nested โŠš ellipse - .stroked.h โฌญ - .stroked.v โฌฏ - .filled.h โฌฌ + .stroked?.h? โฌญ + .stroked?.v โฌฏ + .filled.h? โฌฌ .filled.v โฌฎ triangle - .stroked.t โ–ณ - .stroked.b โ–ฝ - .stroked.r โ–ท - .stroked.l โ— - .stroked.bl โ—บ - .stroked.br โ—ฟ - .stroked.tl โ—ธ - .stroked.tr โ—น - .stroked.small.t โ–ต - .stroked.small.b โ–ฟ - .stroked.small.r โ–น - .stroked.small.l โ—ƒ - .stroked.rounded ๐Ÿ›† - .stroked.nested โŸ - .stroked.dot โ—ฌ - .filled.t โ–ฒ + .stroked?.t? โ–ณ + .stroked?.b โ–ฝ + .stroked?.r โ–ท + .stroked?.l โ— + .stroked?.bl โ—บ + .stroked?.br โ—ฟ + .stroked?.tl โ—ธ + .stroked?.tr โ—น + .stroked?.small.t? โ–ต + .stroked?.small.b โ–ฟ + .stroked?.small.r โ–น + .stroked?.small.l โ—ƒ + .stroked?.rounded ๐Ÿ›† + .stroked?.nested โŸ + .stroked?.dot โ—ฌ + .filled.t? โ–ฒ .filled.b โ–ผ .filled.r โ–ถ .filled.l โ—€ @@ -809,98 +809,98 @@ triangle .filled.br โ—ข .filled.tl โ—ค .filled.tr โ—ฅ - .filled.small.t โ–ด + .filled.small.t? โ–ด .filled.small.b โ–พ .filled.small.r โ–ธ .filled.small.l โ—‚ square - .stroked โ–ก - .stroked.tiny โ–ซ - .stroked.small โ—ฝ - .stroked.medium โ—ป - .stroked.big โฌœ - .stroked.dotted โฌš - .stroked.rounded โ–ข + .stroked? โ–ก + .stroked?.tiny โ–ซ + .stroked?.small โ—ฝ + .stroked?.medium โ—ป + .stroked?.big โฌœ + .stroked?.dotted โฌš + .stroked?.rounded โ–ข .filled โ–  .filled.tiny โ–ช .filled.small โ—พ .filled.medium โ—ผ .filled.big โฌ› rect - .stroked.h โ–ญ - .stroked.v โ–ฏ - .filled.h โ–ฌ + .stroked?.h? โ–ญ + .stroked?.v โ–ฏ + .filled.h? โ–ฌ .filled.v โ–ฎ penta - .stroked โฌ  + .stroked? โฌ  .filled โฌŸ hexa - .stroked โฌก + .stroked? โฌก .filled โฌข diamond - .stroked โ—‡ - .stroked.small โ‹„ - .stroked.medium โฌฆ - .stroked.dot โŸ + .stroked? โ—‡ + .stroked?.small โ‹„ + .stroked?.medium โฌฆ + .stroked?.dot โŸ .filled โ—† .filled.medium โฌฅ .filled.small โฌฉ lozenge - .stroked โ—Š - .stroked.small โฌซ - .stroked.medium โฌจ + .stroked? โ—Š + .stroked?.small โฌซ + .stroked?.medium โฌจ .filled โงซ .filled.small โฌช .filled.medium โฌง parallelogram - .stroked โ–ฑ + .stroked? โ–ฑ .filled โ–ฐ star - .op โ‹† + .op? โ‹† .stroked โ˜† .filled โ˜… // Arrows, harpoons, and tacks. arrow - .r โ†’ - .r.long.bar โŸผ - .r.bar โ†ฆ - .r.curve โคท - .r.turn โฎŽ - .r.dashed โ‡ข - .r.dotted โค‘ - .r.double โ‡’ - .r.double.bar โค‡ - .r.double.long โŸน - .r.double.long.bar โŸพ - .r.double.not โ‡ - .r.double.struck โคƒ - .r.filled โžก - .r.hook โ†ช - .r.long โŸถ - .r.long.squiggly โŸฟ - .r.loop โ†ฌ - .r.not โ†› - .r.quad โญ† - .r.squiggly โ‡ - .r.stop โ‡ฅ - .r.stroked โ‡จ - .r.struck โ‡ธ - .r.dstruck โ‡ป - .r.tail โ†ฃ - .r.tail.struck โค” - .r.tail.dstruck โค• - .r.tilde โฅฒ - .r.triple โ‡› - .r.twohead โ†  - .r.twohead.bar โค… - .r.twohead.struck โค€ - .r.twohead.dstruck โค - .r.twohead.tail โค– - .r.twohead.tail.struck โค— - .r.twohead.tail.dstruck โค˜ - .r.open โ‡พ - .r.wave โ† + .r? โ†’ + .r?.long.bar โŸผ + .r?.bar โ†ฆ + .r?.curve โคท + .r?.turn โฎŽ + .r?.dashed โ‡ข + .r?.dotted โค‘ + .r?.double โ‡’ + .r?.double.bar โค‡ + .r?.double.long โŸน + .r?.double.long.bar โŸพ + .r?.double.not โ‡ + .r?.double.struck โคƒ + .r?.filled โžก + .r?.hook โ†ช + .r?.long โŸถ + .r?.long.squiggly โŸฟ + .r?.loop โ†ฌ + .r?.not โ†› + .r?.quad โญ† + .r?.squiggly โ‡ + .r?.stop โ‡ฅ + .r?.stroked โ‡จ + .r?.struck โ‡ธ + .r?.dstruck โ‡ป + .r?.tail โ†ฃ + .r?.tail.struck โค” + .r?.tail.dstruck โค• + .r?.tilde โฅฒ + .r?.triple โ‡› + .r?.twohead โ†  + .r?.twohead.bar โค… + .r?.twohead.struck โค€ + .r?.twohead.dstruck โค + .r?.twohead.tail โค– + .r?.twohead.tail.struck โค— + .r?.twohead.tail.dstruck โค˜ + .r?.open โ‡พ + .r?.wave โ† .l โ† .l.bar โ†ค .l.curve โคถ @@ -1013,7 +1013,7 @@ arrow .cw.half โ†ท .zigzag โ†ฏ arrows - .rr โ‡‰ + .rr? โ‡‰ .ll โ‡‡ .tt โ‡ˆ .bb โ‡Š @@ -1025,12 +1025,12 @@ arrows .rrr โ‡ถ .lll โฌฑ arrowhead - .t โŒƒ + .t? โŒƒ .b โŒ„ harpoon - .rt โ‡€ - .rt.bar โฅ› - .rt.stop โฅ“ + .rt? โ‡€ + .rt?.bar โฅ› + .rt?.stop โฅ“ .rb โ‡ .rb.bar โฅŸ .rb.stop โฅ— @@ -1061,7 +1061,7 @@ harpoon .tl.br โฅ .tr.bl โฅŒ harpoons - .rtrb โฅค + .rtrb? โฅค .blbr โฅฅ .bltr โฅฏ .lbrb โฅง @@ -1074,18 +1074,18 @@ harpoons .tlbr โฅฎ .tltr โฅฃ tack - .r โŠข - .r.not โŠฌ - .r.long โŸ - .r.short โŠฆ - .r.double โŠจ - .r.double.not โŠญ + .r? โŠข + .r?.not โŠฌ + .r?.long โŸ + .r?.short โŠฆ + .r?.double โŠจ + .r?.double.not โŠญ .l โŠฃ .l.long โŸž .l.short โซž .l.double โซค .t โŠฅ - .t.big โŸ˜ + .t?.big โŸ˜ .t.double โซซ .t.short โซ  .b โŠค @@ -1103,7 +1103,7 @@ delta ฮด digamma ฯ epsilon ฮต .alt ฯต - .alt.rev ฯถ + .alt?.rev ฯถ eta ฮท gamma ฮณ iota ฮน @@ -1226,23 +1226,23 @@ planck ฤง Re โ„œ Im โ„‘ dotless - .i ฤฑ + .i? ฤฑ .j ศท // Miscellany. die - .six โš… + .six? โš… .five โš„ .four โšƒ .three โš‚ .two โš .one โš€ errorbar - .square.stroked โงฎ - .square.filled โงฏ - .diamond.stroked โงฐ + .square?.stroked? โงฎ + .square?.filled โงฏ + .diamond.stroked? โงฐ .diamond.filled โงฑ - .circle.stroked โงฒ + .circle.stroked? โงฒ .circle.filled โงณ gender { diff --git a/src/shared.rs b/src/shared.rs index cc9286e..7d3498b 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -/// A set of modifiers. +/// A set of modifiers, some of which are marked as "optional". /// /// Beware: The [`Eq`] and [`Hash`] implementations are dependent on the /// ordering of the modifiers, in opposition to what a set would usually @@ -15,16 +15,17 @@ pub struct ModifierSet( impl> ModifierSet { /// Constructs a modifier set from a string, where modifiers are separated - /// by the character `.`. + /// by the character `.` and optional modifiers end in the character `?`. + /// (The `?` is not considered part of the modifier.) /// /// `s` should not contain any empty modifiers (i.e. it shouldn't contain - /// the sequence `..`) and no modifier should occur twice. Otherwise, - /// unexpected errors can occur. + /// the sequences `..` or `.?` or end in `.`) and no modifier should occur twice. + /// Otherwise, unexpected errors can occur. pub fn from_raw_dotted(s: S) -> Self { // Checking the other requirement too feels like it would be a bit too // expensive, even for debug mode. debug_assert!( - !s.contains(".."), + !s.contains("..") && !s.contains(".?") && !s.ends_with("."), "ModifierSet::from_dotted called with string containing empty modifier" ); Self(s) @@ -35,6 +36,11 @@ impl> ModifierSet { self.0.is_empty() } + /// Whether `self` has any optional modifier(s). + pub fn has_optional(&self) -> bool { + self.0.contains('?') + } + /// Gets the string of modifiers separated by `.`. pub fn as_str(&self) -> &str { &self.0 @@ -45,10 +51,15 @@ impl> ModifierSet { ModifierSet(&self.0) } - /// Inserts a new modifier into the set. + /// Inserts a new required or optional modifier into the set. /// - /// `m` should not be empty, contain the character `.`, or already be in the - /// set. Otherwise, unexpected errors can occur. + /// `m` should not + /// - be empty, + /// - contain the character `.`, + /// - contain the character `?` other than at the end, or + /// - already be in the set. + /// + /// Otherwise, unexpected errors can occur. pub fn insert_raw(&mut self, m: &str) where S: for<'a> std::ops::AddAssign<&'a str>, @@ -60,20 +71,31 @@ impl> ModifierSet { } /// Iterates over the list of modifiers in an arbitrary order. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.into_iter() } /// Whether the set contains the modifier `m`. + /// + /// Note that the final `?` for optional modifiers is not considered part + /// of the modifier, so they must be passed without it. + /// ```rust + /// # use codex::ModifierSet; + /// let ms = ModifierSet::from_raw_dotted("a?.b"); + /// assert!(ms.contains("a")); + /// assert!(ms.contains("b")); + /// assert!(!ms.contains("c")); + /// ``` pub fn contains(&self, m: &str) -> bool { - self.iter().any(|lhs| lhs == m) + self.iter().any(|lhs| lhs.name() == m) } /// Finds the best match from the list. /// - /// To be considered a match, the modifier set must be a superset of (or - /// equal to) `self`. Among different matches, the best one is selected by - /// the following two criteria (in order): + /// To be considered a match, the modifier set must satisfy + /// `it.required_is_subset(self) && self.is_subset(it)`. + /// Among different matches, the best one is selected by the following two + /// criteria (in order): /// 1. Number of modifiers in common with `self` (more is better). /// 2. Total number of modifiers (fewer is better). /// @@ -86,11 +108,11 @@ impl> ModifierSet { let mut best_score = None; // Find the best table entry with this name. - for candidate in variants.filter(|(set, _)| self.is_subset(*set)) { + for candidate in variants.filter(|(set, _)| self.is_candidate(*set)) { let mut matching = 0; let mut total = 0; for modifier in candidate.0.iter() { - if self.contains(modifier) { + if self.contains(modifier.name()) { matching += 1; } total += 1; @@ -107,8 +129,21 @@ impl> ModifierSet { } /// Whether all modifiers in `self` are also present in `other`. + /// Ignores whether modifiers are optional or not. pub fn is_subset(&self, other: ModifierSet<&str>) -> bool { - self.iter().all(|m| other.contains(m)) + self.iter().all(|m| other.contains(m.name())) + } + + /// Whether all *non-optional* modifiers in `self` are also present in `other`, + /// optional or not. + pub fn required_is_subset(&self, other: ModifierSet<&str>) -> bool { + self.iter() + .filter(|m| !m.is_optional()) + .all(|m| other.contains(m.as_str())) + } + + pub(crate) fn is_candidate(&self, other: ModifierSet<&str>) -> bool { + other.required_is_subset(self.as_deref()) && self.is_subset(other) } } @@ -123,9 +158,44 @@ impl Default for ModifierSet { } } +#[derive(Copy, Clone)] +pub struct Modifier<'a>(&'a str); + +impl<'a> Modifier<'a> { + pub fn as_str(self) -> &'a str { + self.0 + } + + #[inline] + pub fn name(self) -> &'a str { + self.0.strip_suffix('?').unwrap_or(self.0) + } + + #[inline] + pub fn is_optional(self) -> bool { + self.0.ends_with('?') + } +} + +pub struct ModifierSetIter<'a> { + inner: std::str::Split<'a, char>, +} + +impl<'a> Iterator for ModifierSetIter<'a> { + type Item = Modifier<'a>; + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + fn next(&mut self) -> Option { + self.inner.next().map(Modifier) + } +} + impl<'a, S: Deref> IntoIterator for &'a ModifierSet { - type Item = &'a str; - type IntoIter = std::str::Split<'a, char>; + type Item = Modifier<'a>; + type IntoIter = ModifierSetIter<'a>; /// Iterate over the list of modifiers in an arbitrary order. fn into_iter(self) -> Self::IntoIter { @@ -134,13 +204,13 @@ impl<'a, S: Deref> IntoIterator for &'a ModifierSet { // Empty the iterator let _ = iter.next(); } - iter + ModifierSetIter { inner: iter } } } impl<'a> IntoIterator for ModifierSet<&'a str> { - type Item = &'a str; - type IntoIter = std::str::Split<'a, char>; + type Item = Modifier<'a>; + type IntoIter = ModifierSetIter<'a>; /// Iterate over the list of modifiers in an arbitrary order. fn into_iter(self) -> Self::IntoIter { @@ -149,7 +219,7 @@ impl<'a> IntoIterator for ModifierSet<&'a str> { // Empty the iterator let _ = iter.next(); } - iter + ModifierSetIter { inner: iter } } } @@ -178,6 +248,12 @@ mod tests { .is_subset(ModifierSet::from_raw_dotted("b.a"))); assert!(ModifierSet::from_raw_dotted("a.b") .is_subset(ModifierSet::from_raw_dotted("b.c.a"))); + assert!(ModifierSet::from_raw_dotted("a") + .is_subset(ModifierSet::from_raw_dotted("a?.b"))); + assert!(ModifierSet::from_raw_dotted("a?") + .is_subset(ModifierSet::from_raw_dotted("a.b"))); + assert!(ModifierSet::from_raw_dotted("a?") + .is_subset(ModifierSet::from_raw_dotted("a?.b"))); } #[test] @@ -186,8 +262,8 @@ mod tests { assert_eq!( ModifierSet::from_raw_dotted("a.b").best_match_in( [ - (ModifierSet::from_raw_dotted("a.c"), 1), - (ModifierSet::from_raw_dotted("a.b"), 2), + (ModifierSet::from_raw_dotted("a?.c?"), 1), + (ModifierSet::from_raw_dotted("a?.b?"), 2), ] .into_iter() ), @@ -197,8 +273,8 @@ mod tests { assert_eq!( ModifierSet::from_raw_dotted("a").best_match_in( [ - (ModifierSet::from_raw_dotted("a"), 1), - (ModifierSet::from_raw_dotted("a.b"), 2), + (ModifierSet::from_raw_dotted("a?"), 1), + (ModifierSet::from_raw_dotted("a?.b?"), 2), ] .into_iter() ), @@ -208,8 +284,8 @@ mod tests { assert_eq!( ModifierSet::from_raw_dotted("a.b").best_match_in( [ - (ModifierSet::from_raw_dotted("a"), 1), - (ModifierSet::from_raw_dotted("a.b"), 2), + (ModifierSet::from_raw_dotted("a?"), 1), + (ModifierSet::from_raw_dotted("a?.b?"), 2), ] .into_iter() ), @@ -219,8 +295,8 @@ mod tests { assert_eq!( ModifierSet::default().best_match_in( [ - (ModifierSet::from_raw_dotted("a"), 1), - (ModifierSet::from_raw_dotted("b"), 2) + (ModifierSet::from_raw_dotted("a?"), 1), + (ModifierSet::from_raw_dotted("b?"), 2) ] .into_iter() ),