casty 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/CHANGELOG +50 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.markdown +1948 -0
- data/Rakefile +46 -0
- data/casty.gemspec +22 -0
- data/ext/casty/cast.c +6 -0
- data/ext/casty/cast.h +142 -0
- data/ext/casty/extconf.rb +7 -0
- data/ext/casty/parser.c +287 -0
- data/ext/casty/yylex.re +344 -0
- data/lib/casty.rb +5 -0
- data/lib/casty/c.y +911 -0
- data/lib/casty/c_nodes.rb +1096 -0
- data/lib/casty/inspect.rb +57 -0
- data/lib/casty/node.rb +741 -0
- data/lib/casty/node_list.rb +841 -0
- data/lib/casty/parse.rb +254 -0
- data/lib/casty/preprocessor.rb +92 -0
- data/lib/casty/tempfile.rb +33 -0
- data/lib/casty/to_s.rb +555 -0
- data/lib/casty/version.rb +11 -0
- data/test/all.rb +5 -0
- data/test/c_nodes_test.rb +224 -0
- data/test/lexer_test.rb +355 -0
- data/test/node_list_test.rb +2473 -0
- data/test/node_test.rb +1196 -0
- data/test/parse_test.rb +2066 -0
- data/test/parser_test.rb +3012 -0
- data/test/preprocessor_test.rb +95 -0
- data/test/render_test.rb +2086 -0
- data/test/test_helper.rb +195 -0
- metadata +127 -0
data/lib/casty/parse.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
######################################################################
|
2
|
+
#
|
3
|
+
# C.default_parser and the parse_* methods.
|
4
|
+
#
|
5
|
+
# Yeah, this could be so much faster.
|
6
|
+
#
|
7
|
+
######################################################################
|
8
|
+
|
9
|
+
module C
|
10
|
+
@@default_parser = Parser.new
|
11
|
+
def self.default_parser
|
12
|
+
@@default_parser
|
13
|
+
end
|
14
|
+
def self.default_parser=(val)
|
15
|
+
@@default_parser = val
|
16
|
+
end
|
17
|
+
|
18
|
+
class Node
|
19
|
+
#
|
20
|
+
# Return true if `str' is parsed to something `==' to this Node.
|
21
|
+
# str is first converted to a String using #to_s, then given to
|
22
|
+
# self.class.parse (along with the optional `parser').
|
23
|
+
#
|
24
|
+
def match?(str, parser=nil)
|
25
|
+
node = self.class.parse(str.to_s, parser) rescue (return false)
|
26
|
+
self == node
|
27
|
+
end
|
28
|
+
#
|
29
|
+
# Same as #match?.
|
30
|
+
#
|
31
|
+
def =~(*args)
|
32
|
+
match?(*args)
|
33
|
+
end
|
34
|
+
private
|
35
|
+
end
|
36
|
+
class NodeList
|
37
|
+
#
|
38
|
+
# As defined in Node.
|
39
|
+
#
|
40
|
+
def match?(arr, parser=nil)
|
41
|
+
arr = arr.to_a
|
42
|
+
return false if arr.length != self.length
|
43
|
+
each_with_index do |node, i|
|
44
|
+
node.match?(arr[i], parser) or return false
|
45
|
+
end
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.parse(s, parser=nil)
|
51
|
+
TranslationUnit.parse(s, parser)
|
52
|
+
end
|
53
|
+
|
54
|
+
class TranslationUnit
|
55
|
+
def self.parse(s, parser=nil)
|
56
|
+
parser ||= C.default_parser
|
57
|
+
parser.parse(s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Declaration
|
62
|
+
def self.parse(s, parser=nil)
|
63
|
+
parser ||= C.default_parser
|
64
|
+
ents = parser.parse(s).entities
|
65
|
+
if ents.length == 1 && # int i; int j;
|
66
|
+
ents[0].is_a?(Declaration) # void f() {}
|
67
|
+
return ents[0].detach
|
68
|
+
else
|
69
|
+
raise ParseError, "invalid Declaration"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Parameter
|
75
|
+
def self.parse(s, parser=nil)
|
76
|
+
parser ||= C.default_parser
|
77
|
+
ents = parser.parse("void f(#{s}) {}").entities
|
78
|
+
if ents.length == 1 && # ) {} void (
|
79
|
+
ents[0].is_a?(FunctionDef) && # ); void(
|
80
|
+
ents[0].type.params && #
|
81
|
+
ents[0].type.params.length <= 1 # x,y
|
82
|
+
param = ents[0].type.params[0]
|
83
|
+
if param.nil?
|
84
|
+
return Parameter.new(Void.new)
|
85
|
+
else
|
86
|
+
return param.detach
|
87
|
+
end
|
88
|
+
else
|
89
|
+
raise ParseError, "invalid Parameter"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Declarator
|
95
|
+
def self.parse(s, parser=nil)
|
96
|
+
parser ||= C.default_parser
|
97
|
+
# if there's a ':', declare in a struct so we can populate num_bits
|
98
|
+
if s =~ /:/
|
99
|
+
ents = parser.parse("struct {int #{s};};").entities
|
100
|
+
if ents.length == 1 && # i:1;}; struct {int i
|
101
|
+
ents[0].type.members.length == 1 && # i:1; int j
|
102
|
+
ents[0].type.members[0].declarators.length == 1 # i:1,j
|
103
|
+
return ents[0].type.members[0].declarators[0].detach
|
104
|
+
end
|
105
|
+
else
|
106
|
+
ents = parser.parse("int #{s};").entities
|
107
|
+
if ents.length == 1 && # f; int f;
|
108
|
+
ents[0].declarators.length == 1 # i,j
|
109
|
+
return ents[0].declarators[0].detach
|
110
|
+
end
|
111
|
+
end
|
112
|
+
raise ParseError, "invalid Declarator"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class FunctionDef
|
117
|
+
def self.parse(s, parser=nil)
|
118
|
+
parser ||= C.default_parser
|
119
|
+
ents = parser.parse(s).entities
|
120
|
+
if ents.length == 1 && # void f(); void g();
|
121
|
+
ents[0].is_a?(FunctionDef) # int i;
|
122
|
+
return ents[0].detach
|
123
|
+
else
|
124
|
+
raise ParseError, "invalid FunctionDef"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class Enumerator
|
130
|
+
def self.parse(s, parser=nil)
|
131
|
+
parser ||= C.default_parser
|
132
|
+
ents = parser.parse("enum {#{s}};").entities
|
133
|
+
if ents.length == 1 && # } enum {
|
134
|
+
ents[0].is_a?(Declaration) && # } f() {
|
135
|
+
ents[0].type.members.length == 1 # X, Y
|
136
|
+
return ents[0].type.members[0].detach
|
137
|
+
else
|
138
|
+
raise ParseError, "invalid Enumerator"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class MemberInit
|
144
|
+
def self.parse(s, parser=nil)
|
145
|
+
parser ||= C.default_parser
|
146
|
+
ents = parser.parse("int f() {struct s x = {#{s}};}").entities
|
147
|
+
if ents.length == 1 && # } int f() {
|
148
|
+
ents[0].def.stmts.length == 1 && # }} f() {{
|
149
|
+
ents[0].def.stmts[0].declarators.length == 1 && # 1}, y
|
150
|
+
ents[0].def.stmts[0].declarators[0].init.member_inits.length == 1 # 1, 2
|
151
|
+
return ents[0].def.stmts[0].declarators[0].init.member_inits[0].detach
|
152
|
+
else
|
153
|
+
raise ParseError, "invalid MemberInit"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class Member
|
159
|
+
def self.parse(s, parser=nil)
|
160
|
+
parser ||= C.default_parser
|
161
|
+
ents = parser.parse("int f() {struct s x = {.#{s} = 1};}").entities
|
162
|
+
if ents.length == 1 && # a = 1};} int f() {struct s x = {a
|
163
|
+
ents[0].def.stmts.length == 1 && # a = 1}; struct s y = {.a
|
164
|
+
#ents[0].def.stmts[0].length == 1 && # a = 1}, x = {.a
|
165
|
+
ents[0].def.stmts[0].declarators.length == 1 && # a = 1}, x = {.a
|
166
|
+
ents[0].def.stmts[0].declarators[0].init.member_inits.length == 1 && # x = 1, y
|
167
|
+
ents[0].def.stmts[0].declarators[0].init.member_inits[0].member && # 1
|
168
|
+
ents[0].def.stmts[0].declarators[0].init.member_inits[0].member.length == 1 # a .b
|
169
|
+
return ents[0].def.stmts[0].declarators[0].init.member_inits[0].member[0].detach
|
170
|
+
else
|
171
|
+
raise ParseError, "invalid Member"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class Statement
|
177
|
+
def self.parse(s, parser=nil)
|
178
|
+
parser ||= C.default_parser
|
179
|
+
ents = parser.parse("void f() {#{s}}").entities
|
180
|
+
if ents.length == 1 && # } void f() {
|
181
|
+
ents[0].def.stmts.length == 1 && # ;;
|
182
|
+
ents[0].def.stmts[0].is_a?(self) # int i;
|
183
|
+
return ents[0].def.stmts[0].detach
|
184
|
+
else
|
185
|
+
raise ParseError, "invalid #{self}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Label
|
191
|
+
def self.parse(s, parser=nil)
|
192
|
+
parser ||= C.default_parser
|
193
|
+
ents = parser.parse("void f() {switch (0) #{s};}").entities
|
194
|
+
if ents.length == 1 && # } void f() {
|
195
|
+
ents[0].def.stmts.length == 1 && # ;
|
196
|
+
ents[0].def.stmts[0].stmt && #
|
197
|
+
ents[0].def.stmts[0].stmt.labels.length == 1 && # x
|
198
|
+
ents[0].def.stmts[0].stmt.labels[0].is_a?(self)
|
199
|
+
return ents[0].def.stmts[0].stmt.labels[0].detach
|
200
|
+
else
|
201
|
+
raise ParseError, "invalid #{self}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class Expression
|
207
|
+
def self.parse(s, parser=nil)
|
208
|
+
parser ||= C.default_parser
|
209
|
+
if s =~ /\A(\s*(\/\*.*?\*\/)?)*\{/
|
210
|
+
# starts with a brace -- must be a CompoundLiteral. in this
|
211
|
+
# case, use a Declarator since only those can handle typeless
|
212
|
+
# CompoundLiterals.
|
213
|
+
ents = parser.parse("int i = #{s};").entities
|
214
|
+
if ents.length == 1 && # 1; int i = 1
|
215
|
+
ents[0].declarators.length == 1 && # 1, j = 2
|
216
|
+
ents[0].declarators[0].init.is_a?(self)
|
217
|
+
return ents[0].declarators[0].init.detach
|
218
|
+
else
|
219
|
+
raise ParseError, "invalid #{self}"
|
220
|
+
end
|
221
|
+
else
|
222
|
+
ents = parser.parse("void f() {#{s};}").entities
|
223
|
+
if ents.length == 1 && # } void f() {
|
224
|
+
ents[0].def.stmts.length == 1 && # ;
|
225
|
+
ents[0].def.stmts[0].is_a?(ExpressionStatement) && # int i
|
226
|
+
ents[0].def.stmts[0].expr.is_a?(self)
|
227
|
+
return ents[0].def.stmts[0].expr.detach
|
228
|
+
else
|
229
|
+
raise ParseError, "invalid #{self}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class Type
|
236
|
+
def self.parse(s, parser=nil)
|
237
|
+
parser ||= C.default_parser
|
238
|
+
ents = parser.parse("void f() {(#{s})x;}").entities
|
239
|
+
if ents.length == 1 && # 1);} void f() {(int
|
240
|
+
ents[0].def.stmts.length == 1 && # 1); (int
|
241
|
+
ents[0].def.stmts[0].expr.type.is_a?(self)
|
242
|
+
return ents[0].def.stmts[0].expr.type.detach
|
243
|
+
else
|
244
|
+
raise ParseError, "invalid #{self}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Make sure we didn't miss any
|
250
|
+
CORE_C_NODE_CLASSES.each do |c|
|
251
|
+
c.respond_to? :parse or
|
252
|
+
raise "#{c}.parse not defined"
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
######################################################################
|
4
|
+
#
|
5
|
+
# A C preprocessor that wraps the command in Config::CONFIG['CPP'].
|
6
|
+
#
|
7
|
+
# Assumes a POSIX-style cpp command line interface, in particular, -I
|
8
|
+
# and -D options.
|
9
|
+
#
|
10
|
+
######################################################################
|
11
|
+
|
12
|
+
module C
|
13
|
+
class Preprocessor
|
14
|
+
class Error < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :command
|
19
|
+
end
|
20
|
+
self.command = (defined?(RbConfig) ? RbConfig : Config)::CONFIG['CPP']
|
21
|
+
|
22
|
+
attr_accessor :pwd, :include_path, :macros, :undef_macros
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@include_path = []
|
26
|
+
@macros = {}
|
27
|
+
@undef_macros = Set.new
|
28
|
+
end
|
29
|
+
def preprocess(text)
|
30
|
+
filename = nil
|
31
|
+
temp_file = nil
|
32
|
+
Tempfile.open(['cast-preprocessor-input.', '.c'], File.expand_path(pwd || '.')) do |file|
|
33
|
+
temp_file = file
|
34
|
+
filename = file.path
|
35
|
+
file.puts text
|
36
|
+
end
|
37
|
+
return preprocess_file(filename);
|
38
|
+
ensure
|
39
|
+
if temp_file
|
40
|
+
temp_file.close
|
41
|
+
temp_file.unlink
|
42
|
+
end
|
43
|
+
end
|
44
|
+
def preprocess_file(file_name, options={})
|
45
|
+
file_name = File.expand_path(file_name)
|
46
|
+
dir = File.dirname(file_name)
|
47
|
+
stderr_buf = Tempfile.new('cast-preprocessor-stderr.txt')
|
48
|
+
FileUtils.cd(dir) do
|
49
|
+
output = `#{full_command(file_name)} 2>#{shellquote(stderr_buf.path)}`
|
50
|
+
if $? == 0 || options[:force] && !output.empty?
|
51
|
+
return output
|
52
|
+
else
|
53
|
+
raise Error, stderr_buf.read
|
54
|
+
end
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
stderr_buf.close
|
58
|
+
stderr_buf.unlink
|
59
|
+
end
|
60
|
+
|
61
|
+
private # -------------------------------------------------------
|
62
|
+
|
63
|
+
def shellquote(arg)
|
64
|
+
if arg !~ /[\"\'\\$&<>\(\)|\s]/
|
65
|
+
return arg
|
66
|
+
elsif arg !~ /\'/
|
67
|
+
return "'#{arg}'"
|
68
|
+
else
|
69
|
+
arg.gsub!(/([\"\\$&<>|])/, '\\\\\\1')
|
70
|
+
return "\"#{arg}\""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
def full_command(filename)
|
74
|
+
include_args = include_path.map do |path|
|
75
|
+
"#{shellquote('-I'+path)}"
|
76
|
+
end.join(' ')
|
77
|
+
macro_args = (undef_macros.map{|m| shellquote "-U#{m}"} +
|
78
|
+
macros.map do |key, val|
|
79
|
+
case key
|
80
|
+
when :@imacros
|
81
|
+
"-imacros" + shellquote(File.expand_path(val))
|
82
|
+
when :@include
|
83
|
+
"-include" + shellquote(File.expand_path(val))
|
84
|
+
else
|
85
|
+
shellquote("-D#{key}#{"=#{val}" if val}")
|
86
|
+
end
|
87
|
+
end).join(' ')
|
88
|
+
filename = shellquote(filename)
|
89
|
+
"#{Preprocessor.command} #{include_args} #{macro_args} #{filename}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
######################################################################
|
2
|
+
#
|
3
|
+
# Alternative to the standard ruby tempfile library, which lets you
|
4
|
+
# specify a filename suffix.
|
5
|
+
#
|
6
|
+
######################################################################
|
7
|
+
|
8
|
+
require 'delegate'
|
9
|
+
require 'tmpdir'
|
10
|
+
require 'tempfile'
|
11
|
+
|
12
|
+
#
|
13
|
+
# Setting the extension of a temp file has only been possible since
|
14
|
+
# Ruby 1.8.7.
|
15
|
+
#
|
16
|
+
module C
|
17
|
+
if RUBY_VERSION >= '1.8.7'
|
18
|
+
Tempfile = ::Tempfile
|
19
|
+
else
|
20
|
+
class Tempfile < ::Tempfile
|
21
|
+
def initialize(basename, tmpdir=Dir::tmpdir)
|
22
|
+
if basename.is_a?(::Array)
|
23
|
+
basename, @suffix = *basename
|
24
|
+
end
|
25
|
+
super(basename, tmpdir)
|
26
|
+
end
|
27
|
+
|
28
|
+
def make_tmpname(basename, n)
|
29
|
+
sprintf('%s%d.%d%s', basename, $$, n, @suffix)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/casty/to_s.rb
ADDED
@@ -0,0 +1,555 @@
|
|
1
|
+
######################################################################
|
2
|
+
#
|
3
|
+
# The Node#to_s methods.
|
4
|
+
#
|
5
|
+
# Yeah, this could be so so *SO* much faster.
|
6
|
+
#
|
7
|
+
######################################################################
|
8
|
+
|
9
|
+
module C
|
10
|
+
# undef the #to_s methods so we can check that we didn't forget to
|
11
|
+
# define any
|
12
|
+
Node.send(:undef_method, :to_s)
|
13
|
+
|
14
|
+
INDENT = ' '
|
15
|
+
class Node
|
16
|
+
private
|
17
|
+
def indent(s, levels=1)
|
18
|
+
s.gsub(/^/, INDENT*levels)
|
19
|
+
end
|
20
|
+
def hang(stmt, cont=false)
|
21
|
+
if stmt.is_a?(Block) && stmt.labels.empty?
|
22
|
+
return " #{stmt.to_s(:hanging)}" << (cont ? ' ' : '')
|
23
|
+
else
|
24
|
+
return "\n#{stmt.to_s}" << (cont ? "\n" : '')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TranslationUnit
|
30
|
+
def to_s
|
31
|
+
entities.map{|n| n.to_s}.join("\n\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
class Declaration
|
35
|
+
def to_s
|
36
|
+
str = ''
|
37
|
+
inline? and str << "inline "
|
38
|
+
storage and str << "#{storage} "
|
39
|
+
if declarators.empty?
|
40
|
+
return str << "#{type};"
|
41
|
+
else
|
42
|
+
return str << "#{type} " << declarators.join(', ') << ';'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
class Declarator
|
47
|
+
def to_s
|
48
|
+
(indirect_type ? indirect_type.to_s(name) : name.dup) <<
|
49
|
+
(init ? " = #{init}" : '') <<
|
50
|
+
(num_bits ? " : #{num_bits}" : '')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
class FunctionDef
|
54
|
+
def to_s
|
55
|
+
str = ''
|
56
|
+
static? and str << 'static '
|
57
|
+
inline? and str << 'inline '
|
58
|
+
if no_prototype?
|
59
|
+
str << "#{type.to_s(name, true)}\n"
|
60
|
+
type.params.each do |p|
|
61
|
+
str << indent("#{p.to_s};\n")
|
62
|
+
end
|
63
|
+
str << "#{self.def.to_s(:hanging)}"
|
64
|
+
else
|
65
|
+
str << "#{type.to_s(name)}#{hang(self.def)}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# ------------------------------------------------------------------
|
71
|
+
# Statements
|
72
|
+
# ------------------------------------------------------------------
|
73
|
+
|
74
|
+
class Statement
|
75
|
+
def label(str)
|
76
|
+
labels.map{|s| "#{s}\n"}.join + indent(str)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
class Block
|
80
|
+
def to_s(hanging=false)
|
81
|
+
str = stmts.map do |s|
|
82
|
+
if s.is_a? Statement
|
83
|
+
s.to_s
|
84
|
+
else
|
85
|
+
indent(s.to_s)
|
86
|
+
end
|
87
|
+
end.join("\n")
|
88
|
+
str << "\n" unless str == ''
|
89
|
+
str = "{\n" << str << "}"
|
90
|
+
if hanging
|
91
|
+
if labels.empty?
|
92
|
+
return str
|
93
|
+
else
|
94
|
+
return "\n" << label(str)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
return label(str)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
class If
|
102
|
+
def to_s
|
103
|
+
str = "if (#{cond})"
|
104
|
+
if self.else.nil?
|
105
|
+
str << hang(self.then)
|
106
|
+
else
|
107
|
+
str << hang(self.then, :cont) << 'else' << hang(self.else)
|
108
|
+
end
|
109
|
+
return label(str)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
class Switch
|
113
|
+
def to_s
|
114
|
+
label("switch (#{cond})" << hang(stmt))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
class While
|
118
|
+
def to_s
|
119
|
+
if do?
|
120
|
+
label('do' << hang(stmt, :cont) << "while (#{cond});")
|
121
|
+
else
|
122
|
+
label("while (#{cond})" << hang(stmt))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
class For
|
127
|
+
def to_s
|
128
|
+
initstr =
|
129
|
+
case init
|
130
|
+
when nil
|
131
|
+
';'
|
132
|
+
when Declaration
|
133
|
+
"#{init}"
|
134
|
+
else
|
135
|
+
"#{init};"
|
136
|
+
end
|
137
|
+
condstr = cond ? " #{cond};" : ';'
|
138
|
+
iterstr = iter ? " #{iter}" : ''
|
139
|
+
label("for (#{initstr}#{condstr}#{iterstr})" << hang(stmt))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
class Goto
|
143
|
+
def to_s
|
144
|
+
label("goto #{target};")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
class Continue
|
148
|
+
def to_s
|
149
|
+
label("continue;")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
class Break
|
153
|
+
def to_s
|
154
|
+
label("break;")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
class Return
|
158
|
+
def to_s
|
159
|
+
label("return#{expr ? ' '+expr.to_s : ''};")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
class ExpressionStatement
|
163
|
+
def to_s
|
164
|
+
label("#{expr};")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class PlainLabel
|
169
|
+
def to_s
|
170
|
+
"#{name}:"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
class Default
|
174
|
+
def to_s
|
175
|
+
'default:'
|
176
|
+
end
|
177
|
+
end
|
178
|
+
class Case
|
179
|
+
def to_s
|
180
|
+
"case #{expr}:"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# ------------------------------------------------------------------
|
185
|
+
# Expressions
|
186
|
+
# ------------------------------------------------------------------
|
187
|
+
|
188
|
+
precedence_table = {}
|
189
|
+
[[Comma],
|
190
|
+
[Assign, MultiplyAssign, DivideAssign, ModAssign, AddAssign,
|
191
|
+
SubtractAssign, ShiftLeftAssign, ShiftRightAssign, BitAndAssign,
|
192
|
+
BitXorAssign, BitOrAssign],
|
193
|
+
[Conditional],
|
194
|
+
[Or],
|
195
|
+
[And],
|
196
|
+
[BitOr],
|
197
|
+
[BitXor],
|
198
|
+
[BitAnd],
|
199
|
+
[Equal, NotEqual],
|
200
|
+
[Less, More, LessOrEqual, MoreOrEqual],
|
201
|
+
[ShiftLeft, ShiftRight],
|
202
|
+
[Add, Subtract],
|
203
|
+
[Multiply, Divide, Mod],
|
204
|
+
[PreInc, PreDec, Sizeof, Cast, Address, Dereference, Positive, Negative,
|
205
|
+
BitNot, Not],
|
206
|
+
[Index, Call, Arrow, Dot, PostInc, PostDec],
|
207
|
+
[Literal, Variable]
|
208
|
+
].each_with_index do |klasses, prec|
|
209
|
+
klasses.each do |klass|
|
210
|
+
klass.send(:define_method, :to_s_precedence){|| prec}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
# check all Expression classes have a precedence
|
214
|
+
C::Expression.subclasses_recursive do |c|
|
215
|
+
next if !C::Node.subclasses_recursive.include? c
|
216
|
+
c.instance_methods.include? 'to_s_precedence' or
|
217
|
+
raise "#{c}#to_s_precedence not defined"
|
218
|
+
end
|
219
|
+
|
220
|
+
# PrefixExpressions
|
221
|
+
[ [Cast , proc{"(#{self.type})"}, false],
|
222
|
+
[Address , proc{"&" }, true ],
|
223
|
+
[Dereference, proc{"*" }, false],
|
224
|
+
[Positive , proc{"+" }, true ],
|
225
|
+
[Negative , proc{"-" }, true ],
|
226
|
+
[PreInc , proc{"++" }, false],
|
227
|
+
[PreDec , proc{"--" }, false],
|
228
|
+
[BitNot , proc{"~" }, false],
|
229
|
+
[Not , proc{"!" }, false]
|
230
|
+
].each do |c, proc, space_needed|
|
231
|
+
c.send(:define_method, :to_s) do | |
|
232
|
+
if expr.to_s_precedence < self.to_s_precedence
|
233
|
+
return "#{instance_eval(&proc)}(#{expr})"
|
234
|
+
elsif space_needed && expr.class == self.class
|
235
|
+
return "#{instance_eval(&proc)} #{expr}"
|
236
|
+
else
|
237
|
+
return "#{instance_eval(&proc)}#{expr}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# PostfixExpressions
|
242
|
+
[ [Arrow , proc{"->#{member}"}],
|
243
|
+
[Dot , proc{".#{member}" }],
|
244
|
+
[Index , proc{"[#{index}]" }],
|
245
|
+
[PostInc , proc{"++" }],
|
246
|
+
[PostDec , proc{"--" }]
|
247
|
+
].each do |c, proc|
|
248
|
+
c.send(:define_method, :to_s) do | |
|
249
|
+
if expr.to_s_precedence < self.to_s_precedence
|
250
|
+
return "(#{expr})#{instance_eval(&proc)}"
|
251
|
+
else
|
252
|
+
return "#{expr}#{instance_eval(&proc)}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
# BinaryExpressions
|
257
|
+
[ [Add , '+' ],
|
258
|
+
[Subtract , '-' ],
|
259
|
+
[Multiply , '*' ],
|
260
|
+
[Divide , '/' ],
|
261
|
+
[Mod , '%' ],
|
262
|
+
[Equal , '=='],
|
263
|
+
[NotEqual , '!='],
|
264
|
+
[Less , '<' ],
|
265
|
+
[More , '>' ],
|
266
|
+
[LessOrEqual, '<='],
|
267
|
+
[MoreOrEqual, '>='],
|
268
|
+
[BitAnd , '&' ],
|
269
|
+
[BitOr , '|' ],
|
270
|
+
[BitXor , '^' ],
|
271
|
+
[ShiftLeft , '<<'],
|
272
|
+
[ShiftRight , '>>'],
|
273
|
+
[And , '&&'],
|
274
|
+
[Or , '||'],
|
275
|
+
].each do |c, op|
|
276
|
+
c.send(:define_method, :to_s) do | |
|
277
|
+
if expr1.to_s_precedence < self.to_s_precedence
|
278
|
+
str1 = "(#{expr1})"
|
279
|
+
else
|
280
|
+
str1 = "#{expr1}"
|
281
|
+
end
|
282
|
+
# all binary expressions are left associative
|
283
|
+
if expr2.to_s_precedence <= self.to_s_precedence
|
284
|
+
str2 = "(#{expr2})"
|
285
|
+
else
|
286
|
+
str2 = "#{expr2}"
|
287
|
+
end
|
288
|
+
"#{str1} #{op} #{str2}"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
# AssignmentExpressions
|
292
|
+
[ [Assign , '' ],
|
293
|
+
[MultiplyAssign , '*' ],
|
294
|
+
[DivideAssign , '/' ],
|
295
|
+
[ModAssign , '%' ],
|
296
|
+
[AddAssign , '+' ],
|
297
|
+
[SubtractAssign , '-' ],
|
298
|
+
[ShiftLeftAssign , '<<'],
|
299
|
+
[ShiftRightAssign, '>>'],
|
300
|
+
[BitAndAssign , '&' ],
|
301
|
+
[BitXorAssign , '^' ],
|
302
|
+
[BitOrAssign , '|' ]
|
303
|
+
].each do |c, op|
|
304
|
+
c.send(:define_method, :to_s) do | |
|
305
|
+
if rval.to_s_precedence < self.to_s_precedence
|
306
|
+
rvalstr = "(#{rval})"
|
307
|
+
else
|
308
|
+
rvalstr = "#{rval}"
|
309
|
+
end
|
310
|
+
if lval.to_s_precedence < self.to_s_precedence
|
311
|
+
lvalstr = "(#{lval})"
|
312
|
+
else
|
313
|
+
lvalstr = "#{lval}"
|
314
|
+
end
|
315
|
+
"#{lvalstr} #{op}= #{rvalstr}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
# Other Expressions
|
319
|
+
class Sizeof
|
320
|
+
def to_s
|
321
|
+
"sizeof(#{expr})"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
# DirectTypes
|
325
|
+
int_longnesses = ['short ', '', 'long ', 'long long ']
|
326
|
+
float_longnesses = ['float', 'double', 'long double']
|
327
|
+
[ [Struct, proc do
|
328
|
+
str = 'struct'
|
329
|
+
name and str << " #{name}"
|
330
|
+
members and str << " {\n" << indent(members.join("\n")) << "\n}"
|
331
|
+
str
|
332
|
+
end],
|
333
|
+
[Union, proc do
|
334
|
+
str = 'union'
|
335
|
+
name and str << " #{name}"
|
336
|
+
members and str << " {\n" << indent(members.join("\n")) << "\n}"
|
337
|
+
str
|
338
|
+
end],
|
339
|
+
[Enum, proc do
|
340
|
+
str = 'enum'
|
341
|
+
name and str << " #{name}"
|
342
|
+
members and str << " {\n" << indent(members.join(",\n")) << "\n}"
|
343
|
+
str
|
344
|
+
end],
|
345
|
+
[CustomType, proc{name.dup }],
|
346
|
+
[Void , proc{'void' }],
|
347
|
+
[Int , proc do
|
348
|
+
longness_str = int_longnesses[longness+1].dup
|
349
|
+
"#{unsigned? ? 'unsigned ' : ''}#{longness_str}int"
|
350
|
+
end],
|
351
|
+
[Float , proc{float_longnesses[longness].dup}],
|
352
|
+
[Char , proc{"#{unsigned? ? 'unsigned ' : signed? ? 'signed ' : ''}char"}],
|
353
|
+
[Bool , proc{"_Bool" }],
|
354
|
+
[Complex , proc{"_Complex #{float_longnesses[longness]}"}],
|
355
|
+
[Imaginary , proc{"_Imaginary #{float_longnesses[longness]}"}]
|
356
|
+
].each do |c, x|
|
357
|
+
c.send(:define_method, :to_s) do |*args|
|
358
|
+
case args.length
|
359
|
+
when 0
|
360
|
+
namestr = nil
|
361
|
+
when 1
|
362
|
+
namestr = args[0]
|
363
|
+
namestr = nil if namestr == ''
|
364
|
+
else
|
365
|
+
raise ArgumentError, "wrong number of args (#{args.length} for 1)"
|
366
|
+
end
|
367
|
+
str = ''
|
368
|
+
const? and str << 'const '
|
369
|
+
restrict? and str << 'restrict '
|
370
|
+
volatile? and str << 'volatile '
|
371
|
+
str << instance_eval(&x) << (namestr ? " #{namestr}" : '')
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
class Enumerator
|
376
|
+
def to_s
|
377
|
+
if val
|
378
|
+
"#{name} = #{val}"
|
379
|
+
else
|
380
|
+
"#{name}"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
class Comma
|
386
|
+
def to_s
|
387
|
+
exprs.join(', ')
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
class Conditional
|
392
|
+
def to_s
|
393
|
+
strs = [:cond, :then, :else].map do |child|
|
394
|
+
val = send(child)
|
395
|
+
if val.to_s_precedence <= self.to_s_precedence
|
396
|
+
"(#{val})"
|
397
|
+
else
|
398
|
+
"#{val}"
|
399
|
+
end
|
400
|
+
end
|
401
|
+
"#{strs[0]} ? #{strs[1]} : #{strs[2]}"
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
class Call
|
406
|
+
def to_s
|
407
|
+
argstrs = args.map do |arg|
|
408
|
+
if arg.is_a? Comma
|
409
|
+
"(#{arg})"
|
410
|
+
else
|
411
|
+
"#{arg}"
|
412
|
+
end
|
413
|
+
end
|
414
|
+
if expr.to_s_precedence < self.to_s_precedence
|
415
|
+
exprstr = "(#{expr})"
|
416
|
+
else
|
417
|
+
exprstr = "#{expr}"
|
418
|
+
end
|
419
|
+
"#{exprstr}(#{argstrs.join(', ')})"
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
## IndirectTypes
|
424
|
+
class Pointer
|
425
|
+
def to_s(name=nil)
|
426
|
+
str = '*'
|
427
|
+
const? and str << 'const '
|
428
|
+
restrict? and str << 'restrict '
|
429
|
+
volatile? and str << 'volatile '
|
430
|
+
str =
|
431
|
+
case type
|
432
|
+
when Function, Array
|
433
|
+
"(#{str}#{name})"
|
434
|
+
else
|
435
|
+
"#{str}#{name}"
|
436
|
+
end
|
437
|
+
if type
|
438
|
+
type.to_s(str)
|
439
|
+
else
|
440
|
+
str
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
class Array
|
445
|
+
def to_s(name=nil)
|
446
|
+
str = "#{name}[#{length}]"
|
447
|
+
if type
|
448
|
+
type.to_s(str)
|
449
|
+
else
|
450
|
+
str
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
class Function
|
455
|
+
def to_s(name=nil, no_types=false)
|
456
|
+
str =
|
457
|
+
if params.nil?
|
458
|
+
paramstr = ''
|
459
|
+
elsif params.empty?
|
460
|
+
paramstr = 'void'
|
461
|
+
else
|
462
|
+
if no_types
|
463
|
+
paramstr = params.map{|p| p.name}.join(', ')
|
464
|
+
else
|
465
|
+
paramstr = params.join(', ')
|
466
|
+
end
|
467
|
+
end
|
468
|
+
var_args? and paramstr << ', ...'
|
469
|
+
str = "#{name}(#{paramstr})"
|
470
|
+
if type
|
471
|
+
type.to_s(str)
|
472
|
+
else
|
473
|
+
str
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
class Parameter
|
478
|
+
def to_s
|
479
|
+
str = register? ? 'register ' : ''
|
480
|
+
if type
|
481
|
+
str << type.to_s(name)
|
482
|
+
else
|
483
|
+
str << name.to_s
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
## Literals
|
489
|
+
class StringLiteral
|
490
|
+
def to_s
|
491
|
+
"\"#{val}\""
|
492
|
+
end
|
493
|
+
end
|
494
|
+
class CharLiteral
|
495
|
+
def to_s
|
496
|
+
"'#{val}'"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
class CompoundLiteral
|
500
|
+
def to_s
|
501
|
+
str = ''
|
502
|
+
type and
|
503
|
+
str << "(#{type}) "
|
504
|
+
str << "{\n" << indent(member_inits.join(",\n")) << "\n}"
|
505
|
+
end
|
506
|
+
end
|
507
|
+
class MemberInit
|
508
|
+
def to_s
|
509
|
+
str = ''
|
510
|
+
if member
|
511
|
+
memberstr = member.map do |m|
|
512
|
+
if m.is_a? Expression
|
513
|
+
"[#{m}]"
|
514
|
+
else
|
515
|
+
".#{m}"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
str << memberstr.join(' ') << ' = '
|
519
|
+
end
|
520
|
+
return str << init.to_s
|
521
|
+
end
|
522
|
+
end
|
523
|
+
class Member
|
524
|
+
def to_s
|
525
|
+
name.dup
|
526
|
+
end
|
527
|
+
end
|
528
|
+
class IntLiteral
|
529
|
+
def to_s
|
530
|
+
val.to_s
|
531
|
+
end
|
532
|
+
end
|
533
|
+
class FloatLiteral
|
534
|
+
def to_s
|
535
|
+
val.to_s
|
536
|
+
end
|
537
|
+
end
|
538
|
+
class Variable
|
539
|
+
def to_s
|
540
|
+
name.dup
|
541
|
+
end
|
542
|
+
end
|
543
|
+
class BlockExpression
|
544
|
+
def to_s
|
545
|
+
# note that the grammar does not allow the block to have a label
|
546
|
+
"(#{block.to_s(:hanging)})"
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
# check we didn't miss any
|
551
|
+
CORE_C_NODE_CLASSES.each do |c|
|
552
|
+
c.method_defined? :to_s or
|
553
|
+
raise "#{c}#to_s not defined"
|
554
|
+
end
|
555
|
+
end
|