From 714d4ffc33ef5e45215e8d3a033164b02f4fc98a Mon Sep 17 00:00:00 2001 From: Saagar Jha Date: Mon, 16 Dec 2019 23:35:51 -0800 Subject: [PATCH] Implement an Objective-C++ lexer In addition, factor out the common Objective-C bits into a shared module --- lib/rouge/lexers/objective_c.rb | 175 +---------------------- lib/rouge/lexers/objective_c/common.rb | 184 +++++++++++++++++++++++++ lib/rouge/lexers/objective_cpp.rb | 22 +++ 3 files changed, 209 insertions(+), 172 deletions(-) create mode 100644 lib/rouge/lexers/objective_c/common.rb create mode 100644 lib/rouge/lexers/objective_cpp.rb diff --git a/lib/rouge/lexers/objective_c.rb b/lib/rouge/lexers/objective_c.rb index b3a17bb764..46e75ab835 100644 --- a/lib/rouge/lexers/objective_c.rb +++ b/lib/rouge/lexers/objective_c.rb @@ -4,8 +4,11 @@ module Rouge module Lexers load_lexer 'c.rb' + load_lexer 'objective_c/common.rb' class ObjectiveC < C + include ObjectiveCCommon + tag 'objective_c' title "Objective-C" desc 'an extension of C commonly used to write Apple software' @@ -13,178 +16,6 @@ class ObjectiveC < C filenames '*.m', '*.h' mimetypes 'text/x-objective_c', 'application/x-objective_c' - - def self.at_keywords - @at_keywords ||= %w( - selector private protected public encode synchronized try - throw catch finally end property synthesize dynamic optional - interface implementation import - ) - end - - def self.at_builtins - @at_builtins ||= %w(true false YES NO) - end - - def self.builtins - @builtins ||= %w(YES NO nil) - end - - id = /[a-z$_][a-z0-9$_]*/i - - prepend :statements do - rule %r/@"/, Str, :string - rule %r/@'(\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|\\.|[^\\'\n]')/, - Str::Char - rule %r/@(\d+[.]\d*|[.]\d+|\d+)e[+-]?\d+l?/i, - Num::Float - rule %r/@(\d+[.]\d*|[.]\d+|\d+f)f?/i, Num::Float - rule %r/@0x\h+[lL]?/, Num::Hex - rule %r/@0[0-7]+l?/i, Num::Oct - rule %r/@\d+l?/, Num::Integer - rule %r/\bin\b/, Keyword - - rule %r/@(?:interface|implementation)\b/, Keyword, :classname - rule %r/@(?:class|protocol)\b/, Keyword, :forward_classname - - rule %r/@([[:alnum:]]+)/ do |m| - if self.class.at_keywords.include? m[1] - token Keyword - elsif self.class.at_builtins.include? m[1] - token Name::Builtin - else - token Error - end - end - - rule %r/[?]/, Punctuation, :ternary - rule %r/\[/, Punctuation, :message - rule %r/@\[/, Punctuation, :array_literal - rule %r/@\{/, Punctuation, :dictionary_literal - end - - state :ternary do - rule %r/:/, Punctuation, :pop! - mixin :statements - end - - state :message_shared do - rule %r/\]/, Punctuation, :pop! - rule %r/\{/, Punctuation, :pop! - rule %r/;/, Error - - mixin :statements - end - - state :message do - rule %r/(#{id})(\s*)(:)/ do - groups(Name::Function, Text, Punctuation) - goto :message_with_args - end - - rule %r/(#{id})(\s*)(\])/ do - groups(Name::Function, Text, Punctuation) - pop! - end - - mixin :message_shared - end - - state :message_with_args do - rule %r/\{/, Punctuation, :function - rule %r/(#{id})(\s*)(:)/ do - groups(Name::Function, Text, Punctuation) - pop! - end - - mixin :message_shared - end - - state :array_literal do - rule %r/]/, Punctuation, :pop! - rule %r/,/, Punctuation - mixin :statements - end - - state :dictionary_literal do - rule %r/}/, Punctuation, :pop! - rule %r/,/, Punctuation - mixin :statements - end - - state :classname do - mixin :whitespace - - rule %r/(#{id})(\s*)(:)(\s*)(#{id})/ do - groups(Name::Class, Text, - Punctuation, Text, - Name::Class) - pop! - end - - rule %r/(#{id})(\s*)([(])(\s*)(#{id})(\s*)([)])/ do - groups(Name::Class, Text, - Punctuation, Text, - Name::Label, Text, - Punctuation) - pop! - end - - rule id, Name::Class, :pop! - end - - state :forward_classname do - mixin :whitespace - - rule %r/(#{id})(\s*)(,)(\s*)/ do - groups(Name::Class, Text, Punctuation, Text) - push - end - - rule %r/(#{id})(\s*)(;?)/ do - groups(Name::Class, Text, Punctuation) - pop! - end - end - - prepend :root do - rule %r( - ([-+])(\s*) - ([(].*?[)])?(\s*) - (?=#{id}:?) - )ix do |m| - token Keyword, m[1] - token Text, m[2] - recurse(m[3]) if m[3] - token Text, m[4] - push :method_definition - end - end - - state :method_definition do - rule %r/,/, Punctuation - rule %r/[.][.][.]/, Punctuation - rule %r/([(].*?[)])(#{id})/ do |m| - recurse m[1]; token Name::Variable, m[2] - end - - rule %r/(#{id})(\s*)(:)/m do - groups(Name::Function, Text, Punctuation) - end - - rule %r/;/, Punctuation, :pop! - - rule %r/{/ do - token Punctuation - goto :function - end - - mixin :inline_whitespace - rule %r(//.*?\n), Comment::Single - rule %r/\s+/m, Text - - rule(//) { pop! } - end end end end diff --git a/lib/rouge/lexers/objective_c/common.rb b/lib/rouge/lexers/objective_c/common.rb new file mode 100644 index 0000000000..b1cb15c99e --- /dev/null +++ b/lib/rouge/lexers/objective_c/common.rb @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +module Rouge + module Lexers + module ObjectiveCCommon + def self.at_keywords + @at_keywords ||= %w( + selector private protected public encode synchronized try + throw catch finally end property synthesize dynamic optional + interface implementation import + ) + end + + def self.at_builtins + @at_builtins ||= %w(true false YES NO) + end + + def self.builtins + @builtins ||= %w(YES NO nil) + end + + id = /[a-z$_][a-z0-9$_]*/i + + def self.included(base) + base.prepend :statements do + rule %r/@"/, base::Str, :string + rule %r/@'(\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|\\.|[^\\'\n]')/, + base::Str::Char + rule %r/@(\d+[.]\d*|[.]\d+|\d+)e[+-]?\d+l?/i, + base::Num::Float + rule %r/@(\d+[.]\d*|[.]\d+|\d+f)f?/i, base::Num::Float + rule %r/@0x\h+[lL]?/, base::Num::Hex + rule %r/@0[0-7]+l?/i, base::Num::Oct + rule %r/@\d+l?/, base::Num::Integer + rule %r/\bin\b/, base::Keyword + + rule %r/@(?:interface|implementation)\b/, base::Keyword, :classname + rule %r/@(?:class|protocol)\b/, base::Keyword, :forward_classname + + rule %r/@([[:alnum:]]+)/ do |m| + if ObjectiveCCommon::at_keywords.include? m[1] + token base::Keyword + elsif ObjectiveCCommon::at_builtins.include? m[1] + token base::Name::Builtin + else + token Error + end + end + + rule %r/[?]/, base::Punctuation, :ternary + rule %r/\[/, base::Punctuation, :message + rule %r/@\[/, base::Punctuation, :array_literal + rule %r/@\{/, base::Punctuation, :dictionary_literal + end + + id = /[a-z$_][a-z0-9$_]*/i + + base.state :ternary do + rule %r/:/, base::Punctuation, :pop! + mixin :statements + end + + base.state :message_shared do + rule %r/\]/, base::Punctuation, :pop! + rule %r/\{/, base::Punctuation, :pop! + rule %r/;/, base::Error + + mixin :statements + end + + base.state :message do + rule %r/(#{id})(\s*)(:)/ do + groups(base::Name::Function, base::Text, base::Punctuation) + goto :message_with_args + end + + rule %r/(#{id})(\s*)(\])/ do + groups(base::Name::Function, base::Text, base::Punctuation) + pop! + end + + mixin :message_shared + end + + base.state :message_with_args do + rule %r/\{/, base::Punctuation, :function + rule %r/(#{id})(\s*)(:)/ do + groups(base::Name::Function, base::Text, base::Punctuation) + pop! + end + + mixin :message_shared + end + + base.state :array_literal do + rule %r/]/, base::Punctuation, :pop! + rule %r/,/, base::Punctuation + mixin :statements + end + + base.state :dictionary_literal do + rule %r/}/, base::Punctuation, :pop! + rule %r/,/, base::Punctuation + mixin :statements + end + + base.state :classname do + mixin :whitespace + + rule %r/(#{id})(\s*)(:)(\s*)(#{id})/ do + groups(base::Name::Class, base::Text, + base::Punctuation, base::Text, + base::Name::Class) + pop! + end + + rule %r/(#{id})(\s*)([(])(\s*)(#{id})(\s*)([)])/ do + groups(base::Name::Class, base::Text, + base::Punctuation, base::Text, + base::Name::Label, base::Text, + base::Punctuation) + pop! + end + + rule id, base::Name::Class, :pop! + end + + base.state :forward_classname do + mixin :whitespace + + rule %r/(#{id})(\s*)(,)(\s*)/ do + groups(base::Name::Class, base::Text, base::Punctuation, base::Text) + push + end + + rule %r/(#{id})(\s*)(;?)/ do + groups(base::Name::Class, base::Text, base::Punctuation) + pop! + end + end + + base.prepend :root do + rule %r( + ([-+])(\s*) + ([(].*?[)])?(\s*) + (?=#{id}:?) + )ix do |m| + token base::Keyword, m[1] + token base::Text, m[2] + recurse(m[3]) if m[3] + token base::Text, m[4] + push :method_definition + end + end + + base.state :method_definition do + rule %r/,/, base::Punctuation + rule %r/[.][.][.]/, base::Punctuation + rule %r/([(].*?[)])(#{id})/ do |m| + recurse m[1]; token base::Name::Variable, m[2] + end + + rule %r/(#{id})(\s*)(:)/m do + groups(base::Name::Function, base::Text, base::Punctuation) + end + + rule %r/;/, base::Punctuation, :pop! + + rule %r/{/ do + token base::Punctuation + goto :function + end + + mixin :inline_whitespace + rule %r(//.*?\n), base::Comment::Single + rule %r/\s+/m, base::Text + + rule(//) { pop! } + end + end + end + end +end diff --git a/lib/rouge/lexers/objective_cpp.rb b/lib/rouge/lexers/objective_cpp.rb new file mode 100644 index 0000000000..31e69fc9db --- /dev/null +++ b/lib/rouge/lexers/objective_cpp.rb @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +module Rouge + module Lexers + load_lexer 'cpp.rb' + load_lexer 'objective_c/common.rb' + + class ObjectiveCpp < Cpp + include ObjectiveCCommon + + tag 'objective_cpp' + title "Objective-C++" + desc 'an extension of C++ uncommonly used to write Apple software' + aliases 'objcpp', 'obj-cpp', 'obj_cpp', 'objectivecpp', + 'objc++', 'obj-c++', 'obj_c++', 'objectivec++' + filenames '*.mm', '*.h' + + mimetypes 'text/x-objective-c++', 'application/x-objective-c' + end + end +end