fea_rs/token_tree/
typed.rs

1//! typing for ast nodes. based on rust-analyzer.
2//!
3//! Here, we use macros to generate distinct types for specific AST nodes.
4//! We cast from generic nodes or tokens to these distinct types based on their
5//! location in the tree, and the underlying [`Kind`] of the node.
6//!
7//! This lets us implement useful methods on specific AST nodes, which are
8//! internally working on untyped `NodeOrToken`s.
9
10use std::convert::TryFrom;
11use std::ops::Range;
12
13use smol_str::SmolStr;
14use write_fonts::types::Fixed;
15
16use crate::{Kind, Node, NodeOrToken};
17
18use super::{ChildIter, Token};
19
20/// A trait for types that exist in the AST.
21///
22/// Implementations of this type are generally generated via macro.
23pub trait AstNode {
24    /// Attempt to cast from some node or token to this type.
25    fn cast(node: &NodeOrToken) -> Option<Self>
26    where
27        Self: Sized;
28
29    /// The range in the source of this item.
30    ///
31    /// This is used for better diagnostic reporting.
32    fn range(&self) -> Range<usize>;
33
34    /// If this is a node, iterate over its children
35    fn iter(&self) -> ChildIter<'_> {
36        Default::default()
37    }
38}
39
40/// Create a new AstNode wrapping a token.
41macro_rules! ast_token {
42    ($typ:ident, $kind:expr) => {
43        #[derive(Clone, Debug)]
44        #[allow(missing_docs)]
45        pub struct $typ {
46            inner: Token,
47        }
48
49        impl $typ {
50            /// The raw text for this token
51            #[allow(unused)]
52            pub fn text(&self) -> &SmolStr {
53                &self.inner.text
54            }
55
56            /// The underlying `Token`
57            #[allow(unused)]
58            pub fn token(&self) -> &Token {
59                &self.inner
60            }
61
62            // just used for the ast_enum macro
63            #[allow(dead_code)]
64            pub(crate) fn node_(&self) -> Option<&Node> {
65                None
66            }
67        }
68
69        impl AstNode for $typ {
70            fn cast(node: &NodeOrToken) -> Option<Self> {
71                if let NodeOrToken::Token(t) = node {
72                    if t.kind == $kind {
73                        return Some(Self { inner: t.clone() });
74                    }
75                }
76                None
77            }
78
79            fn range(&self) -> std::ops::Range<usize> {
80                self.inner.range()
81            }
82        }
83    };
84}
85
86/// Create a new AstNode, wrapping a Node
87macro_rules! ast_node {
88    ($typ:ident, $kind:expr) => {
89        #[derive(Clone, Debug)]
90        #[allow(missing_docs)]
91        pub struct $typ {
92            inner: Node,
93        }
94
95        impl $typ {
96            pub(crate) fn try_from_node(node: &Node) -> Option<Self> {
97                if node.kind == $kind {
98                    return Some(Self {
99                        inner: node.clone(),
100                    });
101                }
102                None
103            }
104
105            #[allow(dead_code)]
106            pub(crate) fn find_token(&self, kind: Kind) -> Option<&Token> {
107                self.iter()
108                    .find(|t| t.kind() == kind)
109                    .and_then(NodeOrToken::as_token)
110            }
111
112            /// Return a reference to the underlying `Node`.
113            #[allow(dead_code)]
114            pub fn node(&self) -> &Node {
115                &self.inner
116            }
117
118            // just used for the ast_enum macro
119            #[allow(dead_code)]
120            pub(crate) fn node_(&self) -> Option<&Node> {
121                Some(&self.inner)
122            }
123        }
124
125        impl AstNode for $typ {
126            fn cast(node: &NodeOrToken) -> Option<Self> {
127                if let NodeOrToken::Node(inner) = node {
128                    return Self::try_from_node(inner);
129                }
130                None
131            }
132
133            fn range(&self) -> std::ops::Range<usize> {
134                self.inner.range()
135            }
136
137            fn iter(&self) -> ChildIter<'_> {
138                self.inner.iter_children()
139            }
140        }
141    };
142}
143
144/// Create an enum from some set of AstNodes.
145///
146/// This is useful when you have places in the tree where you aceept multiple
147/// different concrete types.
148macro_rules! ast_enum {
149    ($typ:ident{ $($name:ident($member:ident),)*}) => {
150        #[derive(Clone, Debug)]
151        #[allow(missing_docs)]
152        pub enum $typ {
153            $($name($member)),*
154        }
155
156        impl AstNode for $typ {
157            fn cast(node: &NodeOrToken) -> Option<Self> {
158                $(
159                    if let Some(thing) = $member::cast(node) {
160                        return Some(Self::$name(thing));
161                    }
162                )*
163                    None
164
165            }
166
167            fn range(&self) -> std::ops::Range<usize> {
168                match self {
169                    $(
170                        Self::$name(inner) => inner.range(),
171                    )*
172                }
173            }
174        }
175
176        impl $typ {
177            #[allow(unused)]
178            pub(crate) fn node(&self) -> Option<&Node> {
179                match self {
180                    $(
181                        Self::$name(inner) => inner.node_(),
182                    )*
183                }
184            }
185
186            // just used for the ast_enum macro
187            #[allow(dead_code)]
188            pub(crate) fn node_(&self) -> Option<&Node> {
189                self.node()
190            }
191
192        }
193    };
194
195}
196
197ast_token!(Cid, Kind::Cid);
198ast_token!(GlyphName, Kind::GlyphName);
199ast_token!(Tag, Kind::Tag);
200ast_token!(GlyphClassName, Kind::NamedGlyphClass);
201ast_token!(Number, Kind::Number);
202ast_token!(Float, Kind::Float);
203ast_token!(Octal, Kind::Octal);
204ast_token!(Hex, Kind::Hex);
205ast_token!(Null, Kind::NullKw);
206ast_node!(Root, Kind::SourceFile);
207ast_node!(GlyphRange, Kind::GlyphRange);
208ast_node!(GlyphClassDef, Kind::GlyphClassDefNode);
209ast_node!(MarkClassDef, Kind::MarkClassNode);
210ast_node!(Anchor, Kind::AnchorNode);
211ast_node!(AnchorDef, Kind::AnchorDefNode);
212ast_node!(ValueRecordDef, Kind::ValueRecordDefNode);
213ast_node!(GlyphClassLiteral, Kind::GlyphClass);
214ast_node!(LanguageSystem, Kind::LanguageSystemNode);
215ast_node!(Include, Kind::IncludeNode);
216ast_node!(Feature, Kind::FeatureNode);
217ast_node!(Script, Kind::ScriptNode);
218ast_node!(Language, Kind::LanguageNode);
219ast_node!(LookupFlag, Kind::LookupFlagNode);
220ast_node!(LookupRef, Kind::LookupRefNode);
221ast_node!(LookupBlock, Kind::LookupBlockNode);
222ast_node!(ValueRecord, Kind::ValueRecordNode);
223ast_node!(Device, Kind::DeviceNode);
224ast_node!(SizeMenuName, Kind::SizeMenuNameNode);
225ast_node!(Parameters, Kind::ParametersNode);
226ast_node!(FeatureNames, Kind::FeatureNamesKw);
227ast_node!(CvParameters, Kind::CvParametersKw);
228ast_node!(CvParametersName, Kind::CvParamsNameNode);
229ast_node!(CvParametersChar, Kind::CharacterKw);
230
231ast_node!(HeadTable, Kind::HeadTableNode);
232ast_node!(HheaTable, Kind::HheaTableNode);
233ast_node!(NameTable, Kind::NameTableNode);
234ast_node!(BaseTable, Kind::BaseTableNode);
235ast_node!(GdefTable, Kind::GdefTableNode);
236ast_node!(Os2Table, Kind::Os2TableNode);
237ast_node!(VheaTable, Kind::VheaTableNode);
238ast_node!(VmtxTable, Kind::VmtxTableNode);
239ast_node!(StatTable, Kind::StatTableNode);
240ast_node!(UnimplentedTable, Kind::TableNode);
241
242ast_enum!(Table {
243    Head(HeadTable),
244    Hhea(HheaTable),
245    Name(NameTable),
246    Base(BaseTable),
247    Gdef(GdefTable),
248    Os2(Os2Table),
249    Vhea(VheaTable),
250    Vmtx(VmtxTable),
251    Stat(StatTable),
252    Other(UnimplentedTable),
253});
254
255ast_node!(BaseTagList, Kind::BaseTagListNode);
256ast_node!(BaseScriptList, Kind::BaseScriptListNode);
257ast_node!(ScriptRecord, Kind::ScriptRecordNode);
258
259ast_node!(MetricRecord, Kind::MetricValueNode);
260ast_node!(NumberRecord, Kind::NumberValueNode);
261ast_node!(VendorRecord, Kind::Os2VendorNode);
262ast_node!(NameRecord, Kind::NameRecordNode);
263ast_node!(NameSpec, Kind::NameSpecNode);
264ast_node!(VmtxEntry, Kind::VmtxEntryNode);
265
266ast_enum!(DecOctHex {
267    Decimal(Number),
268    Octal(Octal),
269    Hex(Hex),
270});
271
272ast_enum!(FloatLike {
273    Float(Float),
274    Number(Number),
275});
276
277ast_node!(ConditionSet, Kind::ConditionSetNode);
278ast_node!(Condition, Kind::ConditionNode);
279ast_node!(FeatureVariation, Kind::VariationNode);
280ast_node!(VariableMetric, Kind::VariableMetricNode);
281ast_node!(LocationValue, Kind::LocationValueNode);
282ast_node!(LocationSpec, Kind::LocationSpecNode);
283ast_node!(LocationSpecItem, Kind::LocationSpecItemNode);
284ast_enum!(Metric {
285    Scalar(Number),
286    Variable(VariableMetric),
287    GlyphsAppNumber(GlyphsAppNumber),
288});
289ast_node!(AxisLocation, Kind::AxisLocationNode);
290ast_token!(NumberSuffix, Kind::NumberSuffix);
291
292ast_node!(GdefClassDef, Kind::GdefClassDefNode);
293ast_node!(GdefClassDefEntry, Kind::GdefClassDefEntryNode);
294ast_node!(GdefAttach, Kind::GdefAttachNode);
295ast_node!(GdefLigatureCaret, Kind::GdefLigatureCaretNode);
296
297ast_enum!(GdefTableItem {
298    ClassDef(GdefClassDef),
299    Attach(GdefAttach),
300    LigatureCaret(GdefLigatureCaret),
301});
302
303ast_node!(HeadFontRevision, Kind::HeadFontRevisionNode);
304
305ast_node!(Os2NumberList, Kind::Os2NumberListNode);
306ast_node!(Os2FamilyClass, Kind::Os2FamilyClassNode);
307ast_enum!(Os2TableItem {
308    Number(NumberRecord),
309    NumberList(Os2NumberList),
310    Metric(MetricRecord),
311    Vendor(VendorRecord),
312    FamilyClass(Os2FamilyClass),
313});
314
315ast_node!(StatElidedFallbackName, Kind::StatElidedFallbackNameNode);
316ast_node!(StatDesignAxis, Kind::StatDesignAxisNode);
317ast_node!(StatAxisValue, Kind::StatAxisValueNode);
318
319ast_enum!(StatTableItem {
320    ElidedFallbackName(StatElidedFallbackName),
321    DesignAxis(StatDesignAxis),
322    AxisValue(StatAxisValue),
323});
324
325ast_node!(StatAxisFlag, Kind::StatAxisValueFlagNode);
326ast_node!(StatAxisLocation, Kind::StatAxisValueLocationNode);
327
328ast_enum!(StatAxisValueItem {
329    NameRecord(NameSpec),
330    Flag(StatAxisFlag),
331    Location(StatAxisLocation),
332});
333
334ast_node!(FeatureRef, Kind::AaltFeatureNode);
335
336ast_node!(Gsub1, Kind::GsubType1);
337ast_node!(Gsub2, Kind::GsubType2);
338ast_node!(Gsub3, Kind::GsubType3);
339ast_node!(Gsub4, Kind::GsubType4);
340ast_node!(Gsub5, Kind::GsubType5);
341ast_node!(Gsub6, Kind::GsubType6);
342ast_node!(Gsub8, Kind::GsubType8);
343ast_node!(GsubIgnore, Kind::GsubIgnore);
344
345ast_node!(Gpos1, Kind::GposType1);
346ast_node!(Gpos2, Kind::GposType2);
347ast_node!(Gpos3, Kind::GposType3);
348ast_node!(Gpos4, Kind::GposType4);
349ast_node!(Gpos5, Kind::GposType5);
350ast_node!(Gpos6, Kind::GposType6);
351ast_node!(Gpos8, Kind::GposType8);
352ast_node!(GposIgnore, Kind::GposIgnore);
353ast_node!(AnchorMark, Kind::AnchorMarkNode);
354ast_node!(LigatureComponent, Kind::LigatureComponentNode);
355
356ast_node!(BacktrackSequence, Kind::BacktrackSequence);
357ast_node!(LookaheadSequence, Kind::LookaheadSequence);
358ast_node!(InputSequence, Kind::ContextSequence);
359ast_node!(InputItem, Kind::ContextGlyphNode);
360ast_node!(InlineSubRule, Kind::InlineSubNode);
361ast_node!(IgnoreRule, Kind::IgnoreRuleStatementNode);
362
363ast_enum!(GposStatement {
364    Type1(Gpos1),
365    Type2(Gpos2),
366    Type3(Gpos3),
367    Type4(Gpos4),
368    Type5(Gpos5),
369    Type6(Gpos6),
370    Type8(Gpos8),
371    Ignore(GposIgnore),
372});
373
374ast_enum!(GsubStatement {
375    Type1(Gsub1),
376    Type2(Gsub2),
377    Type3(Gsub3),
378    Type4(Gsub4),
379    Type5(Gsub5),
380    Type6(Gsub6),
381    Type8(Gsub8),
382    Ignore(GsubIgnore),
383});
384
385ast_enum!(GlyphOrClass {
386    Glyph(GlyphName),
387    Cid(Cid),
388    NamedClass(GlyphClassName),
389    Class(GlyphClassLiteral),
390    Null(Null),
391});
392
393ast_enum!(Glyph {
394    Named(GlyphName),
395    Cid(Cid),
396    Null(Null),
397});
398
399ast_enum!(GlyphClass {
400    Named(GlyphClassName),
401    Literal(GlyphClassLiteral),
402});
403
404// glyphs app number values: https://glyphsapp.com/learn/tokens#g-number-values
405ast_node!(GlyphsAppNumber, Kind::GlyphsNumberValueNode);
406ast_node!(GlyphsAppNumberExpr, Kind::GlyphsNumberValueExprNode);
407ast_token!(GlyphsAppNumberName, Kind::GlyphsNumberIdent);
408ast_token!(GlyphsAppOperatorPlus, Kind::Plus);
409ast_token!(GlyphsAppOperatorMinus, Kind::Hyphen);
410ast_token!(GlyphsAppOperatorMul, Kind::Asterisk);
411ast_token!(GlyphsAppOperatorDiv, Kind::Slash);
412
413ast_enum!(GlyphsAppOperator {
414    Plus(GlyphsAppOperatorPlus),
415    Minus(GlyphsAppOperatorMinus),
416    Mul(GlyphsAppOperatorMul),
417    Div(GlyphsAppOperatorDiv),
418});
419
420ast_enum!(GlyphsAppExprItem {
421    Ident(GlyphsAppNumberName),
422    Lit(FloatLike),
423    Operator(GlyphsAppOperator),
424});
425
426ast_enum!(GlyphsAppNumberValue {
427    Expr(GlyphsAppNumberExpr),
428    Ident(GlyphsAppNumberName),
429});
430
431/// A trait for contextual and chain contextual rule nodes.
432///
433/// These types share a common implementation, and this lets us reuse code
434/// when processing those types.
435pub trait ContextualRuleNode: AstNode {
436    /// The backtrack sequence
437    fn backtrack(&self) -> BacktrackSequence {
438        self.iter().find_map(BacktrackSequence::cast).unwrap()
439    }
440
441    /// The lookahead sequence
442    fn lookahead(&self) -> LookaheadSequence {
443        self.iter().find_map(LookaheadSequence::cast).unwrap()
444    }
445
446    /// The input sequence
447    fn input(&self) -> InputSequence {
448        self.iter().find_map(InputSequence::cast).unwrap()
449    }
450}
451
452impl ContextualRuleNode for Gpos8 {}
453impl ContextualRuleNode for Gsub6 {}
454impl ContextualRuleNode for Gsub8 {}
455impl ContextualRuleNode for IgnoreRule {}
456
457impl Root {
458    /// Iterate over all top-level statements
459    pub fn statements(&self) -> impl Iterator<Item = &NodeOrToken> {
460        self.iter().filter(|t| !t.kind().is_trivia())
461    }
462}
463
464impl LanguageSystem {
465    /// The script tag
466    pub fn script(&self) -> Tag {
467        self.inner.iter_children().find_map(Tag::cast).unwrap()
468    }
469
470    /// The language tag
471    pub fn language(&self) -> Tag {
472        self.inner
473            .iter_children()
474            .skip_while(|t| t.kind() != Kind::Tag)
475            .skip(1)
476            .find_map(Tag::cast)
477            .unwrap()
478    }
479}
480
481impl Include {
482    pub(crate) fn path(&self) -> &Token {
483        self.find_token(Kind::Path).unwrap()
484    }
485}
486
487impl Tag {
488    pub(crate) fn parse(&self) -> Result<write_fonts::types::Tag, write_fonts::types::InvalidTag> {
489        self.inner.text.parse()
490    }
491
492    /// Convert this AST tag into a raw `Tag`
493    pub fn to_raw(&self) -> write_fonts::types::Tag {
494        self.parse().expect("tag is exactly 4 bytes")
495    }
496}
497
498impl GlyphClassDef {
499    pub(crate) fn class_name(&self) -> GlyphClassName {
500        self.inner
501            .iter_children()
502            .find_map(GlyphClassName::cast)
503            .unwrap()
504    }
505
506    pub(crate) fn class_alias(&self) -> Option<GlyphClassName> {
507        //TODO: ensure this returns non in presence of named glyph class inside class block
508        self.iter()
509            .skip_while(|t| t.kind() != Kind::Eq)
510            .find_map(GlyphClassName::cast)
511    }
512
513    pub(crate) fn class_def(&self) -> Option<GlyphClassLiteral> {
514        self.inner.iter_children().find_map(GlyphClassLiteral::cast)
515    }
516}
517
518impl GlyphClassLiteral {
519    pub(crate) fn items(&self) -> impl Iterator<Item = &NodeOrToken> {
520        self.iter()
521            .skip_while(|t| t.kind() != Kind::LSquare)
522            .skip(1)
523            .take_while(|t| t.kind() != Kind::RSquare)
524            .filter(|t| !t.kind().is_trivia())
525    }
526}
527
528impl Cid {
529    pub(crate) fn parse(&self) -> u16 {
530        self.inner.text.parse().expect("cid is already validated")
531    }
532}
533
534impl GlyphRange {
535    pub(crate) fn start(&self) -> &Token {
536        self.iter()
537            .find(|i| i.kind() == Kind::Cid || i.kind() == Kind::GlyphName)
538            .and_then(NodeOrToken::as_token)
539            .unwrap()
540    }
541
542    pub(crate) fn end(&self) -> &Token {
543        self.iter()
544            .skip_while(|t| t.kind() != Kind::Hyphen)
545            .find(|i| i.kind() == Kind::Cid || i.kind() == Kind::GlyphName)
546            .and_then(NodeOrToken::as_token)
547            .unwrap()
548    }
549}
550
551impl GlyphOrClass {
552    pub(crate) fn is_class(&self) -> bool {
553        matches!(self, GlyphOrClass::Class(_) | GlyphOrClass::NamedClass(_))
554    }
555}
556
557impl MarkClassDef {
558    pub(crate) fn keyword(&self) -> &Token {
559        self.find_token(Kind::MarkClassKw).unwrap()
560    }
561
562    pub(crate) fn glyph_class(&self) -> GlyphOrClass {
563        self.iter().find_map(GlyphOrClass::cast).expect("validated")
564    }
565
566    pub(crate) fn anchor(&self) -> Anchor {
567        self.iter().find_map(Anchor::cast).unwrap()
568    }
569
570    pub(crate) fn mark_class_name(&self) -> GlyphClassName {
571        self.iter()
572            .skip_while(|t| t.kind() != Kind::AnchorNode)
573            .find_map(GlyphClassName::cast)
574            .unwrap()
575    }
576}
577
578impl ValueRecordDef {
579    pub(crate) fn value_record(&self) -> ValueRecord {
580        self.iter().find_map(ValueRecord::cast).unwrap()
581    }
582
583    pub(crate) fn name(&self) -> &Token {
584        self.find_token(Kind::Ident).expect("validated")
585    }
586}
587
588impl AnchorDef {
589    pub(crate) fn anchor(&self) -> Anchor {
590        self.iter().find_map(Anchor::cast).unwrap()
591    }
592
593    pub(crate) fn name(&self) -> &Token {
594        self.find_token(Kind::Ident).expect("pre-validated")
595    }
596}
597
598impl Anchor {
599    pub(crate) fn coords(&self) -> Option<(Metric, Metric)> {
600        let tokens = self.iter();
601        let mut first = None;
602
603        for token in tokens {
604            if let Some(metric) = Metric::cast(token) {
605                if let Some(prev) = first.take() {
606                    return Some((prev, metric));
607                } else {
608                    first = Some(metric);
609                }
610            }
611        }
612        None
613    }
614
615    pub(crate) fn contourpoint(&self) -> Option<Number> {
616        self.iter()
617            .skip_while(|x| x.kind() != Kind::ContourpointKw)
618            .find_map(Number::cast)
619    }
620
621    pub(crate) fn devices(&self) -> Option<(Device, Device)> {
622        let mut iter = self.iter().filter_map(Device::cast);
623        iter.next()
624            .map(|first| (first, iter.next().expect("one device implies another")))
625    }
626
627    pub(crate) fn null(&self) -> Option<&Token> {
628        self.find_token(Kind::NullKw)
629    }
630
631    pub(crate) fn name(&self) -> Option<&Token> {
632        self.find_token(Kind::Ident)
633    }
634}
635
636impl Number {
637    pub(crate) fn parse_signed(&self) -> i16 {
638        self.text().parse().expect("already validated")
639    }
640
641    pub(crate) fn parse_unsigned(&self) -> Option<u16> {
642        self.text().parse().ok()
643    }
644}
645
646impl Float {
647    pub(crate) fn parse(&self) -> f64 {
648        self.text().parse().unwrap()
649    }
650
651    pub(crate) fn parse_fixed(&self) -> Fixed {
652        Fixed::from_f64(self.parse())
653    }
654}
655
656impl FloatLike {
657    pub(crate) fn parse(&self) -> f64 {
658        match self {
659            FloatLike::Number(n) => n.parse_signed() as _,
660            FloatLike::Float(n) => n.parse(),
661        }
662    }
663
664    pub(crate) fn parse_fixed(&self) -> Fixed {
665        Fixed::from_f64(self.parse() as _)
666    }
667}
668
669impl Feature {
670    /// The name (tag) of this feature (kern, merk, etc)
671    pub fn tag(&self) -> Tag {
672        self.iter().find_map(Tag::cast).unwrap()
673    }
674
675    /// Returns `true` if this feature block contains an '# Automatic Code' comment
676    pub fn has_insert_marker(&self) -> bool {
677        self.statements().any(|s| s.kind() == Kind::Comment)
678    }
679
680    pub(crate) fn statements(&self) -> impl Iterator<Item = &NodeOrToken> {
681        fn filter_trivia_except_for_magic_insertion_comments(item: &&NodeOrToken) -> bool {
682            match item.kind() {
683                Kind::Comment => item
684                    .token_text()
685                    .unwrap_or_default()
686                    .trim_start()
687                    //https://github.com/googlefonts/ufo2ft/blob/5a606b7884bb6da5/Lib/ufo2ft/featureWriters/baseFeatureWriter.py#L18
688                    .starts_with("# Automatic Code"),
689                other => !other.is_trivia(),
690            }
691        }
692
693        self.iter()
694            .skip_while(|t| t.kind() != Kind::LBrace)
695            .skip(1)
696            .filter(filter_trivia_except_for_magic_insertion_comments)
697            .take_while(|t| t.kind() != Kind::RBrace)
698    }
699}
700
701impl LookupBlock {
702    #[allow(unused)]
703    //TODO: do we want to support this syntax?
704    pub(crate) fn use_extension(&self) -> Option<&Token> {
705        self.iter()
706            .take_while(|t| t.kind() != Kind::LBrace)
707            .find(|t| t.kind() == Kind::UseExtensionKw)
708            .and_then(NodeOrToken::as_token)
709    }
710
711    pub(crate) fn keyword(&self) -> &Token {
712        self.find_token(Kind::LookupKw).unwrap()
713    }
714
715    pub(crate) fn label(&self) -> &Token {
716        self.find_token(Kind::Label).unwrap()
717    }
718
719    pub(crate) fn statements(&self) -> impl Iterator<Item = &NodeOrToken> {
720        self.iter()
721            .skip_while(|t| t.kind() != Kind::LBrace)
722            .skip(1)
723            .filter(|t| !t.kind().is_trivia())
724            .take_while(|t| t.kind() != Kind::RBrace)
725    }
726}
727
728impl ConditionSet {
729    pub(crate) fn keyword(&self) -> &Token {
730        self.find_token(Kind::ConditionSetKw).unwrap()
731    }
732
733    pub(crate) fn label(&self) -> &Token {
734        self.find_token(Kind::Label).unwrap()
735    }
736
737    pub(crate) fn conditions(&self) -> impl Iterator<Item = Condition> + '_ {
738        self.iter().filter_map(Condition::cast)
739    }
740}
741
742impl Condition {
743    pub(crate) fn tag(&self) -> Tag {
744        self.iter().find_map(Tag::cast).unwrap()
745    }
746
747    pub(crate) fn min_value(&self) -> Number {
748        self.iter().find_map(Number::cast).unwrap()
749    }
750
751    pub(crate) fn max_value(&self) -> Number {
752        self.iter().filter_map(Number::cast).nth(1).unwrap()
753    }
754}
755
756impl FeatureVariation {
757    pub(crate) fn tag(&self) -> Tag {
758        self.iter().find_map(Tag::cast).unwrap()
759    }
760
761    /// optional; if this is 'none' then 'null' must be present
762    pub(crate) fn condition_set(&self) -> Option<&Token> {
763        self.find_token(Kind::Label)
764    }
765
766    pub(crate) fn null(&self) -> Option<&Token> {
767        self.find_token(Kind::NullKw)
768    }
769
770    pub(crate) fn statements(&self) -> impl Iterator<Item = &NodeOrToken> {
771        self.iter()
772            .skip_while(|t| t.kind() != Kind::LBrace)
773            .skip(1)
774            .filter(|t| !t.kind().is_trivia())
775            .take_while(|t| t.kind() != Kind::RBrace)
776    }
777}
778
779impl Script {
780    pub(crate) fn tag(&self) -> Tag {
781        self.iter().find_map(Tag::cast).unwrap()
782    }
783}
784
785impl Language {
786    pub(crate) fn tag(&self) -> Tag {
787        self.iter().find_map(Tag::cast).unwrap()
788    }
789
790    //FIXME: I believe this is never meaningful, as it is the default behaviour?
791    #[allow(unused)]
792    pub(crate) fn include_dflt(&self) -> Option<&Token> {
793        self.find_token(Kind::IncludeDfltKw)
794    }
795
796    pub(crate) fn exclude_dflt(&self) -> Option<&Token> {
797        self.find_token(Kind::ExcludeDfltKw)
798    }
799
800    pub(crate) fn required(&self) -> Option<&Token> {
801        self.find_token(Kind::RequiredKw)
802    }
803}
804
805impl LookupFlag {
806    pub(crate) fn number(&self) -> Option<Number> {
807        self.iter().find_map(Number::cast)
808    }
809
810    pub(crate) fn values(&self) -> impl Iterator<Item = &NodeOrToken> + '_ {
811        self.iter()
812            .skip(1)
813            .take_while(|t| t.kind() != Kind::Number && t.kind() != Kind::Semi)
814            .filter(|t| !t.kind().is_trivia())
815    }
816}
817
818impl LookupRef {
819    pub(crate) fn label(&self) -> &Token {
820        self.find_token(Kind::Ident).unwrap()
821    }
822}
823
824impl Gsub1 {
825    pub(crate) fn target(&self) -> GlyphOrClass {
826        self.iter().find_map(GlyphOrClass::cast).unwrap()
827    }
828
829    pub(crate) fn replacement(&self) -> Option<GlyphOrClass> {
830        self.iter()
831            .skip_while(|t| t.kind() != Kind::ByKw)
832            .find_map(GlyphOrClass::cast)
833    }
834}
835
836impl Gsub2 {
837    pub(crate) fn target(&self) -> Glyph {
838        self.iter().find_map(Glyph::cast).unwrap()
839    }
840
841    pub(crate) fn replacement(&self) -> impl Iterator<Item = Glyph> + '_ {
842        self.iter()
843            .skip_while(|t| t.kind() != Kind::ByKw)
844            .skip(1)
845            .filter_map(Glyph::cast)
846    }
847}
848
849impl Gsub3 {
850    pub(crate) fn target(&self) -> Glyph {
851        self.iter().find_map(Glyph::cast).unwrap()
852    }
853
854    pub(crate) fn alternates(&self) -> GlyphClass {
855        self.iter()
856            .skip_while(|t| t.kind() != Kind::FromKw)
857            .find_map(GlyphClass::cast)
858            .unwrap()
859    }
860}
861
862impl Gsub4 {
863    pub(crate) fn target(&self) -> impl Iterator<Item = GlyphOrClass> + '_ {
864        self.iter()
865            .take_while(|t| t.kind() != Kind::ByKw)
866            .filter_map(GlyphOrClass::cast)
867    }
868
869    pub(crate) fn replacement(&self) -> Glyph {
870        self.iter()
871            .skip_while(|t| t.kind() != Kind::ByKw)
872            .find_map(Glyph::cast)
873            .unwrap()
874    }
875}
876
877impl Gsub6 {
878    pub(crate) fn inline_rule(&self) -> Option<InlineSubRule> {
879        self.iter().find_map(InlineSubRule::cast)
880    }
881}
882
883impl Gsub8 {
884    pub(crate) fn inline_rule(&self) -> Option<InlineSubRule> {
885        self.iter().find_map(InlineSubRule::cast)
886    }
887}
888
889impl GsubIgnore {
890    pub(crate) fn rules(&self) -> impl Iterator<Item = IgnoreRule> + '_ {
891        self.iter().filter_map(IgnoreRule::cast)
892    }
893}
894
895impl BacktrackSequence {
896    pub(crate) fn items(&self) -> impl Iterator<Item = GlyphOrClass> + '_ {
897        self.iter().filter_map(GlyphOrClass::cast)
898    }
899}
900
901impl LookaheadSequence {
902    pub(crate) fn items(&self) -> impl Iterator<Item = GlyphOrClass> + '_ {
903        self.iter().filter_map(GlyphOrClass::cast)
904    }
905}
906
907impl InputSequence {
908    pub(crate) fn items(&self) -> impl Iterator<Item = InputItem> + '_ {
909        self.iter().filter_map(InputItem::cast)
910    }
911}
912
913impl InputItem {
914    pub(crate) fn target(&self) -> GlyphOrClass {
915        self.iter().find_map(GlyphOrClass::cast).unwrap()
916    }
917
918    pub(crate) fn lookups(&self) -> impl Iterator<Item = LookupRef> + '_ {
919        self.iter().filter_map(LookupRef::cast)
920    }
921
922    /// for pos rules only
923    pub(crate) fn valuerecord(&self) -> Option<ValueRecord> {
924        self.iter().find_map(ValueRecord::cast)
925    }
926}
927
928impl InlineSubRule {
929    pub(crate) fn replacement_class(&self) -> Option<GlyphClass> {
930        self.iter().find_map(GlyphClass::cast)
931    }
932
933    // if empty, there is a class
934    pub(crate) fn replacement_glyphs(&self) -> impl Iterator<Item = Glyph> + '_ {
935        self.iter().filter_map(Glyph::cast)
936    }
937
938    // this overlaps with the other two? i don't know what the best API is.. :/
939    pub(crate) fn replacements(&self) -> impl Iterator<Item = GlyphOrClass> + '_ {
940        self.iter().filter_map(GlyphOrClass::cast)
941    }
942
943    pub(crate) fn null(&self) -> Option<Null> {
944        self.iter().find_map(Null::cast)
945    }
946}
947
948impl Gpos1 {
949    pub(crate) fn target(&self) -> GlyphOrClass {
950        self.iter().find_map(GlyphOrClass::cast).unwrap()
951    }
952
953    pub(crate) fn value(&self) -> ValueRecord {
954        self.iter().find_map(ValueRecord::cast).unwrap()
955    }
956}
957
958impl Gpos2 {
959    pub(crate) fn enum_(&self) -> Option<&Token> {
960        self.iter()
961            .take_while(|t| t.kind() != Kind::PosKw)
962            .find(|t| t.kind() == Kind::EnumKw)
963            .and_then(NodeOrToken::as_token)
964    }
965
966    pub(crate) fn first_item(&self) -> GlyphOrClass {
967        self.iter().find_map(GlyphOrClass::cast).unwrap()
968    }
969
970    pub(crate) fn second_item(&self) -> GlyphOrClass {
971        self.iter().filter_map(GlyphOrClass::cast).nth(1).unwrap()
972    }
973
974    pub(crate) fn first_value(&self) -> ValueRecord {
975        self.iter().find_map(ValueRecord::cast).unwrap()
976    }
977
978    pub(crate) fn second_value(&self) -> Option<ValueRecord> {
979        self.iter().filter_map(ValueRecord::cast).nth(1)
980    }
981}
982
983impl Gpos3 {
984    pub(crate) fn target(&self) -> GlyphOrClass {
985        self.iter()
986            .filter(|t| !t.kind().is_trivia())
987            .nth(2)
988            .and_then(GlyphOrClass::cast)
989            .unwrap()
990    }
991
992    pub(crate) fn entry(&self) -> Anchor {
993        self.iter().skip(3).find_map(Anchor::cast).unwrap()
994    }
995
996    pub(crate) fn exit(&self) -> Anchor {
997        self.iter()
998            .skip_while(|t| t.kind() != Kind::AnchorNode)
999            .skip(1)
1000            .find_map(Anchor::cast)
1001            .unwrap()
1002    }
1003}
1004
1005impl Gpos4 {
1006    pub(crate) fn base(&self) -> GlyphOrClass {
1007        self.iter()
1008            .filter(|t| !t.kind().is_trivia())
1009            .nth(2)
1010            .and_then(GlyphOrClass::cast)
1011            .unwrap()
1012    }
1013
1014    pub(crate) fn attachments(&self) -> impl Iterator<Item = AnchorMark> + '_ {
1015        self.iter().skip(3).filter_map(AnchorMark::cast)
1016    }
1017}
1018
1019impl Gpos5 {
1020    pub(crate) fn base(&self) -> GlyphOrClass {
1021        self.iter()
1022            .filter(|t| !t.kind().is_trivia())
1023            .nth(2)
1024            .and_then(GlyphOrClass::cast)
1025            .unwrap()
1026    }
1027
1028    pub(crate) fn ligature_components(&self) -> impl Iterator<Item = LigatureComponent> + '_ {
1029        self.iter().skip(3).filter_map(LigatureComponent::cast)
1030    }
1031}
1032
1033impl Gpos6 {
1034    pub(crate) fn base(&self) -> GlyphOrClass {
1035        self.iter()
1036            .filter(|t| !t.kind().is_trivia())
1037            .nth(2)
1038            .and_then(GlyphOrClass::cast)
1039            .unwrap()
1040    }
1041
1042    pub(crate) fn attachments(&self) -> impl Iterator<Item = AnchorMark> + '_ {
1043        self.iter().skip(3).filter_map(AnchorMark::cast)
1044    }
1045}
1046
1047impl Gpos8 {
1048    pub(crate) fn trailing_value_record(&self) -> Option<ValueRecord> {
1049        self.iter().skip(4).find_map(ValueRecord::cast)
1050    }
1051}
1052
1053impl GposIgnore {
1054    pub(crate) fn rules(&self) -> impl Iterator<Item = IgnoreRule> + '_ {
1055        self.iter().filter_map(IgnoreRule::cast)
1056    }
1057}
1058
1059impl LigatureComponent {
1060    /// If the iterator is empty this is a null anchor
1061    pub(crate) fn attachments(&self) -> impl Iterator<Item = AnchorMark> + '_ {
1062        self.iter().filter_map(AnchorMark::cast)
1063    }
1064}
1065
1066impl AnchorMark {
1067    pub(crate) fn anchor(&self) -> Anchor {
1068        self.iter().find_map(Anchor::cast).unwrap()
1069    }
1070
1071    pub(crate) fn mark_class_name(&self) -> Option<GlyphClassName> {
1072        self.iter().find_map(GlyphClassName::cast)
1073    }
1074}
1075
1076impl ValueRecord {
1077    /// If this record is a single metric, return it
1078    pub(crate) fn advance(&self) -> Option<Metric> {
1079        self.iter().next().and_then(Metric::cast)
1080    }
1081
1082    pub(crate) fn null(&self) -> Option<&Token> {
1083        self.iter()
1084            .take(3)
1085            .find(|t| t.kind() == Kind::NullKw)
1086            .and_then(NodeOrToken::as_token)
1087    }
1088
1089    pub(crate) fn named(&self) -> Option<&Token> {
1090        self.find_token(Kind::Ident)
1091    }
1092
1093    // for validation,
1094    pub(crate) fn all_metrics(&self) -> impl Iterator<Item = Metric> + '_ {
1095        self.iter().filter_map(Metric::cast)
1096    }
1097
1098    pub(crate) fn placement(&self) -> Option<[Metric; 4]> {
1099        if self.iter().filter_map(Metric::cast).count() == 4 {
1100            let mut iter = self.iter().filter_map(Metric::cast);
1101            return Some([
1102                iter.next().unwrap(),
1103                iter.next().unwrap(),
1104                iter.next().unwrap(),
1105                iter.next().unwrap(),
1106            ]);
1107        }
1108        None
1109    }
1110
1111    pub(crate) fn device(&self) -> Option<[Device; 4]> {
1112        if self.iter().skip(4).any(|t| t.kind() == Kind::DeviceNode) {
1113            let mut iter = self.iter().filter_map(Device::cast);
1114            return Some([
1115                iter.next().unwrap(),
1116                iter.next().unwrap(),
1117                iter.next().unwrap(),
1118                iter.next().unwrap(),
1119            ]);
1120        }
1121        None
1122    }
1123}
1124
1125impl Device {
1126    fn null(&self) -> Option<&Token> {
1127        self.iter()
1128            .take(4)
1129            .find(|t| t.kind() == Kind::NullKw)
1130            .and_then(NodeOrToken::as_token)
1131    }
1132
1133    fn entries(&self) -> impl Iterator<Item = (Number, Number)> + '_ {
1134        let mut iter = self
1135            .iter()
1136            .filter(|i| i.kind() == Kind::Number || i.kind() == Kind::Comma);
1137        std::iter::from_fn(move || {
1138            let ppem = iter.next().and_then(Number::cast)?;
1139            let pixels = iter.next().and_then(Number::cast).unwrap();
1140            let _maybe_comma = iter.next();
1141            Some((ppem, pixels))
1142        })
1143    }
1144
1145    pub(crate) fn compile(&self) -> Option<write_fonts::tables::layout::Device> {
1146        if self.null().is_some() {
1147            return None;
1148        }
1149
1150        let mut entries = Vec::new();
1151        for (ppem, pix) in self.entries() {
1152            let ppem = ppem.parse_unsigned().expect("validated before now");
1153            let pix = pix.parse_signed();
1154            // if there are gaps in the range, add zeros
1155            if let Some(prev) = entries.last().map(|(pp, _)| *pp) {
1156                for missing in (prev + 1)..ppem {
1157                    entries.push((missing, 0));
1158                }
1159            }
1160            entries.push((ppem, pix));
1161        }
1162
1163        let first = entries.first().unwrap().0;
1164        let last = entries.last().unwrap().0;
1165        let values = entries
1166            .into_iter()
1167            .map(|(_, pix)| i8::try_from(pix).expect("validated before now"))
1168            .collect::<Vec<_>>();
1169
1170        Some(write_fonts::tables::layout::Device::new(
1171            first, last, &values,
1172        ))
1173    }
1174}
1175
1176impl Table {
1177    pub(crate) fn tag(&self) -> Tag {
1178        self.node()
1179            .unwrap()
1180            .iter_children()
1181            .find_map(Tag::cast)
1182            .unwrap()
1183    }
1184}
1185
1186impl BaseTable {
1187    pub(crate) fn horiz_base_tag_list(&self) -> Option<BaseTagList> {
1188        self.iter()
1189            .filter_map(BaseTagList::cast)
1190            .find(BaseTagList::is_horiz)
1191    }
1192
1193    pub(crate) fn vert_base_tag_list(&self) -> Option<BaseTagList> {
1194        self.iter()
1195            .filter_map(BaseTagList::cast)
1196            .find(|b| !b.is_horiz())
1197    }
1198
1199    pub(crate) fn horiz_base_script_record_list(&self) -> Option<BaseScriptList> {
1200        self.iter()
1201            .filter_map(BaseScriptList::cast)
1202            .find(BaseScriptList::is_horiz)
1203    }
1204
1205    pub(crate) fn vert_base_script_record_list(&self) -> Option<BaseScriptList> {
1206        self.iter()
1207            .filter_map(BaseScriptList::cast)
1208            .find(|b| !b.is_horiz())
1209    }
1210}
1211
1212impl BaseTagList {
1213    fn is_horiz(&self) -> bool {
1214        match self.iter().next().map(|t| t.kind()) {
1215            Some(Kind::HorizAxisBaseTagListKw) => true,
1216            Some(Kind::VertAxisBaseTagListKw) => false,
1217            other => panic!("unexpected token in BaseTagList {other:?}"),
1218        }
1219    }
1220
1221    pub(crate) fn tags(&self) -> impl Iterator<Item = Tag> + '_ {
1222        self.iter()
1223            .skip(1)
1224            .take_while(|t| t.kind() != Kind::Semi)
1225            .filter_map(Tag::cast)
1226    }
1227}
1228
1229impl BaseScriptList {
1230    fn is_horiz(&self) -> bool {
1231        match self.iter().next().map(|t| t.kind()) {
1232            Some(Kind::HorizAxisBaseScriptListKw) => true,
1233            Some(Kind::VertAxisBaseScriptListKw) => false,
1234            other => panic!("unexpected token in BaseScriptList {other:?}"),
1235        }
1236    }
1237
1238    pub(crate) fn script_records(&self) -> impl Iterator<Item = ScriptRecord> + '_ {
1239        self.iter()
1240            .skip(1)
1241            .take_while(|t| t.kind() != Kind::Semi)
1242            .filter_map(ScriptRecord::cast)
1243    }
1244}
1245
1246impl ScriptRecord {
1247    pub(crate) fn script(&self) -> Tag {
1248        self.iter().find_map(Tag::cast).unwrap()
1249    }
1250
1251    pub(crate) fn default_baseline(&self) -> Tag {
1252        self.iter().filter_map(Tag::cast).nth(1).unwrap()
1253    }
1254
1255    pub(crate) fn values(&self) -> impl Iterator<Item = Number> + '_ {
1256        self.iter().skip(2).filter_map(Number::cast)
1257    }
1258}
1259
1260impl HheaTable {
1261    pub(crate) fn metrics(&self) -> impl Iterator<Item = MetricRecord> + '_ {
1262        self.iter().filter_map(MetricRecord::cast)
1263    }
1264}
1265
1266impl VheaTable {
1267    pub(crate) fn metrics(&self) -> impl Iterator<Item = MetricRecord> + '_ {
1268        self.iter().filter_map(MetricRecord::cast)
1269    }
1270}
1271
1272impl VmtxTable {
1273    pub(crate) fn statements(&self) -> impl Iterator<Item = VmtxEntry> + '_ {
1274        self.iter().filter_map(VmtxEntry::cast)
1275    }
1276}
1277
1278impl VmtxEntry {
1279    pub(crate) fn keyword(&self) -> &Token {
1280        self.iter().next().and_then(NodeOrToken::as_token).unwrap()
1281    }
1282
1283    pub(crate) fn glyph(&self) -> Glyph {
1284        self.iter().find_map(Glyph::cast).unwrap()
1285    }
1286
1287    pub(crate) fn value(&self) -> Number {
1288        self.iter().find_map(Number::cast).unwrap()
1289    }
1290}
1291
1292impl MetricRecord {
1293    pub(crate) fn keyword(&self) -> &Token {
1294        self.iter().next().and_then(|t| t.as_token()).unwrap()
1295    }
1296
1297    pub(crate) fn metric(&self) -> Metric {
1298        self.iter().find_map(Metric::cast).unwrap()
1299    }
1300}
1301
1302impl Metric {
1303    /// Returns the value of this metric if it is non-variable
1304    pub(crate) fn parse_simple(&self) -> Option<i16> {
1305        match self {
1306            Metric::Scalar(num) => Some(num.parse_signed()),
1307            Metric::Variable(_) | Metric::GlyphsAppNumber(_) => None,
1308        }
1309    }
1310}
1311
1312impl VariableMetric {
1313    pub(crate) fn location_values(&self) -> impl Iterator<Item = LocationValue> + '_ {
1314        self.iter().filter_map(LocationValue::cast)
1315    }
1316}
1317
1318impl LocationValue {
1319    pub(crate) fn location(&self) -> LocationSpec {
1320        self.iter().find_map(LocationSpec::cast).unwrap()
1321    }
1322
1323    pub(crate) fn value(&self) -> Number {
1324        self.iter().find_map(Number::cast).unwrap()
1325    }
1326}
1327
1328impl LocationSpec {
1329    pub(crate) fn items(&self) -> impl Iterator<Item = LocationSpecItem> + '_ {
1330        self.iter().filter_map(LocationSpecItem::cast)
1331    }
1332}
1333
1334impl LocationSpecItem {
1335    pub(crate) fn axis_tag(&self) -> Tag {
1336        self.iter().find_map(Tag::cast).unwrap()
1337    }
1338
1339    pub(crate) fn value(&self) -> AxisLocation {
1340        self.iter().skip(2).find_map(AxisLocation::cast).unwrap()
1341    }
1342}
1343
1344impl AxisLocation {
1345    pub(crate) fn parse(&self) -> crate::compile::AxisLocation {
1346        use crate::compile::AxisLocation as Output;
1347        let value = self.value();
1348        match self.suffix() {
1349            Some(token) if token.text() == "n" => Output::Normalized(value.into()),
1350            Some(token) if token.text() == "d" => Output::Design(value.into()),
1351            Some(token) if token.text() == "u" => Output::User(value.into()),
1352            None => Output::User(value.into()),
1353            Some(_) => unreachable!("we only parse three suffixes"),
1354        }
1355    }
1356
1357    fn value(&self) -> f64 {
1358        let raw = self.iter().next().unwrap();
1359        Number::cast(raw)
1360            .map(|num| num.parse_signed() as f64)
1361            .or_else(|| Float::cast(raw).map(|num| num.parse()))
1362            .unwrap()
1363    }
1364
1365    fn suffix(&self) -> Option<NumberSuffix> {
1366        self.iter().find_map(NumberSuffix::cast)
1367    }
1368}
1369
1370impl Os2Table {
1371    pub(crate) fn statements(&self) -> impl Iterator<Item = Os2TableItem> + '_ {
1372        self.iter().filter_map(Os2TableItem::cast)
1373    }
1374}
1375
1376impl NumberRecord {
1377    pub(crate) fn keyword(&self) -> &Token {
1378        self.iter().next().and_then(|t| t.as_token()).unwrap()
1379    }
1380
1381    pub(crate) fn number(&self) -> Number {
1382        self.iter().find_map(Number::cast).unwrap()
1383    }
1384}
1385
1386impl VendorRecord {
1387    pub(crate) fn value(&self) -> &Token {
1388        self.find_token(Kind::String).unwrap()
1389    }
1390
1391    pub(crate) fn parse_tag(
1392        &self,
1393    ) -> Result<write_fonts::types::Tag, write_fonts::types::InvalidTag> {
1394        let raw = self.value();
1395        write_fonts::types::Tag::new_checked(raw.text.trim_matches('"').as_bytes())
1396    }
1397}
1398
1399impl Os2NumberList {
1400    pub(crate) fn keyword(&self) -> &Token {
1401        self.iter().next().and_then(|t| t.as_token()).unwrap()
1402    }
1403
1404    pub(crate) fn values(&self) -> impl Iterator<Item = Number> + '_ {
1405        self.iter().skip(1).filter_map(Number::cast)
1406    }
1407}
1408
1409impl Os2FamilyClass {
1410    pub(crate) fn value(&self) -> DecOctHex {
1411        self.iter().find_map(DecOctHex::cast).unwrap()
1412    }
1413}
1414
1415impl FeatureNames {
1416    pub(crate) fn keyword(&self) -> &Token {
1417        debug_assert_eq!(self.iter().next().unwrap().kind(), Kind::FeatureNamesKw);
1418        self.iter().next().and_then(|t| t.as_token()).unwrap()
1419    }
1420
1421    pub(crate) fn statements(&self) -> impl Iterator<Item = NameSpec> + '_ {
1422        self.iter().filter_map(NameSpec::cast)
1423    }
1424}
1425
1426impl CvParameters {
1427    pub(crate) fn keyword(&self) -> &Token {
1428        debug_assert_eq!(self.iter().next().unwrap().kind(), Kind::CvParametersKw);
1429        self.iter().next().and_then(|t| t.as_token()).unwrap()
1430    }
1431
1432    pub(crate) fn find_node(&self, kind: Kind) -> Option<CvParametersName> {
1433        self.iter()
1434            .filter_map(CvParametersName::cast)
1435            .find(|node| node.keyword().kind == kind)
1436    }
1437
1438    pub(crate) fn feat_ui_label_name(&self) -> Option<CvParametersName> {
1439        self.find_node(Kind::FeatUiLabelNameIdKw)
1440    }
1441
1442    pub(crate) fn feat_tooltip_text_name(&self) -> Option<CvParametersName> {
1443        self.find_node(Kind::FeatUiTooltipTextNameIdKw)
1444    }
1445
1446    pub(crate) fn sample_text_name(&self) -> Option<CvParametersName> {
1447        self.find_node(Kind::SampleTextNameIdKw)
1448    }
1449
1450    pub(crate) fn param_ui_label_name(&self) -> impl Iterator<Item = CvParametersName> + '_ {
1451        self.iter()
1452            .filter_map(CvParametersName::cast)
1453            .filter(|node| node.keyword().kind == Kind::ParamUiLabelNameIdKw)
1454    }
1455
1456    pub(crate) fn characters(&self) -> impl Iterator<Item = CvParametersChar> + '_ {
1457        self.iter().filter_map(CvParametersChar::cast)
1458    }
1459}
1460
1461impl CvParametersName {
1462    pub(crate) fn keyword(&self) -> &Token {
1463        self.iter().next().and_then(|t| t.as_token()).unwrap()
1464    }
1465
1466    pub(crate) fn statements(&self) -> impl Iterator<Item = NameSpec> + '_ {
1467        self.iter().filter_map(NameSpec::cast)
1468    }
1469}
1470
1471impl CvParametersChar {
1472    pub(crate) fn value(&self) -> DecOctHex {
1473        self.iter().find_map(DecOctHex::cast).unwrap()
1474    }
1475}
1476
1477impl NameTable {
1478    pub(crate) fn statements(&self) -> impl Iterator<Item = NameRecord> + '_ {
1479        self.iter().filter_map(NameRecord::cast)
1480    }
1481}
1482
1483impl NameRecord {
1484    pub(crate) fn name_id(&self) -> DecOctHex {
1485        self.iter().find_map(DecOctHex::cast).unwrap()
1486    }
1487
1488    pub(crate) fn entry(&self) -> NameSpec {
1489        self.iter().find_map(NameSpec::cast).unwrap()
1490    }
1491}
1492
1493impl NameSpec {
1494    pub(crate) fn platform_id(&self) -> Option<DecOctHex> {
1495        self.iter().find_map(DecOctHex::cast)
1496    }
1497
1498    pub(crate) fn platform_and_language_ids(&self) -> Option<(DecOctHex, DecOctHex)> {
1499        let mut iter = self.iter().filter_map(DecOctHex::cast).skip(1);
1500        if let Some(platform) = iter.next() {
1501            let language = iter.next().unwrap();
1502            Some((platform, language))
1503        } else {
1504            None
1505        }
1506    }
1507
1508    pub(crate) fn string_token(&self) -> &Token {
1509        // There is always a string
1510        self.find_token(Kind::String).unwrap()
1511    }
1512
1513    pub(crate) fn string(&self) -> &str {
1514        // The value is always doublequoted so slice out the actual string
1515        let s = self.string_token().as_str();
1516        &s[1..s.len() - 1]
1517    }
1518}
1519
1520impl DecOctHex {
1521    fn parse_raw(&self) -> Result<u32, String> {
1522        match self {
1523            DecOctHex::Decimal(num) => num.text().parse::<u32>().map_err(|e| e.to_string()),
1524            DecOctHex::Octal(num) => u32::from_str_radix(num.text(), 8).map_err(|e| e.to_string()),
1525            DecOctHex::Hex(num) => u32::from_str_radix(num.text().trim_start_matches("0x"), 16)
1526                .map_err(|e| e.to_string()),
1527        }
1528    }
1529
1530    pub(crate) fn parse(&self) -> Result<u16, String> {
1531        self.parse_raw()
1532            .and_then(|x| u16::try_from(x).map_err(|e| e.to_string()))
1533    }
1534
1535    pub(crate) fn parse_char(&self) -> Result<char, String> {
1536        self.parse_raw().and_then(|int| {
1537            char::from_u32(int).ok_or_else(|| format!("{int} is not a unicode codepoint"))
1538        })
1539    }
1540}
1541
1542impl GdefTable {
1543    pub(crate) fn statements(&self) -> impl Iterator<Item = GdefTableItem> + '_ {
1544        self.iter().filter_map(GdefTableItem::cast)
1545    }
1546}
1547
1548impl GdefClassDef {
1549    fn nth_item(&self, n: usize) -> Option<GlyphClass> {
1550        assert!(n < 4);
1551        self.iter()
1552            .filter(|t| t.kind() == Kind::GdefClassDefEntryNode)
1553            .nth(n)
1554            .and_then(GdefClassDefEntry::cast)
1555            .expect("validated")
1556            .iter()
1557            .find_map(GlyphClass::cast)
1558    }
1559
1560    pub(crate) fn base_glyphs(&self) -> Option<GlyphClass> {
1561        self.nth_item(0)
1562    }
1563
1564    pub(crate) fn ligature_glyphs(&self) -> Option<GlyphClass> {
1565        self.nth_item(1)
1566    }
1567
1568    pub(crate) fn mark_glyphs(&self) -> Option<GlyphClass> {
1569        self.nth_item(2)
1570    }
1571
1572    pub(crate) fn component_glyphs(&self) -> Option<GlyphClass> {
1573        self.nth_item(3)
1574    }
1575}
1576
1577impl GdefAttach {
1578    pub(crate) fn target(&self) -> GlyphOrClass {
1579        self.iter().find_map(GlyphOrClass::cast).unwrap()
1580    }
1581
1582    /// of a contourpoint
1583    pub(crate) fn indices(&self) -> impl Iterator<Item = Number> + '_ {
1584        self.iter().filter_map(Number::cast)
1585    }
1586}
1587
1588impl GdefLigatureCaret {
1589    fn by_pos(&self) -> bool {
1590        match self.iter().next().map(|t| t.kind()) {
1591            Some(Kind::LigatureCaretByPosKw) => true,
1592            Some(Kind::LigatureCaretByIndexKw) => false,
1593            other => panic!("unexpected token in ligaturecaret {other:?}"),
1594        }
1595    }
1596
1597    pub(crate) fn target(&self) -> GlyphOrClass {
1598        self.iter().find_map(GlyphOrClass::cast).unwrap()
1599    }
1600
1601    pub(crate) fn values(&self) -> LigatureCaretValue<'_> {
1602        if self.by_pos() {
1603            LigatureCaretValue::Pos(LigatureCaretIter(self))
1604        } else {
1605            LigatureCaretValue::Index(LigatureCaretIter(self))
1606        }
1607    }
1608}
1609
1610// some helpers for handling the different caret representations; one is signed,
1611// the other unsigned.
1612pub(crate) struct LigatureCaretIter<'a>(&'a GdefLigatureCaret);
1613
1614impl LigatureCaretIter<'_> {
1615    pub(crate) fn values(&self) -> impl Iterator<Item = Number> + '_ {
1616        self.0.iter().filter_map(Number::cast)
1617    }
1618}
1619
1620pub(crate) enum LigatureCaretValue<'a> {
1621    Pos(LigatureCaretIter<'a>),
1622    Index(LigatureCaretIter<'a>),
1623}
1624
1625impl HeadTable {
1626    pub(crate) fn statements(&self) -> impl Iterator<Item = HeadFontRevision> + '_ {
1627        self.iter().filter_map(HeadFontRevision::cast)
1628    }
1629}
1630
1631impl HeadFontRevision {
1632    pub(crate) fn value(&self) -> Float {
1633        self.iter().find_map(Float::cast).unwrap()
1634    }
1635}
1636
1637impl StatTable {
1638    pub(crate) fn tag(&self) -> Tag {
1639        self.iter().find_map(Tag::cast).unwrap()
1640    }
1641
1642    pub(crate) fn statements(&self) -> impl Iterator<Item = StatTableItem> + '_ {
1643        self.iter().filter_map(StatTableItem::cast)
1644    }
1645}
1646
1647impl StatElidedFallbackName {
1648    pub(crate) fn elided_fallback_name_id(&self) -> Option<Number> {
1649        self.iter()
1650            .take_while(|t| t.kind() != Kind::NameKw)
1651            .find_map(Number::cast)
1652    }
1653
1654    pub(crate) fn names(&self) -> impl Iterator<Item = NameSpec> + '_ {
1655        self.iter().filter_map(NameSpec::cast)
1656    }
1657}
1658
1659impl StatDesignAxis {
1660    pub(crate) fn tag(&self) -> Tag {
1661        self.iter().find_map(Tag::cast).unwrap()
1662    }
1663
1664    pub(crate) fn ordering(&self) -> Number {
1665        self.iter()
1666            .take_while(|t| t.kind() != Kind::LBrace)
1667            .find_map(Number::cast)
1668            .unwrap()
1669    }
1670
1671    pub(crate) fn names(&self) -> impl Iterator<Item = NameSpec> + '_ {
1672        self.iter()
1673            .skip_while(|t| t.kind() != Kind::LBrace)
1674            .filter_map(NameSpec::cast)
1675    }
1676}
1677
1678impl StatAxisValue {
1679    pub(crate) fn statements(&self) -> impl Iterator<Item = StatAxisValueItem> + '_ {
1680        self.iter().skip(2).filter_map(StatAxisValueItem::cast)
1681    }
1682}
1683
1684impl StatAxisFlag {
1685    /// iterate bits to be accumulated
1686    pub(crate) fn bits(&self) -> impl Iterator<Item = u16> + '_ {
1687        self.iter()
1688            .skip(1)
1689            .take_while(|t| t.kind() != Kind::Semi)
1690            .filter_map(|t| match t.kind() {
1691                Kind::OlderSiblingFontAttributeKw => Some(0x01),
1692                Kind::ElidableAxisValueNameKw => Some(0x02),
1693                t if t.is_trivia() => None,
1694                other => panic!("parser error '{other}'"),
1695            })
1696    }
1697}
1698
1699impl StatAxisLocation {
1700    pub(crate) fn tag(&self) -> Tag {
1701        self.iter().find_map(Tag::cast).unwrap()
1702    }
1703
1704    pub(crate) fn value(&self) -> StatLocationValue {
1705        let mut iter = self.iter().filter_map(FloatLike::cast);
1706        let first = iter.next().unwrap();
1707        let second = match iter.next() {
1708            Some(second) => second,
1709            None => return StatLocationValue::Value(first),
1710        };
1711        match iter.next() {
1712            Some(third) => StatLocationValue::MinMax {
1713                nominal: first,
1714                min: second,
1715                max: third,
1716            },
1717            None => StatLocationValue::Linked {
1718                value: first,
1719                linked: second,
1720            },
1721        }
1722    }
1723}
1724
1725pub(crate) enum StatLocationValue {
1726    Value(FloatLike),
1727    MinMax {
1728        nominal: FloatLike,
1729        min: FloatLike,
1730        max: FloatLike,
1731    },
1732    Linked {
1733        value: FloatLike,
1734        linked: FloatLike,
1735    },
1736}
1737
1738impl SizeMenuName {
1739    pub(crate) fn spec(&self) -> NameSpec {
1740        self.iter().find_map(NameSpec::cast).unwrap()
1741    }
1742}
1743
1744impl Parameters {
1745    pub(crate) fn design_size(&self) -> FloatLike {
1746        self.iter().find_map(FloatLike::cast).unwrap()
1747    }
1748
1749    pub(crate) fn subfamily(&self) -> Number {
1750        self.iter()
1751            .filter(|t| t.kind() == Kind::Number || t.kind() == Kind::Float)
1752            .nth(1)
1753            .and_then(Number::cast)
1754            .unwrap()
1755    }
1756
1757    pub(crate) fn range_start(&self) -> Option<FloatLike> {
1758        self.iter().filter_map(FloatLike::cast).nth(2)
1759    }
1760
1761    pub(crate) fn range_end(&self) -> Option<FloatLike> {
1762        self.iter().filter_map(FloatLike::cast).nth(3)
1763    }
1764}
1765
1766impl FeatureRef {
1767    pub(crate) fn keyword(&self) -> &Token {
1768        self.find_token(Kind::FeatureKw).unwrap()
1769    }
1770
1771    pub(crate) fn feature(&self) -> Tag {
1772        self.iter().find_map(Tag::cast).unwrap()
1773    }
1774}
1775
1776impl GlyphsAppNumber {
1777    pub(crate) fn value(&self) -> GlyphsAppNumberValue {
1778        self.iter().find_map(GlyphsAppNumberValue::cast).unwrap()
1779    }
1780}
1781
1782impl GlyphsAppNumberExpr {
1783    pub(crate) fn items(&self) -> impl Iterator<Item = GlyphsAppExprItem> + '_ {
1784        self.iter().filter_map(GlyphsAppExprItem::cast)
1785    }
1786}