summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/rouge-4.7.0/lib/rouge/lexers/cython.rb
blob: 945cbc198dab75db163cf74c171754b82da47e4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
  module Lexers
    load_lexer 'python.rb'

    class Cython < Python
      title "Cython"
      desc "Cython and Pyrex source code (cython.org)"
      tag 'cython'
      aliases 'pyx', 'pyrex'
      filenames '*.pyx', '*.pxd', '*.pxi'
      mimetypes 'text/x-cython', 'application/x-cython'

      def initialize(opts = {})
        super opts
        @indentation = nil
      end

      def self.keywords
        @keywords ||= super + %w(
          by except? fused gil nogil
        )
      end

      def self.c_keywords
        @ckeywords ||= %w(
          public readonly extern api inline enum union
        )
      end

      identifier = /[a-z_]\w*/i
      dotted_identifier = /[a-z_.][\w.]*/i

      prepend :root do
        rule %r/cp?def|ctypedef/ do
          token Keyword
          push :c_definitions
          push :c_start
        end

        rule %r/(from)((?:\\\s|\s)+)(#{dotted_identifier})((?:\\\s|\s)+)(cimport)/ do
          groups Keyword::Namespace,
                 Text,
                 Name::Namespace,
                 Text,
                 Keyword::Namespace
        end

        rule %r/(cimport)(\s+)(#{dotted_identifier})/ do
          groups Keyword::Namespace, Text, Name::Namespace
        end

        rule %r/(struct)((?:\\\s|\s)+)/ do
          groups Keyword, Text
          push :classname
        end

        mixin :func_call_fix

        rule %r/[(,]/, Punctuation, :c_start
      end

      prepend :classname do
        rule %r/(?:\\\s|\s)+/, Text
      end

      prepend :funcname do
        rule %r/(?:\\\s|\s)+/, Text
      end
      # This is a fix for the way that function calls are lexed in the Python
      # lexer. This should be moved to the Python lexer once confirmed that it
      # does not cause any regressions.
      state :func_call_fix do
        rule %r/#{identifier}(?=\()/ do |m|
          if self.class.keywords.include? m[0]
            token Keyword
          elsif self.class.exceptions.include? m[0]
            token Name::Builtin
          elsif self.class.builtins.include? m[0]
            token Name::Builtin
          elsif self.class.builtins_pseudo.include? m[0]
            token Name::Builtin::Pseudo
          else
            token Name::Function
          end
        end
      end

      # The Cython lexer adds three states to those already in the Python lexer.
      # Calls to `cdef`, `cpdef` and `ctypedef` move the lexer into the :c_start
      # state. The primary purpose of this state is to highlight datatypes. Once
      # this has been done, the lexer moves to the :c_definitions state where
      # the majority of text in a definition is lexed. Finally, newlines cause
      # the lexer to move to :c_indent. This state is used to check whether we
      # have moved out of a C block.

      state :c_start do
        rule %r/[^\S\n]+/, Text

        rule %r/cp?def|ctypedef/, Keyword

        rule %r/(?:un)?signed/, Keyword::Type

        # This rule matches identifiers that could be type declarations. The
        # lookahead matches (1) pointers, (2) arrays and (3) variable names.
        rule %r/#{identifier}(?=(?:\*+)|(?:[ \t]*\[)|(?:[ \t]+\w))/ do |m|
          if self.class.keywords.include? m[0]
            token Keyword
            pop!
          elsif %w(def).include? m[0]
            token Keyword
            goto :funcname
          elsif %w(struct class).include? m[0]
            token Keyword::Reserved
            goto :classname
          elsif self.class.c_keywords.include? m[0]
            token Keyword::Reserved
          else
            token Keyword::Type
            pop!
          end
        end

        rule(//) { pop! }
      end

      state :c_definitions do
        rule %r/\n/, Text, :c_indent
        mixin :root
      end

      state :c_indent do
        rule %r/[ \t]+/ do |m|
          token Text
          goto :c_start

          if @indentation.nil?
            @indentation = m[0]
          elsif @indentation.length > m[0].length
            @indentation = nil
            pop! 2 # Pop :c_start and :c_definitions
          end
        end

        rule(//) { @indentation = nil; reset_stack }
      end
    end
  end
end