Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostScript lexer #1578

Merged
merged 11 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/rouge/demos/postscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
%!PS
/Courier % name the desired font
20 selectfont % choose the size in points and establish
% the font as the current one
72 500 moveto % position the current point at
% coordinates 72, 500 (the origin is at the
% lower-left corner of the page)
(Hello world!) show % stroke the text in parentheses
showpage % print all on the page
93 changes: 93 additions & 0 deletions lib/rouge/lexers/postscript.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

# Adapted from pygments PostScriptLexer
module Rouge
module Lexers
class PostScript < RegexLexer
title "PostScript"
desc "The PostScript language (adobe.com/devnet/postscript.html)"
tag "postscript"
aliases "postscr", "postscript", "ps", "eps"
filenames "*.ps", "*.eps"
mimetypes "application/postscript"

def self.detect?(text)
return true if /^%!/ =~ text
end

delimiter = %s"()<>\[\]{}/%\s"
delimiter_end = Regexp.new("(?=[#{delimiter}])")
valid_name_chars = Regexp.new("[^#{delimiter}]")
valid_name = /#{valid_name_chars}+#{delimiter_end}/

# These keywords taken from
# <http://www.math.ubc.ca/~cass/graphics/manual/pdf/a1.pdf>
# Is there an authoritative list anywhere that doesn't involve
# trawling documentation?
keywords = %w/abs add aload arc arcn array atan begin
bind ceiling charpath clip closepath concat
concatmatrix copy cos currentlinewidth currentmatrix
currentpoint curveto cvi cvs def defaultmatrix
dict dictstackoverflow div dtransform dup end
exch exec exit exp fill findfont floor get
getinterval grestore gsave identmatrix idiv
idtransform index invertmatrix itransform length
lineto ln load log loop matrix mod moveto
mul neg newpath pathforall pathbbox pop print
pstack put quit rand rangecheck rcurveto repeat
restore rlineto rmoveto roll rotate round run
save scale scalefont setdash setfont setgray
setlinecap setlinejoin setlinewidth setmatrix
setrgbcolor shfill show showpage sin sqrt
stack stringwidth stroke strokepath sub syntaxerror
transform translate truncate typecheck undefined
undefinedfilename undefinedresult/

state :root do
# All comment types
rule %r'^%!.+?$', Comment::Preproc
rule %r'%%.*?$', Comment::Special
rule %r'(^%.*?$){2,}', Comment::Multiline
rule %r'%.*?$', Comment::Single

# String literals are awkward; enter separate state.
rule %r'\(', Str, :stringliteral

# References
rule %r'/#{valid_name}', Name::Variable

rule %r'[{}<>\[\]]', Punctuation

rule %r'(?:#{keywords.join('|')})#{delimiter_end}', Name::Builtin

# Conditionals / flow control
rule %r'(eq|ne|g[et]|l[et]|and|or|not|if(?:else)?|for(?:all)?)#{delimiter_end}', Keyword::Reserved
rule %r'(false|true)#{delimiter_end}', Keyword::Constant

# Numbers
rule %r'<[0-9A-Fa-f]+>#{delimiter_end}', Num::Hex
# Slight abuse: use Oct to signify any explicit base system
rule %r'[0-9]+\#(\-|\+)?([0-9]+\.?|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)((e|E)[0-9]+)?#{delimiter_end}', Num::Oct
rule %r'(\-|\+)?([0-9]+\.?|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)((e|E)[0-9]+)?#{delimiter_end}', Num::Float
rule %r'(\-|\+)?[0-9]+#{delimiter_end}', Num::Integer

# Names
rule valid_name, Name::Function # Anything else is executed

rule %r'\s+', Text
end

state :stringliteral do
rule %r'[^()\\]+', Str
rule %r'\\', Str::Escape, :escape
rule %r'\(', Str, :stringliteral
rule %r'\)', Str, :pop!
end

state :escape do
rule %r'[0-8]{3}|n|r|t|b|f|\\|\(|\)', Str::Escape, :pop!
end
end
end
end
24 changes: 24 additions & 0 deletions spec/lexers/postscript_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

describe Rouge::Lexers::PostScript do
let(:subject) { Rouge::Lexers::PostScript.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.ps'
assert_guess :filename => 'foo.eps'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'application/postscript'
end

it 'guesses by source' do
assert_guess :source => '%!PS'
assert_guess :source => '%!PS-Adobe-3.0'
end
end
end
107 changes: 107 additions & 0 deletions spec/visual/samples/postscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
%!PS-Adobe-3.0
%%BoundingBox: 38 24 574 768
%%Title: Enscript Output
%
% Procedures.
%

/_S { % save current state
/_s save def
} def
/_R { % restore from saved state
_s restore
} def

/S { % showpage protecting gstate
gsave
showpage
grestore
} bind def

/MF { % fontname newfontname -> - make a new encoded font
/newfontname exch def
/fontname exch def

/fontdict fontname findfont def
/newfont fontdict maxlength dict def

fontdict {
exch
dup /FID eq {
% skip FID pair
pop pop
} {
% copy to the new font dictionary
exch newfont 3 1 roll put
} ifelse
} forall

newfont /FontName newfontname put

% insert only valid encoding vectors
encoding_vector length 256 eq {
newfont /Encoding encoding_vector put
} if

newfontname newfont definefont pop
} def

% Column separator lines.
/column_lines {
gsave
.1 setlinewidth
0 d_footer_h translate
/cw d_output_w cols div def
1 1 cols 1 sub {
cw mul 0 moveto
0 d_output_h rlineto stroke
} for
grestore
} def

% Page prefeed
/page_prefeed { % bool -> -
statusdict /prefeed known {
statusdict exch /prefeed exch put
} {
pop
} ifelse
} def

%%BeginResource: procset Enscript-Encoding-88591 1.6 6
/encoding_vector [
/udieresis /yacute /thorn /ydieresis
] def
%%EndResource
%%EndProlog
%%BeginSetup
% Pagedevice definitions:
gs_languagelevel 1 gt {
<<
/PageSize [612 792]
>> setpagedevice
} if

%%EndSetup
%%Page: (1) 1
%%BeginPageSetup
_S
38 24 translate
/pagenum 1 def
/fname (t.txt) def
/fdir (.) def
/ftail (t.txt) def
/fmodstr (ti. feb. 19 15:28:20 2019) def
/pagenumstr (1) def
/user_header_p false def
/user_footer_p false def
%%EndPageSetup
do_header
5 687 M
(text]\(https://example.com\)\)) s
5 588 M
_R
S
%%Trailer
%%Pages: 1
%%EOF