From 3f3b6098e8f2a5731d7d64064713e6fd03be97ff Mon Sep 17 00:00:00 2001 From: deathaxe Date: Sun, 19 Feb 2023 12:01:08 +0100 Subject: [PATCH] Refactor Syntax This commit refactors PostScript syntax to... 1. follow ST4's scope naming guidelines 2. convert all anonymous to named contexts --- PostScript.sublime-syntax | 391 +++++++++++++++++++++++------------ syntax_test_ps.ps | Bin 3490 -> 0 bytes tests/syntax_test_ps.ps | Bin 0 -> 5727 bytes tests/syntax_test_shebang.ps | Bin 0 -> 487 bytes 4 files changed, 256 insertions(+), 135 deletions(-) delete mode 100644 syntax_test_ps.ps create mode 100644 tests/syntax_test_ps.ps create mode 100644 tests/syntax_test_shebang.ps diff --git a/PostScript.sublime-syntax b/PostScript.sublime-syntax index d430daf..e5b4820 100644 --- a/PostScript.sublime-syntax +++ b/PostScript.sublime-syntax @@ -1,163 +1,284 @@ %YAML 1.2 --- name: PostScript +scope: source.postscript + file_extensions: - ps -scope: source.ps + - eps + +first_line_match: ^%!PS\b + +variables: + # identifiers + break: (?!{{ident_char}}) + ident_char: '[^()<>\[\]{}/%\s]' + + # numbers + exponent: (?:[eE][-+]?\d+) + + directives: |- + (?x: BeginBinary | BeginCustomColor | BeginData | BeginDefaults | BeginDocument | BeginEmulation | BeginExitServer + | BeginFeature | BeginFile | BeginFont | BeginObject | BeginPageSetup | BeginPaperSize | BeginPreview | BeginProcSet + | BeginProcessColor | BeginProlog | BeginResource | BeginSetup | BoundingBox | CMYKCustomColor | ChangeFont + | Copyright | CreationDate | Creator | DocumentCustomColors | DocumentData | DocumentFonts | DocumentMedia + | DocumentNeededFiles | DocumentNeededFonts | DocumentNeededProcSets | DocumentNeededResources | DocumentPaperColors + | DocumentPaperForms | DocumentPaperSizes | DocumentPaperWeights | DocumentPrinterRequired | DocumentProcSets + | DocumentProcessColors | DocumentSuppliedFiles | DocumentSuppliedFonts | DocumentSuppliedProcSets + | DocumentSuppliedResources | EOF | Emulation | EndBinary | EndComments | EndCustomColor | EndData | EndDefaults + | EndDocument | EndEmulation | EndExitServer | EndFeature | EndFile | EndFont | EndObject | EndPageSetup + | EndPaperSize | EndPreview | EndProcSet | EndProcessColor | EndProlog | EndResource | EndSetup | ExecuteFile + | Extensions | Feature | For | IncludeDocument | IncludeFeature | IncludeFile | IncludeFont | IncludeProcSet + | IncludeResource | LanguageLevel | OperatorIntervention | OperatorMessage | Orientation | Page | PageBoundingBox + | PageCustomColors | PageCustomColors | PageFiles | PageFonts | PageMedia | PageOrder | PageOrientation + | PageProcessColors | PageProcessColors | PageRequirements | PageResources | PageTrailer | Pages | PaperColor + | PaperForm | PaperSize | PaperWeight | ProofMode | RGBCustomColor | Requirements | Routing | Title | Trailer + | VMlocation | VMusage | Version | Version | \+ | \?BeginFeatureQuery | \?BeginFileQuery | \?BeginFontListQuery + | \?BeginFontQuery | \?BeginPrinterQuery | \?BeginProcSetQuery | \?BeginQuery | \?BeginResourceListQuery + | \?BeginResourceQuery | \?BeginVMStatus | \?EndFeatureQuery | \?EndFileQuery | \?EndFontListQuery | \?EndFontQuery + | \?EndPrinterQuery | \?EndProcSetQuery | \?EndQuery | \?EndResourceListQuery | \?EndResourceQuery | \?EndVMStatus ) + contexts: main: - - include: expressions + - match: '' + set: [postscript, shebang] - expressions: + postscript: + - include: directives - include: comments + - include: arrays + - include: blocks + - include: dictionaries + - include: strings - include: numbers - include: operators - - include: dictionaries # include `dictionaries` before `strings` - - include: strings - - include: brackets - include: constants - - include: variables # include `variables` last + - include: variables comments: - match: '%' - scope: punctuation.definition.comment.ps - push: - - meta_scope: comment.line.ps - - match: \n - pop: true + scope: punctuation.definition.comment.postscript + push: comment-body - numbers: - - match: |- - (?x) - (((3[0-6]|[1-2][0-9]|[2-9])\#[[:alnum:]]+)| # radix numbers - ((((\+|-)?\d+\.?\d*)|((\+|-)?\.\d+))((e|E)(\+|-)?\d+)?))(?=[()<>\[\]{}/%\s]+) # signed integers & real numbers - scope: constant.numeric.ps + comment-body: + - meta_scope: comment.line.percentage.postscript + - match: \n + pop: true - string_escaped_char: - - match: '\\([0-7]{1,3}|[nrtbf(\\)])' - scope: constant.character.escape.c + directives: + - match: ^(%%)({{directives}})(:|{{break}}) + captures: + 1: punctuation.definition.comment.postscript + 2: keyword.control.directive.postscript + 3: punctuation.separator.key-value.postscript + push: directive-body - inside_string_literal: - - match: \( - push: - - include: inside_string_literal - - include: string_escaped_char - - match: \) - pop: true + directive-body: + - meta_scope: meta.directive.structure.postscript comment.line.percentage.postscript + - match: \n + pop: true - strings: - - match: \( - scope: punctuation.definition.string.begin.ps - push: - - meta_scope: string.literal.ps - - match: \) - scope: punctuation.definition.string.end.ps - pop: true - - include: inside_string_literal - - include: string_escaped_char - - match: '<~' - scope: punctuation.definition.string.begin.ps - push: - - meta_scope: string.ascii.ps - - match: '~>' - scope: punctuation.definition.string.end.ps - pop: true - - match: '[^[:ascii:]]' - scope: invalid.illegal.unexpected-character.ps - - match: '<' - scope: punctuation.definition.string.begin.ps - push: - - meta_scope: string.hexadecimal.ps - - match: '>' - scope: punctuation.definition.string.end.ps - pop: true - - match: '[^\h ]' - scope: invalid.illegal.unexpected-character.ps + shebang: + - meta_include_prototype: false + - match: ^\%! + scope: punctuation.definition.comment.postscript + set: shebang-body + - match: ^|(?=\S) # Note: Ensure to highlight shebang if postscript is embedded. + pop: true - variables: - - match: //? - scope: punctuation.definition.variable.ps - push: - - meta_scope: variable.other.readwrite.ps - - match: '(?=\s)' - pop: true - - match: '[^()<>\[\]{}/%\s]+' - scope: variable.other.readwrite.ps - - brackets: + shebang-body: + - meta_include_prototype: false + - meta_scope: comment.line.shebang.postscript + # Note: Keep sync with first_line_match! + - match: PS\b + scope: constant.language.shebang.postscript + - match: \n + pop: true + + arrays: - match: \[ - scope: punctuation.definition.brackets.begin.ps - push: - - meta_scope: meta.brackets.ps - - match: \] - scope: punctuation.definition.brackets.end.ps - pop: true - - include: expressions + scope: punctuation.section.sequence.begin.postscript + push: array-body + + array-body: + - meta_scope: meta.sequence.array.postscript + - match: \] + scope: punctuation.section.sequence.end.postscript + pop: true + - include: postscript + + blocks: - match: \{ - scope: punctuation.definition.brackets.begin.ps - push: - - meta_scope: meta.brackets.ps - - match: \} - scope: punctuation.definition.brackets.end.ps - pop: true - - include: expressions + scope: punctuation.section.block.begin.postscript + push: block-body + + block-body: + - meta_scope: meta.block.postscript + - match: \} + scope: punctuation.section.block.end.postscript + pop: true + - include: postscript dictionaries: - match: '<<' - scope: punctuation.definition.dictionary.begin.ps - push: - - meta_scope: meta.dictionary.ps - - match: '>>' - scope: punctuation.definition.dictionary.end.ps - pop: true - - include: expressions + scope: punctuation.section.mapping.begin.postscript + push: dictionary-body - operators: - - match: '\b(add|div|idiv|mod|mul|sub|abs|neg|ceiling|floor|round|truncate|sqrt|atan|cos|sin|exp|ln|log|rand|srand|rrand|and|not|or|xor|bitshift)\b' - scope: keyword.operator.arithmetic.ps - - match: '\b(pop|exch|dup|index|roll|clear|count|mark|cleartomark|counttomark)\b' - scope: keyword.operator.stack.ps - - match: '\b(array|astore|aload|packedarray|setpacking|currentpacking)\b' - scope: keyword.operator.array.ps - - match: '\b(dict|maxlength|begin|end|def|load|store|undef|known|where|currentdict|countdictstack|dictstack|cleardictstack)\b' - scope: keyword.operator.dictionary.ps - - match: '\b(string|anchorsearch|search)\b' - scope: keyword.operator.string.ps - - match: '\b(eq|ne|ge|gt|le|lt)\b' - scope: keyword.operator.comparison.ps - - match: '\b(exec|if|ifelse|forall|for|repeat|loop|exit|stop|stopped|countexecstack|execstack|quit|start)\b' - scope: keyword.operator.control.ps - - match: '\b(type|cvlit|cvx|xcheck|executeonly|noaccess|readonly|rcheck|wcheck|cvi|cvn|cvr|cvrs|cvs)\b' - scope: keyword.operator.type.ps - - match: '(\b(file|filter|closefile|read|write|readhexstring|writehexstring|readstring|writestring|readline|bytesavailable|flush|flushfile|resetfile|status|run|currentfile|deletefile|renamefile|filenameforall|setfileposition|fileposition|print|stack|pstack|printobject|writeobject|setobjectformat|currentobjectformat)\b|\=\=|\=)' - scope: keyword.operator.file.ps - - match: '\b(defineresource|undefineresource|findresource|findcolorrendering|resourcestatus|resourceforall)\b' - scope: keyword.operator.resource.ps - - match: '\b(save|restore|setglobal|currentglobal|gcheck|startjob|defineuserobject|execuserobject|undefineuserobject|UserObjects)\b' - scope: keyword.operator.virtual-memory.ps - - match: '\b(gsave|grestore|clipsave|cliprestore|grestoreall|initgraphics|gstate|setgstate|currentgstate|setlinewidth|currentlinewidth|setlinecap|currentlinecap|setlinejoin|currentlinejoin|setmiterlimit|currentmiterlimit|setstrokeadjust|currentstrokeadjust|setdash|currentdash|setcolorspace|currentcolorspace|setcolor|currentcolor|setgray|currentgray|sethsbcolor|currenthsbcolor|setrgbcolor|currentrgbcolor|setcmykcolor|currentcmykcolor|sethalftone|currenthalftone|setscreen|currentscreen|setcolorscreen|currentcolorscreen|settransfer|setcolortransfer|currentcolortransfer|currenttransfer|setblackgeneration|currentblackgeneration|setundercolorremoval|currentundercolorremoval|setcolorrendering|setflat|currentcolorrendering|setoverprint|currentflat|currentoverprint|setsmoothness|currentsmoothness)\b' - scope: keyword.operator.graphic-state.ps - - match: '\b(concat|concatmatrix|currentmatrix|defaultmatrix|dtransform|identmatrix|idtransform|initmatrix|invertmatrix|itransform|matrix|rotate|scale|setmatrix|transform|translate)\b' - scope: keyword.operator.matrix.ps - - match: '\b(arc|arcn|arct|arcto|charpath|clip|clippath|closepath|currentpoint|curveto|eoclip|flattenpath|initclip|lineto|moveto|newpath|pathbbox|pathforall|rcurveto|rectclip|reversepath|rlineto|rmoveto|setbbox|strokepath|uappend|ucache|upath|ustrokepath)\b' - scope: keyword.operator.path.ps - - match: '\b(colorimage|eofill|erasepage|fill|image|imagemask|rectfill|rectstroke|shfill|stroke|ueofill|ufill|ustroke)\b' - scope: keyword.operator.paint.ps - - match: '\b(infill|ineofill|inufill|inueofill|instroke|inustroke)\b' - scope: keyword.operator.insideness.ps - - match: '\b(makepattern|setpattern|execform)\b' - scope: keyword.operator.pattern.ps - - match: '\b(showpage|copypage|setpagedevice|currentpagedevice|nulldevice)\b' - scope: keyword.operator.device.ps - - match: '\b(ashow|awidthshow|composefont|cshow|currentfont|definefont|findencoding|findfont|glyphshow|ISOLatin1Encoding|kshow|makefont|rootfont|scalefont|selectfont|setcachedevice|setcachedevice2|setcharwidth|setfont|show|StandardEncoding|stringwidth|undefinefont|widthshow|xshow|xyshow|yshow)\b' - scope: keyword.operator.font.ps - - match: '\b(cachestatus|currentcacheparams|currentdevparams|currentsystemparams|currentuserparams|setcachelimit|setcacheparams|setdevparams|setsystemparams|setucacheparams|setuserparams|setvmthreshold|ucachestatus|vmreclaim|vmstatus|)\b' - scope: keyword.operator.interpreter.ps - - match: '\b(getinterval|putinterval|length|get|put|copy|bind|echo|version|realtime|usertime|languagelevel|product|revision|serialnumber|executive|prompt|token)\b' - scope: keyword.operator.ps + dictionary-body: + - meta_scope: meta.mapping.postscript + - match: '>>' + scope: punctuation.section.mapping.end.postscript + pop: true + - include: postscript + + strings: + - include: literal-strings + - include: ascii-strings + - include: hexadecimal-strings + + ascii-strings: + - match: '<~' + scope: punctuation.definition.string.begin.postscript + push: ascii-string-body + + ascii-string-body: + - meta_include_prototype: false + - meta_scope: meta.string.ascii.postscript string.other.postscript + - match: '~>' + scope: punctuation.definition.string.end.postscript + pop: true + - match: '[^[:ascii:]]+' + scope: invalid.illegal.unexpected-character.postscript + + hexadecimal-strings: + - match: '<' + scope: punctuation.definition.string.begin.postscript + push: hexadecimal-string-body + + hexadecimal-string-body: + - meta_include_prototype: false + - meta_scope: meta.string.hexadecimal.postscript string.other.postscript + - match: '>' + scope: punctuation.definition.string.end.postscript + pop: true + - match: '[^\h ]' + scope: invalid.illegal.unexpected-character.postscript + + literal-strings: + - match: \( + scope: punctuation.definition.string.begin.postscript + push: literal-string-body + + literal-string-body: + - meta_include_prototype: false + - meta_scope: meta.string.literal.postscript string.other.postscript + - match: \) + scope: punctuation.definition.string.end.postscript + pop: true + - include: string-content + + literal-string-parens-body: + - meta_include_prototype: false + - match: \) + pop: true + - include: string-content + + string-content: + - match: \( + push: literal-string-parens-body + - match: \\[0-7]{1,3} + scope: constant.character.escape.octal.postscript + - match: \\[nrtbf(\\)] + scope: constant.character.escape.postscript + + numbers: + # floating point + - match: |- + (?x) + ([-+]?) + (?: + # 1., 1.1, 1.1e1, 1.1e-1, 1.e1, 1.e-1 | 1e1, 1e-1 + \d+ (?: (\.) \d* {{exponent}}? | {{exponent}} ) + # .1, .1e1, .1e-1 + | (\.) \d+ {{exponent}}? + ) + scope: meta.number.float.decimal.postscript constant.numeric.value.postscript + captures: + 1: keyword.operator.arithmetic.postscript + 2: punctuation.separator.decimal.postscript + 3: punctuation.separator.decimal.postscript + # radix + - match: ((?:3[0-6]|[1-2][0-9]|[2-9])\#)([[:alnum:]]+){{break}} + scope: meta.number.integer.radix + captures: + 1: constant.numeric.base.postscript + 2: constant.numeric.value.postscript + # integer + - match: ([-+]?)\d+{{break}} + scope: meta.number.integer.decimal.postscript constant.numeric.value.postscript + captures: + 1: keyword.operator.arithmetic.postscript constants: - - match: '\b(true|false|null)\b' - scope: constant.language.ps - - match: '\b(\$error|errordict|FontDirectory|globaldict|GlobalFontDirectory|statusdict|systemdict|userdict)\b' - scope: constant.other.dictionary.ps + - match: (?:true|false){{break}} + scope: constant.language.boolean.postscript + - match: null\b + scope: constant.language.null.postscript + - match: (?:\$error|errordict|FontDirectory|globaldict|GlobalFontDirectory|statusdict|systemdict|userdict){{break}} + scope: constant.other.dictionary.postscript + + operators: + - match: (?:add|div|idiv|mod|mul|sub|abs|neg|ceiling|floor|round|truncate|sqrt|atan|cos|sin|exp|ln|log|rand|srand|rrand|and|not|or|xor|bitshift){{break}} + scope: keyword.operator.arithmetic.postscript + - match: (?:pop|exch|dup|index|roll|clear|count|mark|cleartomark|counttomark){{break}} + scope: keyword.operator.stack.postscript + - match: (?:array|astore|aload|packedarray|setpacking|currentpacking){{break}} + scope: keyword.operator.array.postscript + - match: (?:dict|maxlength|begin|end|def|load|store|undef|known|where|currentdict|countdictstack|dictstack|cleardictstack){{break}} + scope: keyword.operator.dictionary.postscript + - match: (?:string|anchorsearch|search){{break}} + scope: keyword.operator.string.postscript + - match: (?:eq|ne|ge|gt|le|lt){{break}} + scope: keyword.operator.comparison.postscript + - match: (?:exec|if|ifelse|forall|for|repeat|loop|exit|stop|stopped|countexecstack|execstack|quit|start){{break}} + scope: keyword.operator.control.postscript + - match: (?:type|cvlit|cvx|xcheck|executeonly|noaccess|readonly|rcheck|wcheck|cvi|cvn|cvr|cvrs|cvs){{break}} + scope: keyword.operator.type.postscript + - match: ==?|\b(?:file|filter|closefile|read|write|readhexstring|writehexstring|readstring|writestring|readline|bytesavailable|flush|flushfile|resetfile|status|run|currentfile|deletefile|renamefile|filenameforall|setfileposition|fileposition|print|stack|pstack|printobject|writeobject|setobjectformat|currentobjectformat){{break}} + scope: keyword.operator.file.postscript + - match: (?:defineresource|undefineresource|findresource|findcolorrendering|resourcestatus|resourceforall){{break}} + scope: keyword.operator.resource.postscript + - match: (?:save|restore|setglobal|currentglobal|gcheck|startjob|defineuserobject|execuserobject|undefineuserobject|UserObjects){{break}} + scope: keyword.operator.virtual-memory.postscript + - match: (?:gsave|grestore|clipsave|cliprestore|grestoreall|initgraphics|gstate|setgstate|currentgstate|setlinewidth|currentlinewidth|setlinecap|currentlinecap|setlinejoin|currentlinejoin|setmiterlimit|currentmiterlimit|setstrokeadjust|currentstrokeadjust|setdash|currentdash|setcolorspace|currentcolorspace|setcolor|currentcolor|setgray|currentgray|sethsbcolor|currenthsbcolor|setrgbcolor|currentrgbcolor|setcmykcolor|currentcmykcolor|sethalftone|currenthalftone|setscreen|currentscreen|setcolorscreen|currentcolorscreen|settransfer|setcolortransfer|currentcolortransfer|currenttransfer|setblackgeneration|currentblackgeneration|setundercolorremoval|currentundercolorremoval|setcolorrendering|setflat|currentcolorrendering|setoverprint|currentflat|currentoverprint|setsmoothness|currentsmoothness){{break}} + scope: keyword.operator.graphic-state.postscript + - match: (?:concat|concatmatrix|currentmatrix|defaultmatrix|dtransform|identmatrix|idtransform|initmatrix|invertmatrix|itransform|matrix|rotate|scale|setmatrix|transform|translate){{break}} + scope: keyword.operator.matrix.postscript + - match: (?:arc|arcn|arct|arcto|charpath|clip|clippath|closepath|currentpoint|curveto|eoclip|flattenpath|initclip|lineto|moveto|newpath|pathbbox|pathforall|rcurveto|rectclip|reversepath|rlineto|rmoveto|setbbox|strokepath|uappend|ucache|upath|ustrokepath){{break}} + scope: keyword.operator.path.postscript + - match: (?:colorimage|eofill|erasepage|fill|image|imagemask|rectfill|rectstroke|shfill|stroke|ueofill|ufill|ustroke){{break}} + scope: keyword.operator.paint.postscript + - match: (?:infill|ineofill|inufill|inueofill|instroke|inustroke){{break}} + scope: keyword.operator.insideness.postscript + - match: (?:makepattern|setpattern|execform){{break}} + scope: keyword.operator.pattern.postscript + - match: (?:showpage|copypage|setpagedevice|currentpagedevice|nulldevice){{break}} + scope: keyword.operator.device.postscript + - match: (?:ashow|awidthshow|composefont|cshow|currentfont|definefont|findencoding|findfont|glyphshow|ISOLatin1Encoding|kshow|makefont|rootfont|scalefont|selectfont|setcachedevice|setcachedevice2|setcharwidth|setfont|show|StandardEncoding|stringwidth|undefinefont|widthshow|xshow|xyshow|yshow){{break}} + scope: keyword.operator.font.postscript + - match: (?:cachestatus|currentcacheparams|currentdevparams|currentsystemparams|currentuserparams|setcachelimit|setcacheparams|setdevparams|setsystemparams|setucacheparams|setuserparams|setvmthreshold|ucachestatus|vmreclaim|vmstatus|){{break}} + scope: keyword.operator.interpreter.postscript + - match: (?:getinterval|putinterval|length|get|put|copy|bind|echo|version|realtime|usertime|languagelevel|product|revision|serialnumber|executive|prompt|token){{break}} + scope: keyword.operator.postscript + + variables: + - match: //? + scope: punctuation.definition.variable.postscript + push: variable-body + - match: '{{ident_char}}+' + scope: variable.other.readwrite.postscript + + variable-body: + - meta_scope: variable.other.readwrite.postscript + - match: (?=[\s\])}]) + pop: true diff --git a/syntax_test_ps.ps b/syntax_test_ps.ps deleted file mode 100644 index b3501cbe0769d8747052379a7a5c811d5b3cab1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3490 zcmeHK&2Hm15Wedvrkl91TbQ=&Zn6cOpbK<+Yr8;lXwj{Kkwn`>D9RA2-MB&a+;iWc zuhK{9lXOT*mc2F|rM=b%i?D{@%=gU?=^ott{KM_*pWyb*%`Lomi|HLMxgNbOw7E%3 zu{JwPrmIAXoDcOz8N7cHMLkH1Jm<>5!>BiaMB=LbqPMdltSgn83XLcf%lJYl;gs~=s;kbIP1vropT72_0ygAMZ?xfgCQ<`S zStKakveLY?izaiQI4R7Em&_VaJz}|OVriYZ4LcH?-cc)WAAaL5ql2wc|HhO;Ej8r0 zvBDS>3iO(%0wts?EHO2_)bPb;pFhrffGVQ^!%d_e8cX<6sDn;w;-M$hhEfnZ%80~5 zmGTVMSW@$@xaJ!A{lNhGrsLfQld^?r?Qt9+Y(0ujPS=+)~+)LYx0 zdBUx$2TW4k%zUH)I};sr2~2O8Y9oYzFP^39jkv2p4K=xK|?0NT8>$w$`q$ z{cWZ@VY~EQ!PS+o);qrWP?Q-f)^yh~MadRI+QuZTW~h6yEC;uakISw=iQnon%@#K?mq`*J*%c(W2=9ltkM^WXct(Ox(b;?z%VV zReF@3q>rRzQIh3IZU)`di-?il@Av*l_Tc)bcQ6w{UJ_562w9!apYj&;MD{DD@&JbMH8yxgrJDwJ&jTq zEYAZNPflU@)ob`-e2yS%WvE0dDijgvk|jLA*G6#jr^e4He@I57iPl2S*mwkEGI~3F z1;a6c5gA#H`vm7n)MS-0&Hk~ZEeJE7J-#|po#jpjbn*2OX6V_`cW^p68vphrij4>x zRmO|D6Q~BWoxzMol7UF0D8sHGji{KXx`zBXju;iCM7C~Jgty=*_;x7LX1H3=NV%4Z zEkhnCm`AZsBeP5;OY|a-cg2oaEm(p{jb$ARnyg)!HuW8pBM25)VS!?agwH>Fxq06MDnfJ=Tcqh2*rkvc8y%8R7cR)Y zSk*-#H?@b1+-GxMj&1Hu`{#j{c^FSB0u};x6>QiGCYgkOe=vYPIt?<0SPaPEiRQnx z^H4KNIpKu0yexM@XGCQ{G7-9o=Jjs{+@7AF!|edjh5smk2V>%P(QQr=CIh-E^?VJU zjzE$7HR?D}rA$RIvfo~|B83YEt?Y?}uEt@yU=Npd{P8UfvE-H|)1I}6O4xVPr^h-M zIAD25coeZYTA2#=fK5WNaJYq7Bh#L;Gj;&ek5mSK{mDOS2zOT+5^(OMeiMd9z!Sl% zcO#oA9egwlm{<)P%7$vCegHqrW|Ap5K8DHZ8yKJd{rewZzB&U+d^rAo{jDBC->j&@ z#w&D7f_s{9>PL*&mYXmden_w{>!4&Mj;%hHvd}?Le6^ zPpB?dRwU?u^R#RheNQ%Gf7?dWJJtV<}fywbUJsG+$Cr|dlMXAACR0@K-ObP0WIK!aZ_ z%63D?eoYzff(T6#>h4ub9Zc#&DjtnxPF?Smc^QG;S9i}Om z_s$YCC@X8{rJF_tm-Dtr>AZeB=-XnYhg>0ETe7n=S<&^R&E|7si`ixGJ`I)N6v6Gl zTj`Rz0j8F)14GvLI3P?=OPE1cN)(^-pE(uO9$}A{BV(Jx_Cgo)2rs@_~ z=9MH?C~>KR46xBvNY2mAP0cIO%gM}3)ho_OO-js5*DC-TQ4BUhp`bJ`xui6)Br`ux rFC{fCGcOa&glR*l