xref: /aosp_15_r20/external/fonttools/Lib/fontTools/voltLib/ast.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.voltLib.error import VoltLibError
2*e1fe3e4aSElliott Hughesfrom typing import NamedTuple
3*e1fe3e4aSElliott Hughes
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughesclass Pos(NamedTuple):
6*e1fe3e4aSElliott Hughes    adv: int
7*e1fe3e4aSElliott Hughes    dx: int
8*e1fe3e4aSElliott Hughes    dy: int
9*e1fe3e4aSElliott Hughes    adv_adjust_by: dict
10*e1fe3e4aSElliott Hughes    dx_adjust_by: dict
11*e1fe3e4aSElliott Hughes    dy_adjust_by: dict
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hughes    def __str__(self):
14*e1fe3e4aSElliott Hughes        res = " POS"
15*e1fe3e4aSElliott Hughes        for attr in ("adv", "dx", "dy"):
16*e1fe3e4aSElliott Hughes            value = getattr(self, attr)
17*e1fe3e4aSElliott Hughes            if value is not None:
18*e1fe3e4aSElliott Hughes                res += f" {attr.upper()} {value}"
19*e1fe3e4aSElliott Hughes                adjust_by = getattr(self, f"{attr}_adjust_by", {})
20*e1fe3e4aSElliott Hughes                for size, adjustment in adjust_by.items():
21*e1fe3e4aSElliott Hughes                    res += f" ADJUST_BY {adjustment} AT {size}"
22*e1fe3e4aSElliott Hughes        res += " END_POS"
23*e1fe3e4aSElliott Hughes        return res
24*e1fe3e4aSElliott Hughes
25*e1fe3e4aSElliott Hughes
26*e1fe3e4aSElliott Hughesclass Element(object):
27*e1fe3e4aSElliott Hughes    def __init__(self, location=None):
28*e1fe3e4aSElliott Hughes        self.location = location
29*e1fe3e4aSElliott Hughes
30*e1fe3e4aSElliott Hughes    def build(self, builder):
31*e1fe3e4aSElliott Hughes        pass
32*e1fe3e4aSElliott Hughes
33*e1fe3e4aSElliott Hughes    def __str__(self):
34*e1fe3e4aSElliott Hughes        raise NotImplementedError
35*e1fe3e4aSElliott Hughes
36*e1fe3e4aSElliott Hughes
37*e1fe3e4aSElliott Hughesclass Statement(Element):
38*e1fe3e4aSElliott Hughes    pass
39*e1fe3e4aSElliott Hughes
40*e1fe3e4aSElliott Hughes
41*e1fe3e4aSElliott Hughesclass Expression(Element):
42*e1fe3e4aSElliott Hughes    pass
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughes
45*e1fe3e4aSElliott Hughesclass VoltFile(Statement):
46*e1fe3e4aSElliott Hughes    def __init__(self):
47*e1fe3e4aSElliott Hughes        Statement.__init__(self, location=None)
48*e1fe3e4aSElliott Hughes        self.statements = []
49*e1fe3e4aSElliott Hughes
50*e1fe3e4aSElliott Hughes    def build(self, builder):
51*e1fe3e4aSElliott Hughes        for s in self.statements:
52*e1fe3e4aSElliott Hughes            s.build(builder)
53*e1fe3e4aSElliott Hughes
54*e1fe3e4aSElliott Hughes    def __str__(self):
55*e1fe3e4aSElliott Hughes        return "\n" + "\n".join(str(s) for s in self.statements) + " END\n"
56*e1fe3e4aSElliott Hughes
57*e1fe3e4aSElliott Hughes
58*e1fe3e4aSElliott Hughesclass GlyphDefinition(Statement):
59*e1fe3e4aSElliott Hughes    def __init__(self, name, gid, gunicode, gtype, components, location=None):
60*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
61*e1fe3e4aSElliott Hughes        self.name = name
62*e1fe3e4aSElliott Hughes        self.id = gid
63*e1fe3e4aSElliott Hughes        self.unicode = gunicode
64*e1fe3e4aSElliott Hughes        self.type = gtype
65*e1fe3e4aSElliott Hughes        self.components = components
66*e1fe3e4aSElliott Hughes
67*e1fe3e4aSElliott Hughes    def __str__(self):
68*e1fe3e4aSElliott Hughes        res = f'DEF_GLYPH "{self.name}" ID {self.id}'
69*e1fe3e4aSElliott Hughes        if self.unicode is not None:
70*e1fe3e4aSElliott Hughes            if len(self.unicode) > 1:
71*e1fe3e4aSElliott Hughes                unicodes = ",".join(f"U+{u:04X}" for u in self.unicode)
72*e1fe3e4aSElliott Hughes                res += f' UNICODEVALUES "{unicodes}"'
73*e1fe3e4aSElliott Hughes            else:
74*e1fe3e4aSElliott Hughes                res += f" UNICODE {self.unicode[0]}"
75*e1fe3e4aSElliott Hughes        if self.type is not None:
76*e1fe3e4aSElliott Hughes            res += f" TYPE {self.type}"
77*e1fe3e4aSElliott Hughes        if self.components is not None:
78*e1fe3e4aSElliott Hughes            res += f" COMPONENTS {self.components}"
79*e1fe3e4aSElliott Hughes        res += " END_GLYPH"
80*e1fe3e4aSElliott Hughes        return res
81*e1fe3e4aSElliott Hughes
82*e1fe3e4aSElliott Hughes
83*e1fe3e4aSElliott Hughesclass GroupDefinition(Statement):
84*e1fe3e4aSElliott Hughes    def __init__(self, name, enum, location=None):
85*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
86*e1fe3e4aSElliott Hughes        self.name = name
87*e1fe3e4aSElliott Hughes        self.enum = enum
88*e1fe3e4aSElliott Hughes        self.glyphs_ = None
89*e1fe3e4aSElliott Hughes
90*e1fe3e4aSElliott Hughes    def glyphSet(self, groups=None):
91*e1fe3e4aSElliott Hughes        if groups is not None and self.name in groups:
92*e1fe3e4aSElliott Hughes            raise VoltLibError(
93*e1fe3e4aSElliott Hughes                'Group "%s" contains itself.' % (self.name), self.location
94*e1fe3e4aSElliott Hughes            )
95*e1fe3e4aSElliott Hughes        if self.glyphs_ is None:
96*e1fe3e4aSElliott Hughes            if groups is None:
97*e1fe3e4aSElliott Hughes                groups = set({self.name})
98*e1fe3e4aSElliott Hughes            else:
99*e1fe3e4aSElliott Hughes                groups.add(self.name)
100*e1fe3e4aSElliott Hughes            self.glyphs_ = self.enum.glyphSet(groups)
101*e1fe3e4aSElliott Hughes        return self.glyphs_
102*e1fe3e4aSElliott Hughes
103*e1fe3e4aSElliott Hughes    def __str__(self):
104*e1fe3e4aSElliott Hughes        enum = self.enum and str(self.enum) or ""
105*e1fe3e4aSElliott Hughes        return f'DEF_GROUP "{self.name}"\n{enum}\nEND_GROUP'
106*e1fe3e4aSElliott Hughes
107*e1fe3e4aSElliott Hughes
108*e1fe3e4aSElliott Hughesclass GlyphName(Expression):
109*e1fe3e4aSElliott Hughes    """A single glyph name, such as cedilla."""
110*e1fe3e4aSElliott Hughes
111*e1fe3e4aSElliott Hughes    def __init__(self, glyph, location=None):
112*e1fe3e4aSElliott Hughes        Expression.__init__(self, location)
113*e1fe3e4aSElliott Hughes        self.glyph = glyph
114*e1fe3e4aSElliott Hughes
115*e1fe3e4aSElliott Hughes    def glyphSet(self):
116*e1fe3e4aSElliott Hughes        return (self.glyph,)
117*e1fe3e4aSElliott Hughes
118*e1fe3e4aSElliott Hughes    def __str__(self):
119*e1fe3e4aSElliott Hughes        return f' GLYPH "{self.glyph}"'
120*e1fe3e4aSElliott Hughes
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughesclass Enum(Expression):
123*e1fe3e4aSElliott Hughes    """An enum"""
124*e1fe3e4aSElliott Hughes
125*e1fe3e4aSElliott Hughes    def __init__(self, enum, location=None):
126*e1fe3e4aSElliott Hughes        Expression.__init__(self, location)
127*e1fe3e4aSElliott Hughes        self.enum = enum
128*e1fe3e4aSElliott Hughes
129*e1fe3e4aSElliott Hughes    def __iter__(self):
130*e1fe3e4aSElliott Hughes        for e in self.glyphSet():
131*e1fe3e4aSElliott Hughes            yield e
132*e1fe3e4aSElliott Hughes
133*e1fe3e4aSElliott Hughes    def glyphSet(self, groups=None):
134*e1fe3e4aSElliott Hughes        glyphs = []
135*e1fe3e4aSElliott Hughes        for element in self.enum:
136*e1fe3e4aSElliott Hughes            if isinstance(element, (GroupName, Enum)):
137*e1fe3e4aSElliott Hughes                glyphs.extend(element.glyphSet(groups))
138*e1fe3e4aSElliott Hughes            else:
139*e1fe3e4aSElliott Hughes                glyphs.extend(element.glyphSet())
140*e1fe3e4aSElliott Hughes        return tuple(glyphs)
141*e1fe3e4aSElliott Hughes
142*e1fe3e4aSElliott Hughes    def __str__(self):
143*e1fe3e4aSElliott Hughes        enum = "".join(str(e) for e in self.enum)
144*e1fe3e4aSElliott Hughes        return f" ENUM{enum} END_ENUM"
145*e1fe3e4aSElliott Hughes
146*e1fe3e4aSElliott Hughes
147*e1fe3e4aSElliott Hughesclass GroupName(Expression):
148*e1fe3e4aSElliott Hughes    """A glyph group"""
149*e1fe3e4aSElliott Hughes
150*e1fe3e4aSElliott Hughes    def __init__(self, group, parser, location=None):
151*e1fe3e4aSElliott Hughes        Expression.__init__(self, location)
152*e1fe3e4aSElliott Hughes        self.group = group
153*e1fe3e4aSElliott Hughes        self.parser_ = parser
154*e1fe3e4aSElliott Hughes
155*e1fe3e4aSElliott Hughes    def glyphSet(self, groups=None):
156*e1fe3e4aSElliott Hughes        group = self.parser_.resolve_group(self.group)
157*e1fe3e4aSElliott Hughes        if group is not None:
158*e1fe3e4aSElliott Hughes            self.glyphs_ = group.glyphSet(groups)
159*e1fe3e4aSElliott Hughes            return self.glyphs_
160*e1fe3e4aSElliott Hughes        else:
161*e1fe3e4aSElliott Hughes            raise VoltLibError(
162*e1fe3e4aSElliott Hughes                'Group "%s" is used but undefined.' % (self.group), self.location
163*e1fe3e4aSElliott Hughes            )
164*e1fe3e4aSElliott Hughes
165*e1fe3e4aSElliott Hughes    def __str__(self):
166*e1fe3e4aSElliott Hughes        return f' GROUP "{self.group}"'
167*e1fe3e4aSElliott Hughes
168*e1fe3e4aSElliott Hughes
169*e1fe3e4aSElliott Hughesclass Range(Expression):
170*e1fe3e4aSElliott Hughes    """A glyph range"""
171*e1fe3e4aSElliott Hughes
172*e1fe3e4aSElliott Hughes    def __init__(self, start, end, parser, location=None):
173*e1fe3e4aSElliott Hughes        Expression.__init__(self, location)
174*e1fe3e4aSElliott Hughes        self.start = start
175*e1fe3e4aSElliott Hughes        self.end = end
176*e1fe3e4aSElliott Hughes        self.parser = parser
177*e1fe3e4aSElliott Hughes
178*e1fe3e4aSElliott Hughes    def glyphSet(self):
179*e1fe3e4aSElliott Hughes        return tuple(self.parser.glyph_range(self.start, self.end))
180*e1fe3e4aSElliott Hughes
181*e1fe3e4aSElliott Hughes    def __str__(self):
182*e1fe3e4aSElliott Hughes        return f' RANGE "{self.start}" TO "{self.end}"'
183*e1fe3e4aSElliott Hughes
184*e1fe3e4aSElliott Hughes
185*e1fe3e4aSElliott Hughesclass ScriptDefinition(Statement):
186*e1fe3e4aSElliott Hughes    def __init__(self, name, tag, langs, location=None):
187*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
188*e1fe3e4aSElliott Hughes        self.name = name
189*e1fe3e4aSElliott Hughes        self.tag = tag
190*e1fe3e4aSElliott Hughes        self.langs = langs
191*e1fe3e4aSElliott Hughes
192*e1fe3e4aSElliott Hughes    def __str__(self):
193*e1fe3e4aSElliott Hughes        res = "DEF_SCRIPT"
194*e1fe3e4aSElliott Hughes        if self.name is not None:
195*e1fe3e4aSElliott Hughes            res += f' NAME "{self.name}"'
196*e1fe3e4aSElliott Hughes        res += f' TAG "{self.tag}"\n\n'
197*e1fe3e4aSElliott Hughes        for lang in self.langs:
198*e1fe3e4aSElliott Hughes            res += f"{lang}"
199*e1fe3e4aSElliott Hughes        res += "END_SCRIPT"
200*e1fe3e4aSElliott Hughes        return res
201*e1fe3e4aSElliott Hughes
202*e1fe3e4aSElliott Hughes
203*e1fe3e4aSElliott Hughesclass LangSysDefinition(Statement):
204*e1fe3e4aSElliott Hughes    def __init__(self, name, tag, features, location=None):
205*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
206*e1fe3e4aSElliott Hughes        self.name = name
207*e1fe3e4aSElliott Hughes        self.tag = tag
208*e1fe3e4aSElliott Hughes        self.features = features
209*e1fe3e4aSElliott Hughes
210*e1fe3e4aSElliott Hughes    def __str__(self):
211*e1fe3e4aSElliott Hughes        res = "DEF_LANGSYS"
212*e1fe3e4aSElliott Hughes        if self.name is not None:
213*e1fe3e4aSElliott Hughes            res += f' NAME "{self.name}"'
214*e1fe3e4aSElliott Hughes        res += f' TAG "{self.tag}"\n\n'
215*e1fe3e4aSElliott Hughes        for feature in self.features:
216*e1fe3e4aSElliott Hughes            res += f"{feature}"
217*e1fe3e4aSElliott Hughes        res += "END_LANGSYS\n"
218*e1fe3e4aSElliott Hughes        return res
219*e1fe3e4aSElliott Hughes
220*e1fe3e4aSElliott Hughes
221*e1fe3e4aSElliott Hughesclass FeatureDefinition(Statement):
222*e1fe3e4aSElliott Hughes    def __init__(self, name, tag, lookups, location=None):
223*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
224*e1fe3e4aSElliott Hughes        self.name = name
225*e1fe3e4aSElliott Hughes        self.tag = tag
226*e1fe3e4aSElliott Hughes        self.lookups = lookups
227*e1fe3e4aSElliott Hughes
228*e1fe3e4aSElliott Hughes    def __str__(self):
229*e1fe3e4aSElliott Hughes        res = f'DEF_FEATURE NAME "{self.name}" TAG "{self.tag}"\n'
230*e1fe3e4aSElliott Hughes        res += " " + " ".join(f'LOOKUP "{l}"' for l in self.lookups) + "\n"
231*e1fe3e4aSElliott Hughes        res += "END_FEATURE\n"
232*e1fe3e4aSElliott Hughes        return res
233*e1fe3e4aSElliott Hughes
234*e1fe3e4aSElliott Hughes
235*e1fe3e4aSElliott Hughesclass LookupDefinition(Statement):
236*e1fe3e4aSElliott Hughes    def __init__(
237*e1fe3e4aSElliott Hughes        self,
238*e1fe3e4aSElliott Hughes        name,
239*e1fe3e4aSElliott Hughes        process_base,
240*e1fe3e4aSElliott Hughes        process_marks,
241*e1fe3e4aSElliott Hughes        mark_glyph_set,
242*e1fe3e4aSElliott Hughes        direction,
243*e1fe3e4aSElliott Hughes        reversal,
244*e1fe3e4aSElliott Hughes        comments,
245*e1fe3e4aSElliott Hughes        context,
246*e1fe3e4aSElliott Hughes        sub,
247*e1fe3e4aSElliott Hughes        pos,
248*e1fe3e4aSElliott Hughes        location=None,
249*e1fe3e4aSElliott Hughes    ):
250*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
251*e1fe3e4aSElliott Hughes        self.name = name
252*e1fe3e4aSElliott Hughes        self.process_base = process_base
253*e1fe3e4aSElliott Hughes        self.process_marks = process_marks
254*e1fe3e4aSElliott Hughes        self.mark_glyph_set = mark_glyph_set
255*e1fe3e4aSElliott Hughes        self.direction = direction
256*e1fe3e4aSElliott Hughes        self.reversal = reversal
257*e1fe3e4aSElliott Hughes        self.comments = comments
258*e1fe3e4aSElliott Hughes        self.context = context
259*e1fe3e4aSElliott Hughes        self.sub = sub
260*e1fe3e4aSElliott Hughes        self.pos = pos
261*e1fe3e4aSElliott Hughes
262*e1fe3e4aSElliott Hughes    def __str__(self):
263*e1fe3e4aSElliott Hughes        res = f'DEF_LOOKUP "{self.name}"'
264*e1fe3e4aSElliott Hughes        res += f' {self.process_base and "PROCESS_BASE" or "SKIP_BASE"}'
265*e1fe3e4aSElliott Hughes        if self.process_marks:
266*e1fe3e4aSElliott Hughes            res += " PROCESS_MARKS "
267*e1fe3e4aSElliott Hughes            if self.mark_glyph_set:
268*e1fe3e4aSElliott Hughes                res += f'MARK_GLYPH_SET "{self.mark_glyph_set}"'
269*e1fe3e4aSElliott Hughes            elif isinstance(self.process_marks, str):
270*e1fe3e4aSElliott Hughes                res += f'"{self.process_marks}"'
271*e1fe3e4aSElliott Hughes            else:
272*e1fe3e4aSElliott Hughes                res += "ALL"
273*e1fe3e4aSElliott Hughes        else:
274*e1fe3e4aSElliott Hughes            res += " SKIP_MARKS"
275*e1fe3e4aSElliott Hughes        if self.direction is not None:
276*e1fe3e4aSElliott Hughes            res += f" DIRECTION {self.direction}"
277*e1fe3e4aSElliott Hughes        if self.reversal:
278*e1fe3e4aSElliott Hughes            res += " REVERSAL"
279*e1fe3e4aSElliott Hughes        if self.comments is not None:
280*e1fe3e4aSElliott Hughes            comments = self.comments.replace("\n", r"\n")
281*e1fe3e4aSElliott Hughes            res += f'\nCOMMENTS "{comments}"'
282*e1fe3e4aSElliott Hughes        if self.context:
283*e1fe3e4aSElliott Hughes            res += "\n" + "\n".join(str(c) for c in self.context)
284*e1fe3e4aSElliott Hughes        else:
285*e1fe3e4aSElliott Hughes            res += "\nIN_CONTEXT\nEND_CONTEXT"
286*e1fe3e4aSElliott Hughes        if self.sub:
287*e1fe3e4aSElliott Hughes            res += f"\n{self.sub}"
288*e1fe3e4aSElliott Hughes        if self.pos:
289*e1fe3e4aSElliott Hughes            res += f"\n{self.pos}"
290*e1fe3e4aSElliott Hughes        return res
291*e1fe3e4aSElliott Hughes
292*e1fe3e4aSElliott Hughes
293*e1fe3e4aSElliott Hughesclass SubstitutionDefinition(Statement):
294*e1fe3e4aSElliott Hughes    def __init__(self, mapping, location=None):
295*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
296*e1fe3e4aSElliott Hughes        self.mapping = mapping
297*e1fe3e4aSElliott Hughes
298*e1fe3e4aSElliott Hughes    def __str__(self):
299*e1fe3e4aSElliott Hughes        res = "AS_SUBSTITUTION\n"
300*e1fe3e4aSElliott Hughes        for src, dst in self.mapping.items():
301*e1fe3e4aSElliott Hughes            src = "".join(str(s) for s in src)
302*e1fe3e4aSElliott Hughes            dst = "".join(str(d) for d in dst)
303*e1fe3e4aSElliott Hughes            res += f"SUB{src}\nWITH{dst}\nEND_SUB\n"
304*e1fe3e4aSElliott Hughes        res += "END_SUBSTITUTION"
305*e1fe3e4aSElliott Hughes        return res
306*e1fe3e4aSElliott Hughes
307*e1fe3e4aSElliott Hughes
308*e1fe3e4aSElliott Hughesclass SubstitutionSingleDefinition(SubstitutionDefinition):
309*e1fe3e4aSElliott Hughes    pass
310*e1fe3e4aSElliott Hughes
311*e1fe3e4aSElliott Hughes
312*e1fe3e4aSElliott Hughesclass SubstitutionMultipleDefinition(SubstitutionDefinition):
313*e1fe3e4aSElliott Hughes    pass
314*e1fe3e4aSElliott Hughes
315*e1fe3e4aSElliott Hughes
316*e1fe3e4aSElliott Hughesclass SubstitutionLigatureDefinition(SubstitutionDefinition):
317*e1fe3e4aSElliott Hughes    pass
318*e1fe3e4aSElliott Hughes
319*e1fe3e4aSElliott Hughes
320*e1fe3e4aSElliott Hughesclass SubstitutionReverseChainingSingleDefinition(SubstitutionDefinition):
321*e1fe3e4aSElliott Hughes    pass
322*e1fe3e4aSElliott Hughes
323*e1fe3e4aSElliott Hughes
324*e1fe3e4aSElliott Hughesclass PositionAttachDefinition(Statement):
325*e1fe3e4aSElliott Hughes    def __init__(self, coverage, coverage_to, location=None):
326*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
327*e1fe3e4aSElliott Hughes        self.coverage = coverage
328*e1fe3e4aSElliott Hughes        self.coverage_to = coverage_to
329*e1fe3e4aSElliott Hughes
330*e1fe3e4aSElliott Hughes    def __str__(self):
331*e1fe3e4aSElliott Hughes        coverage = "".join(str(c) for c in self.coverage)
332*e1fe3e4aSElliott Hughes        res = f"AS_POSITION\nATTACH{coverage}\nTO"
333*e1fe3e4aSElliott Hughes        for coverage, anchor in self.coverage_to:
334*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
335*e1fe3e4aSElliott Hughes            res += f'{coverage} AT ANCHOR "{anchor}"'
336*e1fe3e4aSElliott Hughes        res += "\nEND_ATTACH\nEND_POSITION"
337*e1fe3e4aSElliott Hughes        return res
338*e1fe3e4aSElliott Hughes
339*e1fe3e4aSElliott Hughes
340*e1fe3e4aSElliott Hughesclass PositionAttachCursiveDefinition(Statement):
341*e1fe3e4aSElliott Hughes    def __init__(self, coverages_exit, coverages_enter, location=None):
342*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
343*e1fe3e4aSElliott Hughes        self.coverages_exit = coverages_exit
344*e1fe3e4aSElliott Hughes        self.coverages_enter = coverages_enter
345*e1fe3e4aSElliott Hughes
346*e1fe3e4aSElliott Hughes    def __str__(self):
347*e1fe3e4aSElliott Hughes        res = "AS_POSITION\nATTACH_CURSIVE"
348*e1fe3e4aSElliott Hughes        for coverage in self.coverages_exit:
349*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
350*e1fe3e4aSElliott Hughes            res += f"\nEXIT {coverage}"
351*e1fe3e4aSElliott Hughes        for coverage in self.coverages_enter:
352*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
353*e1fe3e4aSElliott Hughes            res += f"\nENTER {coverage}"
354*e1fe3e4aSElliott Hughes        res += "\nEND_ATTACH\nEND_POSITION"
355*e1fe3e4aSElliott Hughes        return res
356*e1fe3e4aSElliott Hughes
357*e1fe3e4aSElliott Hughes
358*e1fe3e4aSElliott Hughesclass PositionAdjustPairDefinition(Statement):
359*e1fe3e4aSElliott Hughes    def __init__(self, coverages_1, coverages_2, adjust_pair, location=None):
360*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
361*e1fe3e4aSElliott Hughes        self.coverages_1 = coverages_1
362*e1fe3e4aSElliott Hughes        self.coverages_2 = coverages_2
363*e1fe3e4aSElliott Hughes        self.adjust_pair = adjust_pair
364*e1fe3e4aSElliott Hughes
365*e1fe3e4aSElliott Hughes    def __str__(self):
366*e1fe3e4aSElliott Hughes        res = "AS_POSITION\nADJUST_PAIR\n"
367*e1fe3e4aSElliott Hughes        for coverage in self.coverages_1:
368*e1fe3e4aSElliott Hughes            coverage = " ".join(str(c) for c in coverage)
369*e1fe3e4aSElliott Hughes            res += f" FIRST {coverage}"
370*e1fe3e4aSElliott Hughes        res += "\n"
371*e1fe3e4aSElliott Hughes        for coverage in self.coverages_2:
372*e1fe3e4aSElliott Hughes            coverage = " ".join(str(c) for c in coverage)
373*e1fe3e4aSElliott Hughes            res += f" SECOND {coverage}"
374*e1fe3e4aSElliott Hughes        res += "\n"
375*e1fe3e4aSElliott Hughes        for (id_1, id_2), (pos_1, pos_2) in self.adjust_pair.items():
376*e1fe3e4aSElliott Hughes            res += f" {id_1} {id_2} BY{pos_1}{pos_2}\n"
377*e1fe3e4aSElliott Hughes        res += "\nEND_ADJUST\nEND_POSITION"
378*e1fe3e4aSElliott Hughes        return res
379*e1fe3e4aSElliott Hughes
380*e1fe3e4aSElliott Hughes
381*e1fe3e4aSElliott Hughesclass PositionAdjustSingleDefinition(Statement):
382*e1fe3e4aSElliott Hughes    def __init__(self, adjust_single, location=None):
383*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
384*e1fe3e4aSElliott Hughes        self.adjust_single = adjust_single
385*e1fe3e4aSElliott Hughes
386*e1fe3e4aSElliott Hughes    def __str__(self):
387*e1fe3e4aSElliott Hughes        res = "AS_POSITION\nADJUST_SINGLE"
388*e1fe3e4aSElliott Hughes        for coverage, pos in self.adjust_single:
389*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
390*e1fe3e4aSElliott Hughes            res += f"{coverage} BY{pos}"
391*e1fe3e4aSElliott Hughes        res += "\nEND_ADJUST\nEND_POSITION"
392*e1fe3e4aSElliott Hughes        return res
393*e1fe3e4aSElliott Hughes
394*e1fe3e4aSElliott Hughes
395*e1fe3e4aSElliott Hughesclass ContextDefinition(Statement):
396*e1fe3e4aSElliott Hughes    def __init__(self, ex_or_in, left=None, right=None, location=None):
397*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
398*e1fe3e4aSElliott Hughes        self.ex_or_in = ex_or_in
399*e1fe3e4aSElliott Hughes        self.left = left if left is not None else []
400*e1fe3e4aSElliott Hughes        self.right = right if right is not None else []
401*e1fe3e4aSElliott Hughes
402*e1fe3e4aSElliott Hughes    def __str__(self):
403*e1fe3e4aSElliott Hughes        res = self.ex_or_in + "\n"
404*e1fe3e4aSElliott Hughes        for coverage in self.left:
405*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
406*e1fe3e4aSElliott Hughes            res += f" LEFT{coverage}\n"
407*e1fe3e4aSElliott Hughes        for coverage in self.right:
408*e1fe3e4aSElliott Hughes            coverage = "".join(str(c) for c in coverage)
409*e1fe3e4aSElliott Hughes            res += f" RIGHT{coverage}\n"
410*e1fe3e4aSElliott Hughes        res += "END_CONTEXT"
411*e1fe3e4aSElliott Hughes        return res
412*e1fe3e4aSElliott Hughes
413*e1fe3e4aSElliott Hughes
414*e1fe3e4aSElliott Hughesclass AnchorDefinition(Statement):
415*e1fe3e4aSElliott Hughes    def __init__(self, name, gid, glyph_name, component, locked, pos, location=None):
416*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
417*e1fe3e4aSElliott Hughes        self.name = name
418*e1fe3e4aSElliott Hughes        self.gid = gid
419*e1fe3e4aSElliott Hughes        self.glyph_name = glyph_name
420*e1fe3e4aSElliott Hughes        self.component = component
421*e1fe3e4aSElliott Hughes        self.locked = locked
422*e1fe3e4aSElliott Hughes        self.pos = pos
423*e1fe3e4aSElliott Hughes
424*e1fe3e4aSElliott Hughes    def __str__(self):
425*e1fe3e4aSElliott Hughes        locked = self.locked and " LOCKED" or ""
426*e1fe3e4aSElliott Hughes        return (
427*e1fe3e4aSElliott Hughes            f'DEF_ANCHOR "{self.name}"'
428*e1fe3e4aSElliott Hughes            f" ON {self.gid}"
429*e1fe3e4aSElliott Hughes            f" GLYPH {self.glyph_name}"
430*e1fe3e4aSElliott Hughes            f" COMPONENT {self.component}"
431*e1fe3e4aSElliott Hughes            f"{locked}"
432*e1fe3e4aSElliott Hughes            f" AT {self.pos} END_ANCHOR"
433*e1fe3e4aSElliott Hughes        )
434*e1fe3e4aSElliott Hughes
435*e1fe3e4aSElliott Hughes
436*e1fe3e4aSElliott Hughesclass SettingDefinition(Statement):
437*e1fe3e4aSElliott Hughes    def __init__(self, name, value, location=None):
438*e1fe3e4aSElliott Hughes        Statement.__init__(self, location)
439*e1fe3e4aSElliott Hughes        self.name = name
440*e1fe3e4aSElliott Hughes        self.value = value
441*e1fe3e4aSElliott Hughes
442*e1fe3e4aSElliott Hughes    def __str__(self):
443*e1fe3e4aSElliott Hughes        if self.value is True:
444*e1fe3e4aSElliott Hughes            return f"{self.name}"
445*e1fe3e4aSElliott Hughes        if isinstance(self.value, (tuple, list)):
446*e1fe3e4aSElliott Hughes            value = " ".join(str(v) for v in self.value)
447*e1fe3e4aSElliott Hughes            return f"{self.name} {value}"
448*e1fe3e4aSElliott Hughes        return f"{self.name} {self.value}"
449