module Rouge
module Lexers
class Sed < RegexLexer
desc 'sed, the ultimate stream editor'
tag 'sed'
filenames '*.sed'
mimetypes 'text/x-sed'
def self.analyze_text(text)
return 1 if text.shebang? 'sed'
end
class Regex < RegexLexer
state :root do
rule /\\./, Str::Escape
rule /\[/, Punctuation, :brackets
rule /[$^.*]/, Operator
rule /[()]/, Punctuation
rule /./, Str::Regex
end
state :brackets do
rule /\^?/ do
token Punctuation
goto :brackets_int
end
end
state :brackets_int do
# ranges
rule /.-./, Name::Variable
rule /\]/, Punctuation, :pop!
rule /./, Str::Regex
end
end
class Replacement < RegexLexer
state :root do
rule /\\./m, Str::Escape
rule /&/, Operator
rule /[^\\&]+/m, Text
end
end
def regex
@regex ||= Regex.new(options)
end
def replacement
@replacement ||= Replacement.new(options)
end
start { regex.reset!; replacement.reset! }
state :whitespace do
rule /\s+/m, Text
rule(/#.*?\n/) { token Comment; reset_stack }
rule(/\n/) { token Text; reset_stack }
rule(/;/) { token Punctuation; reset_stack }
end
state :root do
mixin :addr_range
end
edot = /\\.|./m
state :command do
mixin :whitespace
# subst and transliteration
rule /(s)(.)(#{edot}*?)(\2)(#{edot}*?)(\2)/m do |m|
token Keyword, m[1]
token Punctuation, m[2]
delegate regex, m[3]
token Punctuation, m[4]
delegate replacement, m[5]
token Punctuation, m[6]
goto :flags
end
rule /(y)(.)(#{edot}*?)(\2)(#{edot}*?)(\2)/m do |m|
token Keyword, m[1]
token Punctuation, m[2]
delegate replacement, m[3]
token Punctuation, m[4]
delegate replacement, m[5]
token Punctuation, m[6]
pop!
end
# commands that take a text segment as an argument
rule /([aic])(\s*)/ do
groups Keyword, Text; goto :text
end
rule /[pd]/, Keyword
# commands that take a number argument
rule /([qQl])(\s+)(\d+)/i do
groups Keyword, Text, Num
pop!
end
# no-argument commands
rule /[={}dDgGhHlnpPqx]/, Keyword, :pop!
# commands that take a filename argument
rule /([rRwW])(\s+)(\S+)/ do
groups Keyword, Text, Name
pop!
end
# commands that take a label argument
rule /([:btT])(\s+)(\S+)/ do
groups Keyword, Text, Name::Label
pop!
end
end
state :addr_range do
mixin :whitespace
### address ranges ###
addr_tok = Keyword::Namespace
rule /\d+/, addr_tok
rule /[$,~+!]/, addr_tok
rule %r((/)(\\.|.)*?(/)) do |m|
token addr_tok, m[1]; delegate regex, m[2]; token addr_tok, m[3]
end
# alternate regex rage delimiters
rule %r((\\)(.)(\\.|.)*?(\2)) do |m|
token addr_tok, m[1] + m[2]
delegate regex, m[3]
token addr_tok, m[4]
end
rule(//) { push :command }
end
state :text do
rule /[^\\\n]+/, Str
rule /\\\n/, Str::Escape
rule /\\/, Str
rule /\n/, Text, :pop!
end
state :flags do
rule /[gp]+/, Keyword, :pop!
# writing to a file with the subst command.
# who'da thunk...?
rule /([wW])(\s+)(\S+)/ do
token Keyword; token Text; token Name
end
rule(//) { pop! }
end
end
end
end