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 b3501cb..0000000 Binary files a/syntax_test_ps.ps and /dev/null differ diff --git a/tests/syntax_test_ps.ps b/tests/syntax_test_ps.ps new file mode 100644 index 0000000..a9040ff Binary files /dev/null and b/tests/syntax_test_ps.ps differ diff --git a/tests/syntax_test_shebang.ps b/tests/syntax_test_shebang.ps new file mode 100644 index 0000000..710594c Binary files /dev/null and b/tests/syntax_test_shebang.ps differ