# -*- 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