Initial Revision
[ohcount] / ext / ohcount_native / transition.rb
1 module Ohcount
2         # Defines a transition between one parser State and another.
3         #
4         # A state transition is triggered when a regular expression or "token" match
5         # is found in the source code.
6         #
7         # The Transition class handles transitions between states in a single language. To
8         # define transitions between two different languages, use the StitchTransition class.
9         class Transition
10                 # This transition defines state changes within this language only.
11                 attr_reader :language
12
13                 # A regular expression which triggers the state transition when it is matched.
14                 attr_reader :token
15
16                 # This transition can only be followed when we are in the 'from' state.
17                 attr_reader :from
18
19                 # The state we will transition to when the token is matched.
20                 attr_reader :to
21
22                 # One of :from, :to, or :neither.
23                 #
24                 # This determines which state 'owns' the text of the token.
25                 #
26                 # For instance, a double-quote character in the source code might trigger
27                 # a transition from a code state to a string state.
28                 # The who_eats value dictates whether the double-quote character itself
29                 # should be considered part of the code or part of the string.
30                 attr_reader :who_eats
31
32                 # Set to true if this transition should be ignored -- that is, continue
33                 # on without changing state, and do not notify any listeners that a state
34                 # transition occured.
35                 #
36                 # Why would we ever want this?
37                 # One example use is handling escaped string chars in the CMonoglot.
38                 # When we are in the string state, we want to ignore all escaped double-quotes (\")
39                 # since these should not return us to the code state. We do this by swallowing the
40                 # entire \" token with a fake transition and remaining in the string state.
41                 # Without this fake transition, the \ would be considered an ordinary part of the
42                 # string and the " would incorrectly return us to the code state.
43                 attr_reader :fake
44
45                 # Each transition needs a unique name. The default name is generated
46                 # by combining the language name with the names of the 'from' state and the 'to' state.
47                 # In the case that this is not unique, you may provide an additional string here
48                 # that will be appended to the generated name.
49                 attr_reader :name_extension
50
51                 def initialize(language, token, from, to, who_eats, fake, name_extension = "")
52                         @language = language
53                         @token = token
54                         @from = from.to_s
55                         @to = to
56                         @who_eats = who_eats
57                         @fake = fake
58                         @name_extension = name_extension.to_s.upcase
59                 end
60
61                 def definition
62                 "#{ language.upcase }_#{ from.to_s.upcase }__#{ to.to_s.upcase }#{ name_extension == "" ? "" : "_" + name_extension }".upcase
63                 end
64
65                 def token_eater
66                         case @who_eats
67                         when :from
68                                 'FromEatsToken'
69                         when :to
70                                 'ToEatsToken'
71                         when :neither
72                                 'NeitherEatsToken'
73                         else
74                                 raise 'UnknownWhoEats'
75                         end
76                 end
77
78                 # Emit the transition as a C code definition in the generated source file.
79                 def print(io)
80                         full_from = from == :return ? "RETURN" : "&#{ language }_#{ from }".upcase
81                         full_to = to == :return ? "RETURN" : "&#{ language }_#{ to }".upcase
82                         io.puts %(Transition #{ definition } = { "#{ token }", #{ full_from }, #{ full_to }, #{ self.token_eater }, #{ fake } };)
83                 end
84         end
85
86         # Represents a transition from a state in one Monoglot to a state in another Monoglot.
87         class StitchTransition < Transition
88                 def initialize(token, from, to, who_eats, fake, polyglot_name)
89                         @token = token
90                         @from = from.to_s
91                         @to = to
92                         @who_eats = who_eats
93                         @fake = fake
94                         @polyglot_name = polyglot_name
95                 end
96
97                 def definition
98                         "#{ @polyglot_name.upcase }_#{ @from.to_s.upcase }__#{ @to.to_s.upcase }"
99                 end
100
101                 def print(io)
102                         full_from = @from == :return ? "RETURN" : "&#{ @from }".upcase
103                         full_to = @to == :return ? "RETURN" : "&#{ @to }".upcase
104                         io.puts %(Transition #{ definition } = { "#{ @token }", #{ full_from }, #{ full_to }, #{ self.token_eater }, #{ @fake } };)
105                 end
106         end
107 end