163 lines
5.4 KiB
Ruby
163 lines
5.4 KiB
Ruby
# -*- coding: utf-8 -*- #
|
||
# frozen_string_literal: true
|
||
|
||
module Rouge
|
||
module Lexers
|
||
class Ada < RegexLexer
|
||
tag 'ada'
|
||
filenames '*.ada', '*.ads', '*.adb', '*.gpr'
|
||
mimetypes 'text/x-ada'
|
||
|
||
title 'Ada'
|
||
desc 'The Ada 2012 programming language'
|
||
|
||
# Ada identifiers are Unicode with underscores only allowed as separators.
|
||
ID = /\b[[:alpha:]](?:\p{Pc}?[[:alnum:]])*\b/
|
||
|
||
# Numerals can also contain underscores.
|
||
NUM = /\d(_?\d)*/
|
||
XNUM = /\h(_?\h)*/
|
||
EXP = /(E[-+]?#{NUM})?/i
|
||
|
||
# Return a hash mapping lower-case identifiers to token classes.
|
||
def self.idents
|
||
@idents ||= Hash.new(Name).tap do |h|
|
||
%w(
|
||
abort abstract accept access aliased all array at begin body
|
||
case constant declare delay delta digits do else elsif end
|
||
exception exit for generic goto if in interface is limited
|
||
loop new null of others out overriding pragma private
|
||
protected raise range record renames requeue return reverse
|
||
select separate some synchronized tagged task terminate then
|
||
until use when while with
|
||
).each {|w| h[w] = Keyword}
|
||
|
||
%w(abs and mod not or rem xor).each {|w| h[w] = Operator::Word}
|
||
|
||
%w(
|
||
entry function package procedure subtype type
|
||
).each {|w| h[w] = Keyword::Declaration}
|
||
|
||
%w(
|
||
boolean character constraint_error duration float integer
|
||
natural positive long_float long_integer long_long_float
|
||
long_long_integer program_error short_float short_integer
|
||
short_short_integer storage_error string tasking_error
|
||
wide_character wide_string wide_wide_character
|
||
wide_wide_string
|
||
).each {|w| h[w] = Name::Builtin}
|
||
end
|
||
end
|
||
|
||
state :whitespace do
|
||
rule %r{\s+}m, Text
|
||
rule %r{--.*$}, Comment::Single
|
||
end
|
||
|
||
state :dquote_string do
|
||
rule %r{[^"\n]+}, Literal::String::Double
|
||
rule %r{""}, Literal::String::Escape
|
||
rule %r{"}, Literal::String::Double, :pop!
|
||
rule %r{\n}, Error, :pop!
|
||
end
|
||
|
||
state :attr do
|
||
mixin :whitespace
|
||
rule ID, Name::Attribute, :pop!
|
||
rule %r{}, Text, :pop!
|
||
end
|
||
|
||
# Handle a dotted name immediately following a declaration keyword.
|
||
state :decl_name do
|
||
mixin :whitespace
|
||
rule %r{body\b}i, Keyword::Declaration # package body Foo.Bar is...
|
||
rule %r{(#{ID})(\.)} do
|
||
groups Name::Namespace, Punctuation
|
||
end
|
||
# function "<=" (Left, Right: Type) is ...
|
||
rule %r{#{ID}|"(and|or|xor|/?=|<=?|>=?|\+|–|&\|/|mod|rem|\*?\*|abs|not)"},
|
||
Name::Function, :pop!
|
||
rule %r{}, Text, :pop!
|
||
end
|
||
|
||
# Handle a sequence of library unit names: with Ada.Foo, Ada.Bar;
|
||
#
|
||
# There's a chance we entered this state mistakenly since 'with'
|
||
# has multiple other uses in Ada (none of which are likely to
|
||
# appear at the beginning of a line). Try to bail as soon as
|
||
# possible if we see something suspicious like keywords.
|
||
#
|
||
# See ada_spec.rb for some examples.
|
||
state :libunit_name do
|
||
mixin :whitespace
|
||
|
||
rule ID do |m|
|
||
t = self.class.idents[m[0].downcase]
|
||
if t <= Name
|
||
# Convert all kinds of Name to namespaces in this context.
|
||
token Name::Namespace
|
||
else
|
||
# Yikes, we're not supposed to get a keyword in a library unit name!
|
||
# We probably entered this state by mistake, so try to fix it.
|
||
token t
|
||
if t == Keyword::Declaration
|
||
goto :decl_name
|
||
else
|
||
pop!
|
||
end
|
||
end
|
||
end
|
||
|
||
rule %r{[.,]}, Punctuation
|
||
rule %r{}, Text, :pop!
|
||
end
|
||
|
||
state :root do
|
||
mixin :whitespace
|
||
|
||
# String literals.
|
||
rule %r{'.'}, Literal::String::Char
|
||
rule %r{"[^"\n]*}, Literal::String::Double, :dquote_string
|
||
|
||
# Real literals.
|
||
rule %r{#{NUM}\.#{NUM}#{EXP}}, Literal::Number::Float
|
||
rule %r{#{NUM}##{XNUM}\.#{XNUM}##{EXP}}, Literal::Number::Float
|
||
|
||
# Integer literals.
|
||
rule %r{2#[01](_?[01])*##{EXP}}, Literal::Number::Bin
|
||
rule %r{8#[0-7](_?[0-7])*##{EXP}}, Literal::Number::Oct
|
||
rule %r{16##{XNUM}*##{EXP}}, Literal::Number::Hex
|
||
rule %r{#{NUM}##{XNUM}##{EXP}}, Literal::Number::Integer
|
||
rule %r{#{NUM}#\w+#}, Error
|
||
rule %r{#{NUM}#{EXP}}, Literal::Number::Integer
|
||
|
||
# Special constructs.
|
||
rule %r{'}, Punctuation, :attr
|
||
rule %r{<<#{ID}>>}, Name::Label
|
||
|
||
# Context clauses are tricky because the 'with' keyword is used
|
||
# for many purposes. Detect at beginning of the line only.
|
||
rule %r{^(?:(limited)(\s+))?(?:(private)(\s+))?(with)\b}i do
|
||
groups Keyword::Namespace, Text, Keyword::Namespace, Text, Keyword::Namespace
|
||
push :libunit_name
|
||
end
|
||
|
||
# Operators and punctuation characters.
|
||
rule %r{[+*/&<=>|]|-|=>|\.\.|\*\*|[:></]=|<<|>>|<>}, Operator
|
||
rule %r{[.,:;()]}, Punctuation
|
||
|
||
rule ID do |m|
|
||
t = self.class.idents[m[0].downcase]
|
||
token t
|
||
if t == Keyword::Declaration
|
||
push :decl_name
|
||
end
|
||
end
|
||
|
||
# Flag word-like things that don't match the ID pattern.
|
||
rule %r{\b(\p{Pc}|[[alpha]])\p{Word}*}, Error
|
||
end
|
||
end
|
||
end
|
||
end
|