rucc 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +55 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +46 -0
- data/LICENCE +21 -0
- data/README.md +82 -0
- data/Rakefile +2 -0
- data/Vagrantfile +10 -0
- data/bin/console +10 -0
- data/bin/rspec +2 -0
- data/bin/setup +8 -0
- data/exe/rucc +7 -0
- data/include/8cc.h +48 -0
- data/include/float.h +44 -0
- data/include/iso646.h +20 -0
- data/include/rucc.h +2 -0
- data/include/stdalign.h +11 -0
- data/include/stdarg.h +52 -0
- data/include/stdbool.h +11 -0
- data/include/stddef.h +15 -0
- data/include/stdnoreturn.h +8 -0
- data/lib/rucc.rb +8 -0
- data/lib/rucc/case.rb +22 -0
- data/lib/rucc/decl.rb +9 -0
- data/lib/rucc/enc.rb +9 -0
- data/lib/rucc/engine.rb +138 -0
- data/lib/rucc/file_io.rb +108 -0
- data/lib/rucc/file_io_list.rb +56 -0
- data/lib/rucc/gen.rb +1602 -0
- data/lib/rucc/int_evaluator.rb +114 -0
- data/lib/rucc/k.rb +73 -0
- data/lib/rucc/keyword.rb +17 -0
- data/lib/rucc/kind.rb +43 -0
- data/lib/rucc/label_gen.rb +13 -0
- data/lib/rucc/lexer.rb +40 -0
- data/lib/rucc/lexer/impl.rb +683 -0
- data/lib/rucc/lexer/preprocessor.rb +888 -0
- data/lib/rucc/lexer/preprocessor/cond_incl.rb +27 -0
- data/lib/rucc/lexer/preprocessor/constructor.rb +54 -0
- data/lib/rucc/lexer/preprocessor/pragma.rb +31 -0
- data/lib/rucc/lexer/preprocessor/special_macro.rb +110 -0
- data/lib/rucc/libc.rb +47 -0
- data/lib/rucc/m.rb +7 -0
- data/lib/rucc/macro.rb +24 -0
- data/lib/rucc/node.rb +530 -0
- data/lib/rucc/node/conv.rb +33 -0
- data/lib/rucc/op.rb +61 -0
- data/lib/rucc/operator.rb +13 -0
- data/lib/rucc/option.rb +30 -0
- data/lib/rucc/parser.rb +961 -0
- data/lib/rucc/parser/break.rb +18 -0
- data/lib/rucc/parser/builtin.rb +25 -0
- data/lib/rucc/parser/continue.rb +18 -0
- data/lib/rucc/parser/do.rb +33 -0
- data/lib/rucc/parser/ensure.rb +39 -0
- data/lib/rucc/parser/enum.rb +64 -0
- data/lib/rucc/parser/expr.rb +493 -0
- data/lib/rucc/parser/for.rb +71 -0
- data/lib/rucc/parser/func.rb +274 -0
- data/lib/rucc/parser/func_call.rb +54 -0
- data/lib/rucc/parser/goto.rb +29 -0
- data/lib/rucc/parser/if.rb +23 -0
- data/lib/rucc/parser/initializer.rb +237 -0
- data/lib/rucc/parser/label.rb +31 -0
- data/lib/rucc/parser/return.rb +16 -0
- data/lib/rucc/parser/struct_and_union.rb +280 -0
- data/lib/rucc/parser/switch.rb +117 -0
- data/lib/rucc/parser/while.rb +29 -0
- data/lib/rucc/pos.rb +11 -0
- data/lib/rucc/rmap.rb +22 -0
- data/lib/rucc/s.rb +9 -0
- data/lib/rucc/static_label_gen.rb +15 -0
- data/lib/rucc/t.rb +18 -0
- data/lib/rucc/tempname_gen.rb +14 -0
- data/lib/rucc/token.rb +114 -0
- data/lib/rucc/token_gen.rb +68 -0
- data/lib/rucc/type.rb +304 -0
- data/lib/rucc/type/check.rb +39 -0
- data/lib/rucc/type/conv.rb +29 -0
- data/lib/rucc/type_info.rb +21 -0
- data/lib/rucc/utf.rb +126 -0
- data/lib/rucc/util.rb +111 -0
- data/lib/rucc/version.rb +3 -0
- data/rucc.gemspec +38 -0
- metadata +201 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Rucc
|
2
|
+
class Parser
|
3
|
+
module For
|
4
|
+
private
|
5
|
+
# @return [Node]
|
6
|
+
def read_for_stmt
|
7
|
+
expect!('(')
|
8
|
+
beg = @label_gen.next
|
9
|
+
mid = @label_gen.next
|
10
|
+
lst = @label_gen.next
|
11
|
+
|
12
|
+
orig = @localenv
|
13
|
+
@localenv = RMap.new(@localenv)
|
14
|
+
init = read_opt_decl_or_stmt
|
15
|
+
cond = read_expr_opt
|
16
|
+
if cond && Type.is_flotype(cond.ty)
|
17
|
+
cond = Node.ast_conv(Type::BOOL, cond)
|
18
|
+
end
|
19
|
+
expect!(';')
|
20
|
+
step = read_expr_opt
|
21
|
+
expect!(')')
|
22
|
+
body = nil # declaration for ruby
|
23
|
+
with_jump_labels(mid, lst) { body = read_stmt }
|
24
|
+
@localenv = orig
|
25
|
+
|
26
|
+
v = []
|
27
|
+
if !init.nil?
|
28
|
+
v.push(init)
|
29
|
+
end
|
30
|
+
v.push(Node.ast_dest(beg))
|
31
|
+
if !cond.nil?
|
32
|
+
v.push(Node.ast_if(cond, nil, Node.ast_jump(lst)))
|
33
|
+
end
|
34
|
+
if !body.nil?
|
35
|
+
v.push(body)
|
36
|
+
end
|
37
|
+
v.push(Node.ast_dest(mid))
|
38
|
+
if !step.nil?
|
39
|
+
v.push(step)
|
40
|
+
end
|
41
|
+
v.push(Node.ast_jump(beg))
|
42
|
+
v.push(Node.ast_dest(lst))
|
43
|
+
Node.ast_compound_stmt(v)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [String] cont
|
47
|
+
# @param [String] brk
|
48
|
+
def with_jump_labels(cont, brk)
|
49
|
+
# Set jump lablels
|
50
|
+
ocontinue = @lcontinue
|
51
|
+
obreak = @lbreak
|
52
|
+
@lcontinue = cont
|
53
|
+
@lbreak = brk
|
54
|
+
|
55
|
+
yield
|
56
|
+
|
57
|
+
# Restore jump lablels
|
58
|
+
@lcontinue = ocontinue
|
59
|
+
@lbreak = obreak
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Node, NilClass]
|
63
|
+
def read_opt_decl_or_stmt
|
64
|
+
return nil if next_token?(';')
|
65
|
+
list = []
|
66
|
+
read_decl_or_stmt(list)
|
67
|
+
Node.ast_compound_stmt(list)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
module Rucc
|
2
|
+
class Parser
|
3
|
+
module Func
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# @param [Type] basety
|
8
|
+
# @param(return) [Array] params
|
9
|
+
# @return [Type]
|
10
|
+
def read_declarator_func(basety, params)
|
11
|
+
if basety.kind == Kind::FUNC
|
12
|
+
raise "function returning a function"
|
13
|
+
end
|
14
|
+
if basety.kind == Kind::ARRAY
|
15
|
+
raise "function returning an array"
|
16
|
+
end
|
17
|
+
read_func_param_list(params, basety)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Array, NilClass] paramvars
|
21
|
+
# @param [Type] rettype
|
22
|
+
# @return [Type]
|
23
|
+
def read_func_param_list(paramvars, rettype)
|
24
|
+
# C11 6.7.6.3p10: A parameter list with just "void" specifies that
|
25
|
+
# the function has no parameters.
|
26
|
+
tok = get
|
27
|
+
if Token.is_keyword?(tok, K::VOID) && next_token?(')')
|
28
|
+
return Type.make_func_type(rettype, [], false, false)
|
29
|
+
end
|
30
|
+
|
31
|
+
# C11 6.7.6.3p14: K&R-style un-prototyped declaration or
|
32
|
+
# function definition having no parameters.
|
33
|
+
# We return a type representing K&R-style declaration here.
|
34
|
+
# If this is actually part of a declartion, the type will be fixed later.
|
35
|
+
if Token.is_keyword?(tok, ')')
|
36
|
+
return Type.make_func_type(rettype, [], true, true)
|
37
|
+
end
|
38
|
+
@lexer.unget_token(tok)
|
39
|
+
|
40
|
+
tok2 = peek
|
41
|
+
if next_token?(K::ELLIPSIS)
|
42
|
+
Util.errort!(tok2, "at least one parameter is required before \"...\"")
|
43
|
+
end
|
44
|
+
|
45
|
+
if is_type?(peek)
|
46
|
+
paramtypes = []
|
47
|
+
ellipsis = read_declarator_params(paramtypes, paramvars)
|
48
|
+
return Type.make_func_type(rettype, paramtypes, ellipsis, false)
|
49
|
+
end
|
50
|
+
|
51
|
+
if paramvars.nil?
|
52
|
+
Util.errort!(tok, "invalid function definition")
|
53
|
+
end
|
54
|
+
|
55
|
+
read_declarator_params_oldstyle(paramvars)
|
56
|
+
paramtypes = []
|
57
|
+
(0..(paramvars.size - 1)).each do
|
58
|
+
paramtypes.push Type::INT
|
59
|
+
end
|
60
|
+
Type.make_func_type(rettype, paramtypes, false, true)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Array] types
|
64
|
+
# @param [Array] vars
|
65
|
+
# @return [Boolean] ellipsis
|
66
|
+
def read_declarator_params(types, vars)
|
67
|
+
typeonly = vars.nil?
|
68
|
+
ellipsis = false
|
69
|
+
while true
|
70
|
+
tok = peek
|
71
|
+
if next_token?(K::ELLIPSIS)
|
72
|
+
if types.size == 0
|
73
|
+
Util.errort!(tok, "at least one parameter is required before \"...\"")
|
74
|
+
end
|
75
|
+
expect!(')')
|
76
|
+
ellipsis = true
|
77
|
+
return ellipsis
|
78
|
+
end
|
79
|
+
|
80
|
+
name = ""
|
81
|
+
ty = read_func_param(name, typeonly)
|
82
|
+
ensure_not_void!(ty)
|
83
|
+
types.push(ty)
|
84
|
+
if !typeonly
|
85
|
+
vars.push(Node.ast_lvar(ty, name, @localenv, @localvars))
|
86
|
+
end
|
87
|
+
|
88
|
+
tok = get
|
89
|
+
if Token.is_keyword?(tok, ')')
|
90
|
+
return ellipsis
|
91
|
+
end
|
92
|
+
if !Token.is_keyword?(tok, ',')
|
93
|
+
Util.errort!(tok, "comma expected, but got #{tok}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
raise "must not reach here"
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param [<String>] name
|
100
|
+
# @param [Boolean] optional
|
101
|
+
# @return [Type]
|
102
|
+
def read_func_param(name, optional)
|
103
|
+
basety = Type::INT
|
104
|
+
if is_type?(peek)
|
105
|
+
basety, _ = read_decl_spec
|
106
|
+
elsif optional
|
107
|
+
Util.errort!(peek, "type expected, but got #{peek}")
|
108
|
+
end
|
109
|
+
ty = read_declarator(name, basety, nil, optional ? DECL::PARAM_TYPEONLY : DECL::PARAM)
|
110
|
+
# C11 6.7.6.3p7: Array of T is adjusted to pointer to T
|
111
|
+
# in a function parameter list.
|
112
|
+
if ty.kind == Kind::ARRAY
|
113
|
+
return Type.make_ptr_type(ty.ptr)
|
114
|
+
end
|
115
|
+
# C11 6.7.6.3p8: Function is adjusted to pointer to function
|
116
|
+
# in a function parameter list.
|
117
|
+
if ty.kind == Kind::FUNC
|
118
|
+
return Type.make_ptr_type(ty)
|
119
|
+
end
|
120
|
+
ty
|
121
|
+
end
|
122
|
+
|
123
|
+
# Reads a K&R-style un-prototyped function parameter list.
|
124
|
+
#
|
125
|
+
# @param [<Node>] vars
|
126
|
+
def read_declarator_params_oldstyle(vars)
|
127
|
+
while true
|
128
|
+
tok = get
|
129
|
+
if tok.kind != T::IDENT
|
130
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
131
|
+
end
|
132
|
+
vars.push(Node.ast_lvar(Type::INT, tok.sval, @localenv, @localvars))
|
133
|
+
if next_token?(')')
|
134
|
+
return
|
135
|
+
end
|
136
|
+
if !next_token?(',')
|
137
|
+
Util.errort!(tok, "comma expected, but got #{get}")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Function definition
|
144
|
+
#
|
145
|
+
|
146
|
+
# @return [Node]
|
147
|
+
def read_funcdef
|
148
|
+
basetype, sclass = read_decl_spec_opt
|
149
|
+
r = nil
|
150
|
+
|
151
|
+
with_func_context do
|
152
|
+
name = "" # Used as return value
|
153
|
+
params = [] # Used as return value
|
154
|
+
functype = read_declarator(name, basetype, params, DECL::BODY)
|
155
|
+
if functype.oldstyle
|
156
|
+
if (params.size == 0)
|
157
|
+
functype.hasva = false
|
158
|
+
end
|
159
|
+
read_oldstyle_param_type(params)
|
160
|
+
functype.params = param_types(params)
|
161
|
+
end
|
162
|
+
functype.isstatic = (sclass == S::STATIC)
|
163
|
+
Node.ast_gvar(functype, name, @globalenv)
|
164
|
+
expect!('{')
|
165
|
+
r = read_func_body(functype, name, params)
|
166
|
+
backfill_labels!
|
167
|
+
end
|
168
|
+
|
169
|
+
r
|
170
|
+
end
|
171
|
+
|
172
|
+
def with_func_context(&block)
|
173
|
+
@localenv = RMap.new(@globalenv)
|
174
|
+
@gotos = []
|
175
|
+
@labels = RMap.new
|
176
|
+
|
177
|
+
yield
|
178
|
+
|
179
|
+
@localenv = nil
|
180
|
+
@gotos = nil
|
181
|
+
@labels = nil
|
182
|
+
end
|
183
|
+
|
184
|
+
# @param(return) [<Node>] params
|
185
|
+
def read_oldstyle_param_type(params)
|
186
|
+
vars = read_oldstyle_param_args
|
187
|
+
update_oldstyle_param_type(params, vars)
|
188
|
+
end
|
189
|
+
|
190
|
+
def read_oldstyle_param_args
|
191
|
+
orig = @localenv
|
192
|
+
@localenv = nil
|
193
|
+
r = []
|
194
|
+
while true
|
195
|
+
if Token.is_keyword?(peek, '{')
|
196
|
+
break
|
197
|
+
end
|
198
|
+
if !is_type?(peek)
|
199
|
+
Util.errort!(peek, "K&R-style declarator expected, but got #{peek}")
|
200
|
+
end
|
201
|
+
read_decl(r, false)
|
202
|
+
end
|
203
|
+
@localenv = orig
|
204
|
+
r
|
205
|
+
end
|
206
|
+
|
207
|
+
# @param(return) [Array] params
|
208
|
+
# @param [Node] vars
|
209
|
+
def update_oldstyle_param_type(params, vars)
|
210
|
+
vars.each do |decl|
|
211
|
+
Util.assert!{ decl.kind == AST::DECL }
|
212
|
+
var = decl.declvar
|
213
|
+
Util.assert!{ var.kind == AST::LVAR }
|
214
|
+
params.each do |param|
|
215
|
+
Util.assert!{ param.kind == AST::LVAR }
|
216
|
+
if (param.varname != var.varname)
|
217
|
+
next
|
218
|
+
end
|
219
|
+
param.ty = var.ty
|
220
|
+
return # found
|
221
|
+
end
|
222
|
+
raise "missing parameter: #{var.varname}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def backfill_labels!
|
227
|
+
@gotos.each do |src|
|
228
|
+
label = src.label
|
229
|
+
dest = @labels[label]
|
230
|
+
if dest.nil?
|
231
|
+
raise "stray #{src.kind == AST::GOTO ? "goto" : "unary &&"}: #{label}"
|
232
|
+
end
|
233
|
+
|
234
|
+
if dest.newlabel
|
235
|
+
src.newlabel = dest.newlabel
|
236
|
+
else
|
237
|
+
src.newlabel = dest.newlabel = @label_gen.next
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# @param [Type] functype
|
243
|
+
# @param [String] fname
|
244
|
+
# @param [Array] params
|
245
|
+
# @return [Node]
|
246
|
+
def read_func_body(functype, fname, params)
|
247
|
+
r = nil
|
248
|
+
|
249
|
+
with_func_body_context(functype) do
|
250
|
+
funcname = Node.ast_string(ENC::NONE, fname)
|
251
|
+
@localenv["__func__"] = funcname
|
252
|
+
@localenv["__FUNCTION__"] = funcname
|
253
|
+
body = read_compound_stmt
|
254
|
+
r = Node.ast_func(functype, fname, params, body, @localvars)
|
255
|
+
end
|
256
|
+
|
257
|
+
r
|
258
|
+
end
|
259
|
+
|
260
|
+
# @param [Type] functype
|
261
|
+
def with_func_body_context(functype, &block)
|
262
|
+
@localenv = RMap.new(@localenv)
|
263
|
+
@localvars = []
|
264
|
+
@current_func_type = functype
|
265
|
+
|
266
|
+
yield
|
267
|
+
|
268
|
+
@localenv = nil
|
269
|
+
@localvars = nil
|
270
|
+
@current_func_type = nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rucc
|
2
|
+
class Parser
|
3
|
+
module FuncCall
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# @param [Node] fp
|
8
|
+
def read_funcall(fp)
|
9
|
+
if (fp.kind == AST::ADDR) && (fp.operand.kind == AST::FUNCDESG)
|
10
|
+
desg = fp.operand
|
11
|
+
args = read_func_args(desg.ty.params)
|
12
|
+
return Node.ast_funcall(desg.ty, desg.fname, args)
|
13
|
+
end
|
14
|
+
args = read_func_args(fp.ty.ptr.params)
|
15
|
+
Node.ast_funcptr_call(fp, args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Array] params
|
19
|
+
# @return [Array]
|
20
|
+
def read_func_args(params)
|
21
|
+
args = []
|
22
|
+
i = 0
|
23
|
+
while true
|
24
|
+
break if next_token?(')')
|
25
|
+
arg = Node.conv(read_assignment_expr)
|
26
|
+
if i < params.size
|
27
|
+
paramtype = params[i]
|
28
|
+
i += 1
|
29
|
+
else
|
30
|
+
paramtype =
|
31
|
+
if Type.is_flotype(arg.ty)
|
32
|
+
Type::DOUBLE
|
33
|
+
elsif Type.is_inttype(arg.ty)
|
34
|
+
Type::INT
|
35
|
+
else
|
36
|
+
arg.ty
|
37
|
+
end
|
38
|
+
end
|
39
|
+
Type.ensure_assignable!(paramtype, arg.ty)
|
40
|
+
if paramtype.kind != arg.ty.kind
|
41
|
+
arg = Node.ast_conv(paramtype, arg)
|
42
|
+
end
|
43
|
+
args.push(arg)
|
44
|
+
tok = get
|
45
|
+
break if Token.is_keyword?(tok, ')')
|
46
|
+
if !Token.is_keyword?(tok, ',')
|
47
|
+
Util.errort(tok, "unexpected token: '#{tok}'")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
args
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rucc
|
2
|
+
class Parser
|
3
|
+
module Goto
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# @return [Node]
|
8
|
+
def read_goto_stmt
|
9
|
+
if next_token?('*')
|
10
|
+
# [GNU] computed goto. "goto *p" jumps to the address pointed by p.
|
11
|
+
tok = peek
|
12
|
+
expr = read_cast_expr
|
13
|
+
if expr.ty.kind != Kind::PTR
|
14
|
+
Util.errort!(tok, "pointer expected for computed goto, but got #{expr}")
|
15
|
+
end
|
16
|
+
return Node.ast_computed_goto(expr)
|
17
|
+
end
|
18
|
+
tok = get
|
19
|
+
if !tok || (tok.kind != T::IDENT)
|
20
|
+
Util.errort!(tok, "identifier expected, but got #{tok}")
|
21
|
+
end
|
22
|
+
expect!(';')
|
23
|
+
r = Node.ast_goto(tok.sval)
|
24
|
+
@gotos.push(r)
|
25
|
+
r
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rucc
|
2
|
+
class Parser
|
3
|
+
module If
|
4
|
+
private
|
5
|
+
def read_if_stmt
|
6
|
+
expect!('(')
|
7
|
+
cond = read_boolean_expr
|
8
|
+
expect!(')')
|
9
|
+
thn = read_stmt
|
10
|
+
if !next_token?(K::ELSE)
|
11
|
+
return Node.ast_if(cond, thn, nil)
|
12
|
+
end
|
13
|
+
els = read_stmt
|
14
|
+
Node.ast_if(cond, thn, els)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_boolean_expr
|
18
|
+
cond = read_expr
|
19
|
+
Type.is_flotype(cond.ty) ? Node.ast_conv(Type::BOOL, cond) : cond
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|