Skip to main content

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