rubex 0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +101 -0
- data/HISTORY.md +3 -0
- data/README.md +112 -297
- data/REFERENCE.md +753 -0
- data/Rakefile +4 -1
- data/TUTORIAL.md +234 -0
- data/bin/rubex +1 -1
- data/docs/_config.yml +1 -0
- data/docs/index.html +1 -0
- data/examples/c_struct_interface/c_struct_interface.rb +6 -0
- data/examples/c_struct_interface/c_struct_interface.rubex +47 -0
- data/examples/linked_list/linked_list.rubex +39 -0
- data/examples/linked_list/rb_linked_list.rb +8 -0
- data/examples/rcsv wrapper/rcsv/README.md +1 -0
- data/examples/rcsv wrapper/rcsv/Rakefile +7 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/extconf.rb +3 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.c +302 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.rubex +124 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.rb +8 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv/version.rb +1 -0
- data/examples/rcsv wrapper/rcsv/rcsv.gemspec +27 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv.csv +5 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv_spec.rb +17 -0
- data/examples/rcsv wrapper/rcsv/spec/spec_helper.rb +6 -0
- data/{spec/fixtures/basic_ruby_method/Makefile → examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/Makefile } +20 -20
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.o +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/stage/lib/rcsv.so +0 -0
- data/lib/rubex.rb +6 -50
- data/lib/rubex/ast.rb +1 -3
- data/lib/rubex/ast/expression.rb +1257 -8
- data/lib/rubex/ast/node.rb +226 -28
- data/lib/rubex/ast/statement.rb +1162 -35
- data/lib/rubex/ast/top_statement.rb +815 -0
- data/lib/rubex/code_writer.rb +103 -26
- data/lib/rubex/compiler.rb +72 -0
- data/lib/rubex/compiler_config.rb +19 -0
- data/lib/rubex/constants.rb +145 -8
- data/lib/rubex/data_type.rb +667 -4
- data/lib/rubex/error.rb +15 -0
- data/lib/rubex/helpers.rb +154 -0
- data/lib/rubex/lexer.rex +186 -22
- data/lib/rubex/lexer.rex.rb +261 -35
- data/lib/rubex/parser.racc +876 -28
- data/lib/rubex/parser.racc.rb +2845 -90
- data/lib/rubex/rake_task.rb +34 -0
- data/lib/rubex/symbol_table/entry.rb +17 -3
- data/lib/rubex/symbol_table/scope.rb +298 -25
- data/lib/rubex/version.rb +1 -1
- data/rubex.gemspec +11 -3
- data/spec/basic_ruby_method_spec.rb +15 -21
- data/spec/binding_ptr_args_spec.rb +33 -0
- data/spec/bitwise_operators_spec.rb +40 -0
- data/spec/blocks_spec.rb +35 -0
- data/spec/c_bindings_spec.rb +36 -0
- data/spec/c_constants_spec.rb +33 -0
- data/spec/c_function_ptrs_spec.rb +38 -0
- data/spec/c_functions_spec.rb +35 -0
- data/spec/c_struct_interface_spec.rb +38 -0
- data/spec/call_by_reference_spec.rb +33 -0
- data/spec/class_methods_spec.rb +33 -0
- data/spec/class_spec.rb +40 -0
- data/spec/comments_spec.rb +33 -0
- data/spec/default_args_spec.rb +37 -0
- data/spec/error_handling_spec.rb +42 -0
- data/spec/examples_spec.rb +52 -0
- data/spec/expressions_spec.rb +33 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +2 -0
- data/spec/fixtures/binding_ptr_args/binding_ptr_args.rubex +30 -0
- data/spec/fixtures/bitwise_operators/bitwise_operators.rubex +40 -0
- data/spec/fixtures/blocks/blocks.rubex +11 -0
- data/spec/fixtures/c_bindings/c_bindings.rubex +58 -0
- data/spec/fixtures/c_constants/c_constants.rubex +7 -0
- data/spec/fixtures/c_function_ptrs/c_function_ptrs.rubex +52 -0
- data/spec/fixtures/c_functions/c_functions.rubex +25 -0
- data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +34 -0
- data/spec/fixtures/call_by_reference/call_by_reference.rubex +30 -0
- data/spec/fixtures/class/class.rubex +20 -0
- data/spec/fixtures/class_methods/class_methods.rubex +12 -0
- data/spec/fixtures/comments/comments.rubex +9 -0
- data/spec/fixtures/default_args/default_args.rubex +11 -0
- data/spec/fixtures/error_handling/error_handling.rubex +54 -0
- data/spec/fixtures/examples/array_to_hash.rubex +14 -0
- data/spec/fixtures/examples/rcsv.csv +5 -0
- data/spec/fixtures/examples/rcsv.rubex +329 -0
- data/spec/fixtures/expressions/expressions.rubex +10 -0
- data/spec/fixtures/if_else/if_else.rubex +77 -0
- data/spec/fixtures/implicit_lib_include/implicit_lib_include.rubex +15 -0
- data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +17 -0
- data/spec/fixtures/loops/loops.rubex +33 -0
- data/spec/fixtures/recursion/recursion.rubex +9 -0
- data/spec/fixtures/ruby_constant_method_calls/ruby_constant_method_calls.rubex +17 -0
- data/spec/fixtures/ruby_operators/ruby_operators.rubex +29 -0
- data/spec/fixtures/ruby_raise/ruby_raise.rubex +13 -0
- data/spec/fixtures/ruby_strings/ruby_strings.rubex +19 -0
- data/spec/fixtures/ruby_strings/string_blank_bm.rb +37 -0
- data/spec/fixtures/ruby_symbols/ruby_symbols.rubex +12 -0
- data/spec/fixtures/ruby_types/ruby_types.rubex +15 -0
- data/spec/fixtures/statement_expression/statement_expression.rubex +23 -0
- data/spec/fixtures/static_array/static_array.rubex +20 -0
- data/spec/fixtures/string_literals/string_literals.rubex +15 -0
- data/spec/fixtures/struct/struct.rubex +82 -0
- data/spec/fixtures/typecasting/typecasting.rubex +23 -0
- data/spec/fixtures/var_declarations/var_declarations.rubex +39 -0
- data/spec/if_else_spec.rb +39 -0
- data/spec/implicit_lib_include_spec.rb +33 -0
- data/spec/init_ruby_objects_with_literal_syntax_spec.rb +39 -0
- data/spec/loops_spec.rb +34 -0
- data/spec/recursion_spec.rb +35 -0
- data/spec/ruby_constant_method_calls_spec.rb +35 -0
- data/spec/ruby_operators_spec.rb +40 -0
- data/spec/ruby_raise_spec.rb +35 -0
- data/spec/ruby_strings_spec.rb +33 -0
- data/spec/ruby_symbols_spec.rb +37 -0
- data/spec/ruby_types_spec.rb +35 -0
- data/spec/spec_helper.rb +54 -1
- data/spec/statement_expression_spec.rb +34 -0
- data/spec/static_array_spec.rb +33 -0
- data/spec/string_literals_spec.rb +34 -0
- data/spec/struct_spec.rb +36 -0
- data/spec/typecasting_spec.rb +38 -0
- data/spec/var_declarions_spec.rb +35 -0
- metadata +255 -29
- data/lib/rubex/ast/argument_list.rb +0 -20
- data/lib/rubex/ast/c_base_type.rb +0 -11
- data/lib/rubex/ast/ruby_method_def.rb +0 -84
- data/spec/fixtures/basic_ruby_method/basic.rb +0 -3
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +0 -16
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
- data/spec/fixtures/basic_ruby_method/extconf.rb +0 -3
@@ -0,0 +1,124 @@
|
|
1
|
+
lib "rubex/ruby"; end
|
2
|
+
lib "rubex/ruby/encoding"; end
|
3
|
+
lib "rubex/stdlib"; end
|
4
|
+
|
5
|
+
lib "csv.h", link: "csv"
|
6
|
+
struct csv_parser
|
7
|
+
end
|
8
|
+
|
9
|
+
struct FILE
|
10
|
+
end
|
11
|
+
|
12
|
+
int CSV_STRICT_FINI
|
13
|
+
int CSV_APPEND_NULL
|
14
|
+
int CSV_EPARSE
|
15
|
+
int CSV_ENOMEM
|
16
|
+
int CSV_ETOOBIG
|
17
|
+
int CSV_EINVALID
|
18
|
+
int CSV_STRICT
|
19
|
+
|
20
|
+
int csv_init(csv_parser, unsigned char)
|
21
|
+
int csv_fini(csv_parser, void (*cb1)(void *, size_t, void *), void (*cb2)(int, void *), void *)
|
22
|
+
void csv_free(csv_parser *)
|
23
|
+
int csv_error(csv_parser *)
|
24
|
+
size_t csv_parse(csv_parser *p, void *, size_t, void (*cb1)(void *, size_t, void *), void (*cb2)(int, void *), void *)
|
25
|
+
end
|
26
|
+
|
27
|
+
struct rcsv_metadata
|
28
|
+
# Internal state
|
29
|
+
bool skip_current_row # Used by only_rows and except_rows filters to skip parsing of the row remainder
|
30
|
+
size_t current_col # Current column's index
|
31
|
+
size_t current_row # Current row's index
|
32
|
+
|
33
|
+
object last_entry # last entry that's going to be appended to result
|
34
|
+
object result # parsed data
|
35
|
+
end
|
36
|
+
|
37
|
+
class RcsvParseError < StandardError
|
38
|
+
end
|
39
|
+
|
40
|
+
class Rcsv
|
41
|
+
cfunc void end_of_field_callback(void* field, size_t field_size, void* data)
|
42
|
+
char* field_str = <char *>field
|
43
|
+
rcsv_metadata* meta_p = <rcsv_metadata*>data
|
44
|
+
rcsv_metadata meta = meta_p[0]
|
45
|
+
char row_conversion = 0
|
46
|
+
object parsed_field
|
47
|
+
|
48
|
+
parsed_field = ENCODED_STR_NEW(field_str, field_size)
|
49
|
+
|
50
|
+
meta_p[0].last_entry.push(parsed_field) # /* last_entry << field */
|
51
|
+
meta_p[0].current_col += 1
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
cfunc void end_of_line_callback(int last_char, void* data)
|
56
|
+
rcsv_metadata* meta = <rcsv_metadata *> data
|
57
|
+
|
58
|
+
meta.result.push(meta.last_entry)
|
59
|
+
meta.last_entry = [] if last_char != -1
|
60
|
+
meta.current_col = 0
|
61
|
+
meta.current_row += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
cfunc object ENCODED_STR_NEW(char* string, int length)
|
65
|
+
return rb_str_new(string, length)
|
66
|
+
end
|
67
|
+
|
68
|
+
cfunc void setup_rcsv_metadata_defaults(rcsv_metadata *p_meta)
|
69
|
+
p_meta.skip_current_row = 0
|
70
|
+
p_meta.current_col = 0
|
71
|
+
p_meta.current_row = 0
|
72
|
+
p_meta.result = []
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.parse(file_name, opts)
|
76
|
+
rcsv_metadata meta
|
77
|
+
object csvio, options, option
|
78
|
+
csv_parser cp
|
79
|
+
ensure_container = []
|
80
|
+
unsigned char csv_options = CSV_STRICT_FINI | CSV_APPEND_NULL
|
81
|
+
int csv_string_len
|
82
|
+
|
83
|
+
setup_rcsv_metadata_defaults(&meta)
|
84
|
+
csvio = StringIO.new(file_name)
|
85
|
+
|
86
|
+
if csv_init(&cp, csv_options) == -1
|
87
|
+
raise(RcsvParseError, "Failed to initialize libcsv.")
|
88
|
+
end
|
89
|
+
meta.last_entry = []
|
90
|
+
|
91
|
+
begin
|
92
|
+
while true do
|
93
|
+
csvstr = csvio.read
|
94
|
+
|
95
|
+
break if csvstr.nil? || csvstr.size == 0
|
96
|
+
|
97
|
+
char* csv_string = csvstr
|
98
|
+
csv_string_len = csvstr.size
|
99
|
+
|
100
|
+
if csv_string_len != csv_parse(&cp, csv_string, csv_string_len, &end_of_field_callback, &end_of_line_callback, &meta)
|
101
|
+
int error = csv_error(&cp)
|
102
|
+
|
103
|
+
if error == CSV_EPARSE
|
104
|
+
raise(RcsvParseError, "Error when parsing malformed data.")
|
105
|
+
elsif error == CSV_ENOMEM
|
106
|
+
raise(RcsvParseError, "No memory.")
|
107
|
+
elsif error == CSV_ETOOBIG
|
108
|
+
raise(RcsvParseError, "Field data data is too large.")
|
109
|
+
elsif error == CSV_EINVALID
|
110
|
+
raise(RcsvParseError)
|
111
|
+
else
|
112
|
+
raise(RcsvParseError, "Something went wrong.")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
csv_fini(&cp, &end_of_field_callback, &end_of_line_callback, &meta)
|
118
|
+
ensure
|
119
|
+
csv_free(&cp) if &cp
|
120
|
+
end
|
121
|
+
|
122
|
+
return meta.result
|
123
|
+
end
|
124
|
+
end
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Rcsv::VERSION = "0.1"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require 'rcsv/version.rb'
|
5
|
+
|
6
|
+
Rcsv::DESCRIPTION = <<MSG
|
7
|
+
A Crystal-inspired language for writing Ruby extensions
|
8
|
+
MSG
|
9
|
+
|
10
|
+
Gem::Specification.new do |spec|
|
11
|
+
spec.name = 'rcsv'
|
12
|
+
spec.version = Rcsv::VERSION
|
13
|
+
spec.authors = ['Sameer Deshmukh']
|
14
|
+
spec.email = ['sameer.deshmukh93@gmail.com']
|
15
|
+
spec.summary = Rcsv::DESCRIPTION
|
16
|
+
spec.description = Rcsv::DESCRIPTION
|
17
|
+
spec.license = 'BSD-2'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'rubex', '~> 0.1'
|
25
|
+
spec.add_development_dependency 'rake', '~> 11.2'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Rubex wrapper over libcsv" do
|
4
|
+
context "Rcsv" do
|
5
|
+
it ".p" do
|
6
|
+
expect(Rcsv.p("spec/rcsv.csv")).to eq(
|
7
|
+
[
|
8
|
+
["Name", "age", "sex"],
|
9
|
+
["Sameer", "24", "M"],
|
10
|
+
["Ameya", "23", "M"],
|
11
|
+
["Neeraja", "23", "F"],
|
12
|
+
["Shounak", "24", "M"]
|
13
|
+
]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -11,13 +11,13 @@ NULLCMD = :
|
|
11
11
|
|
12
12
|
#### Start of system configuration section. ####
|
13
13
|
|
14
|
-
srcdir =
|
15
|
-
topdir = /home/sameer/.rvm/rubies/ruby-2.3.
|
14
|
+
srcdir = ../../../../ext/rcsv
|
15
|
+
topdir = /home/sameer/.rvm/rubies/ruby-2.3.3/include/ruby-2.3.0
|
16
16
|
hdrdir = $(topdir)
|
17
|
-
arch_hdrdir = /home/sameer/.rvm/rubies/ruby-2.3.
|
17
|
+
arch_hdrdir = /home/sameer/.rvm/rubies/ruby-2.3.3/include/ruby-2.3.0/x86_64-linux
|
18
18
|
PATH_SEPARATOR = :
|
19
19
|
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
20
|
-
prefix = $(DESTDIR)/home/sameer/.rvm/rubies/ruby-2.3.
|
20
|
+
prefix = $(DESTDIR)/home/sameer/.rvm/rubies/ruby-2.3.3
|
21
21
|
rubysitearchprefix = $(rubylibprefix)/$(sitearch)
|
22
22
|
rubyarchprefix = $(rubylibprefix)/$(arch)
|
23
23
|
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
@@ -54,7 +54,7 @@ oldincludedir = $(DESTDIR)/usr/include
|
|
54
54
|
includedir = $(prefix)/include
|
55
55
|
localstatedir = $(prefix)/var
|
56
56
|
sharedstatedir = $(prefix)/com
|
57
|
-
sysconfdir = $(
|
57
|
+
sysconfdir = $(DESTDIR)/etc
|
58
58
|
datadir = $(datarootdir)
|
59
59
|
datarootdir = $(prefix)/share
|
60
60
|
libexecdir = $(exec_prefix)/libexec
|
@@ -67,8 +67,8 @@ CC = gcc
|
|
67
67
|
CXX = g++
|
68
68
|
LIBRUBY = $(LIBRUBY_SO)
|
69
69
|
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
70
|
-
LIBRUBYARG_SHARED = -Wl,-
|
71
|
-
LIBRUBYARG_STATIC = -Wl,-
|
70
|
+
LIBRUBYARG_SHARED = -Wl,-rpath,'$${ORIGIN}/../lib' -Wl,-R'$${ORIGIN}/../lib' -l$(RUBY_SO_NAME)
|
71
|
+
LIBRUBYARG_STATIC = -Wl,-rpath,'$${ORIGIN}/../lib' -Wl,-R'$${ORIGIN}/../lib' -l$(RUBY_SO_NAME)-static
|
72
72
|
empty =
|
73
73
|
OUTFLAG = -o $(empty)
|
74
74
|
COUTFLAG = -o $(empty)
|
@@ -78,11 +78,11 @@ cflags = $(optflags) $(debugflags) $(warnflags)
|
|
78
78
|
cxxflags = $(optflags) $(debugflags) $(warnflags)
|
79
79
|
optflags = -O3 -fno-fast-math
|
80
80
|
debugflags = -ggdb3
|
81
|
-
warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration -Wdeprecated-declarations -Wno-packed-bitfield-compat
|
81
|
+
warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration -Wdeprecated-declarations -Wno-packed-bitfield-compat -Wno-maybe-uninitialized
|
82
82
|
CCDLFLAGS = -fPIC
|
83
83
|
CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC $(ARCH_FLAG)
|
84
84
|
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
85
|
-
DEFS =
|
85
|
+
DEFS =
|
86
86
|
CPPFLAGS = $(DEFS) $(cppflags)
|
87
87
|
CXXFLAGS = $(CCDLFLAGS) $(cxxflags) $(ARCH_FLAG)
|
88
88
|
ldflags = -L. -fstack-protector -rdynamic -Wl,-export-dynamic
|
@@ -101,7 +101,7 @@ RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
|
101
101
|
RUBYW_BASE_NAME = rubyw
|
102
102
|
RUBY_BASE_NAME = ruby
|
103
103
|
|
104
|
-
arch =
|
104
|
+
arch = x86_64-linux
|
105
105
|
sitearch = $(arch)
|
106
106
|
ruby_version = 2.3.0
|
107
107
|
ruby = $(bindir)/$(RUBY_BASE_NAME)
|
@@ -112,7 +112,7 @@ RM = rm -f
|
|
112
112
|
RM_RF = $(RUBY) -run -e rm -- -rf
|
113
113
|
RMDIRS = rmdir --ignore-fail-on-non-empty -p
|
114
114
|
MAKEDIRS = /bin/mkdir -p
|
115
|
-
INSTALL = /usr/bin/install
|
115
|
+
INSTALL = /usr/bin/install
|
116
116
|
INSTALL_PROG = $(INSTALL) -m 0755
|
117
117
|
INSTALL_DATA = $(INSTALL) -m 644
|
118
118
|
COPY = cp
|
@@ -132,15 +132,15 @@ DISTCLEANDIRS =
|
|
132
132
|
|
133
133
|
extout =
|
134
134
|
extout_prefix =
|
135
|
-
target_prefix = /
|
135
|
+
target_prefix = /ext/rcsv
|
136
136
|
LOCAL_LIBS =
|
137
|
-
LIBS = $(LIBRUBYARG_SHARED) -lpthread -lgmp -ldl -lcrypt -lm -lc
|
138
|
-
ORIG_SRCS =
|
137
|
+
LIBS = $(LIBRUBYARG_SHARED) -lcsv -lpthread -lgmp -ldl -lcrypt -lm -lc
|
138
|
+
ORIG_SRCS = rcsv.c
|
139
139
|
SRCS = $(ORIG_SRCS)
|
140
|
-
OBJS =
|
140
|
+
OBJS = rcsv.o
|
141
141
|
HDRS =
|
142
|
-
TARGET =
|
143
|
-
TARGET_NAME =
|
142
|
+
TARGET = rcsv
|
143
|
+
TARGET_NAME = rcsv
|
144
144
|
TARGET_ENTRY = Init_$(TARGET_NAME)
|
145
145
|
DLLIB = $(TARGET).so
|
146
146
|
EXTSTATIC =
|
@@ -182,7 +182,7 @@ distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
|
182
182
|
realclean: distclean
|
183
183
|
install: install-so install-rb
|
184
184
|
|
185
|
-
install-so: $(DLLIB) $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.
|
185
|
+
install-so: $(DLLIB) $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.ext.-.rcsv.time
|
186
186
|
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
187
187
|
clean-static::
|
188
188
|
-$(Q)$(RM) $(STATIC_LIB)
|
@@ -192,7 +192,7 @@ pre-install-rb: Makefile
|
|
192
192
|
pre-install-rb-default: Makefile
|
193
193
|
pre-install-rb-default:
|
194
194
|
@$(NULLCMD)
|
195
|
-
$(TIMESTAMP_DIR)/.RUBYARCHDIR.-.
|
195
|
+
$(TIMESTAMP_DIR)/.RUBYARCHDIR.-.ext.-.rcsv.time:
|
196
196
|
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
197
197
|
$(Q) $(TOUCH) $@
|
198
198
|
|
@@ -251,7 +251,7 @@ site-install-rb: install-rb
|
|
251
251
|
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<
|
252
252
|
|
253
253
|
$(DLLIB): $(OBJS) Makefile
|
254
|
-
$(ECHO) linking shared-object
|
254
|
+
$(ECHO) linking shared-object ext/rcsv/$(DLLIB)
|
255
255
|
-$(Q)$(RM) $(@)
|
256
256
|
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
257
257
|
|
Binary file
|
Binary file
|
Binary file
|
data/lib/rubex.rb
CHANGED
@@ -1,56 +1,12 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'rubex/error'
|
4
|
+
require 'rubex/helpers'
|
1
5
|
require 'rubex/code_writer'
|
2
6
|
require 'rubex/data_type'
|
3
7
|
require 'rubex/constants'
|
4
8
|
require 'rubex/ast'
|
5
9
|
require 'rubex/symbol_table'
|
6
10
|
require 'rubex/parser.racc.rb'
|
7
|
-
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def compile path, test=false
|
11
|
-
tree = ast path
|
12
|
-
target_name = extract_target_name path
|
13
|
-
code = generate_code tree, target_name
|
14
|
-
ext = extconf target_name
|
15
|
-
|
16
|
-
return [code, ext] if test
|
17
|
-
write_files target_name, code, ext
|
18
|
-
end
|
19
|
-
|
20
|
-
def ast path
|
21
|
-
parser = Rubex::Parser.new
|
22
|
-
parser.parse(path)
|
23
|
-
parser.do_parse
|
24
|
-
end
|
25
|
-
|
26
|
-
def extconf target_name, dir=nil
|
27
|
-
extconf = ""
|
28
|
-
extconf << "require 'mkmf'\n"
|
29
|
-
extconf << "create_makefile('#{target_name}/#{target_name}')\n"
|
30
|
-
extconf
|
31
|
-
end
|
32
|
-
|
33
|
-
def generate_code tree, target_name
|
34
|
-
code = Rubex::CodeWriter.new target_name
|
35
|
-
raise "Must be a Rubex::AST::Node, not #{tree.class}" unless
|
36
|
-
tree.is_a? Rubex::AST::Node
|
37
|
-
tree.process_statements target_name, code
|
38
|
-
code
|
39
|
-
end
|
40
|
-
|
41
|
-
def extract_target_name path
|
42
|
-
File.basename(path).split('.')[0]
|
43
|
-
end
|
44
|
-
|
45
|
-
def write_files target_name, code, ext
|
46
|
-
Dir.mkdir(target_name)
|
47
|
-
code_file = File.new "#{target_name}/#{target_name}.c", "w+"
|
48
|
-
code_file.puts code.to_s
|
49
|
-
code_file.close
|
50
|
-
|
51
|
-
extconf_file = File.new "#{target_name}/extconf.rb", "w+"
|
52
|
-
extconf_file.puts ext
|
53
|
-
extconf_file.close
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
11
|
+
require 'rubex/compiler_config'
|
12
|
+
require 'rubex/compiler'
|
data/lib/rubex/ast.rb
CHANGED
data/lib/rubex/ast/expression.rb
CHANGED
@@ -1,13 +1,1262 @@
|
|
1
1
|
module Rubex
|
2
2
|
module AST
|
3
|
-
|
4
|
-
class
|
5
|
-
|
3
|
+
module Expression
|
4
|
+
class Base
|
5
|
+
attr_accessor :typecast
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# In case an expr has to be of a certain type, like a string literal
|
8
|
+
# assigned to a char*, this method will analyse the literal in context
|
9
|
+
# to the target dtype.
|
10
|
+
def analyse_for_target_type target_type, local_scope
|
11
|
+
analyse_statement local_scope
|
9
12
|
end
|
13
|
+
|
14
|
+
# If the typecast exists, the typecast is made the overall type of
|
15
|
+
# the expression.
|
16
|
+
def analyse_statement local_scope
|
17
|
+
if @typecast
|
18
|
+
@typecast.analyse_statement(local_scope)
|
19
|
+
@type = @typecast.type
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def expression?; true; end
|
24
|
+
|
25
|
+
def c_code local_scope
|
26
|
+
@typecast ? @typecast.c_code(local_scope) : ""
|
27
|
+
end
|
28
|
+
|
29
|
+
def possible_typecast code, local_scope
|
30
|
+
@typecast ? @typecast.c_code(local_scope) : ""
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ruby_object
|
34
|
+
ToRubyObject.new self
|
35
|
+
end
|
36
|
+
|
37
|
+
def from_ruby_object from_node
|
38
|
+
FromRubyObject.new self, from_node
|
39
|
+
end
|
40
|
+
|
41
|
+
def release_temp local_scope
|
42
|
+
local_scope.release_temp(@c_code) if @has_temp
|
43
|
+
end
|
44
|
+
|
45
|
+
def allocate_temp local_scope, type
|
46
|
+
if @has_temp
|
47
|
+
@c_code = local_scope.allocate_temp(type)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def allocate_temps local_scope
|
52
|
+
if @subexprs
|
53
|
+
@subexprs.each { |expr| expr.allocate_temp(local_scope, expr.type) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def release_temps local_scope
|
58
|
+
if @subexprs
|
59
|
+
@subexprs.each { |expr| expr.release_temp(local_scope) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_evaluation_code code, local_scope
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_disposal_code code
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_assignment_code rhs, code, local_scope
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Typecast < Base
|
77
|
+
attr_reader :type
|
78
|
+
|
79
|
+
def initialize dtype, ptr_level
|
80
|
+
@dtype, @ptr_level = dtype, ptr_level
|
81
|
+
end
|
82
|
+
|
83
|
+
def analyse_statement local_scope
|
84
|
+
@type = Rubex::Helpers.determine_dtype @dtype, @ptr_level
|
85
|
+
end
|
86
|
+
|
87
|
+
def c_code local_scope
|
88
|
+
"(#{@type.to_s})"
|
89
|
+
end
|
90
|
+
end # class Typecast
|
91
|
+
|
92
|
+
class SizeOf < Base
|
93
|
+
attr_reader :type
|
94
|
+
|
95
|
+
def initialize type, ptr_level
|
96
|
+
@size_of_type = Helpers.determine_dtype type, ptr_level
|
97
|
+
end
|
98
|
+
|
99
|
+
def analyse_statement local_scope
|
100
|
+
@type = DataType::ULInt.new
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
def c_code local_scope
|
105
|
+
"sizeof(#{@size_of_type})"
|
106
|
+
end
|
107
|
+
end # class SizeOf
|
108
|
+
|
109
|
+
class Binary < Base
|
110
|
+
include Rubex::Helpers::NodeTypeMethods
|
111
|
+
|
112
|
+
attr_reader :operator
|
113
|
+
attr_accessor :left, :right
|
114
|
+
# Final return type of expression
|
115
|
+
attr_accessor :type, :subexprs
|
116
|
+
|
117
|
+
def initialize left, operator, right
|
118
|
+
@left, @operator, @right = left, operator, right
|
119
|
+
@@analyse_visited = []
|
120
|
+
@subexprs = []
|
121
|
+
end
|
122
|
+
|
123
|
+
def analyse_statement local_scope
|
124
|
+
analyse_left_and_right_nodes local_scope, self
|
125
|
+
analyse_return_type local_scope, self
|
126
|
+
super
|
127
|
+
end
|
128
|
+
|
129
|
+
def allocate_temps local_scope
|
130
|
+
@subexprs.each do |expr|
|
131
|
+
if expr.is_a?(Binary)
|
132
|
+
expr.allocate_temps local_scope
|
133
|
+
else
|
134
|
+
expr.allocate_temp local_scope, expr.type
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def generate_evaluation_code code, local_scope
|
140
|
+
@left.generate_evaluation_code code, local_scope
|
141
|
+
@right.generate_evaluation_code code, local_scope
|
142
|
+
end
|
143
|
+
|
144
|
+
def generate_disposal_code code
|
145
|
+
@left.generate_disposal_code code
|
146
|
+
@right.generate_disposal_code code
|
147
|
+
end
|
148
|
+
|
149
|
+
def c_code local_scope
|
150
|
+
code = super
|
151
|
+
code << "( "
|
152
|
+
left_code = @left.c_code(local_scope)
|
153
|
+
right_code = @right.c_code(local_scope)
|
154
|
+
if type_of(@left).object? || type_of(@right).object?
|
155
|
+
left_ruby_code = @left.type.to_ruby_object(left_code)
|
156
|
+
right_ruby_code = @right.type.to_ruby_object(right_code)
|
157
|
+
|
158
|
+
if ["&&", "||"].include?(@operator)
|
159
|
+
code << Rubex::C_MACRO_INT2BOOL +
|
160
|
+
"(RTEST(#{left_ruby_code}) #{@operator} RTEST(#{right_ruby_code}))"
|
161
|
+
else
|
162
|
+
code << "rb_funcall(#{left_ruby_code}, rb_intern(\"#{@operator}\") "
|
163
|
+
code << ", 1, #{right_ruby_code})"
|
164
|
+
end
|
165
|
+
else
|
166
|
+
code << "#{left_code} #{@operator} #{right_code}"
|
167
|
+
end
|
168
|
+
code << " )"
|
169
|
+
|
170
|
+
code
|
171
|
+
end
|
172
|
+
|
173
|
+
def == other
|
174
|
+
self.class == other.class && @type == other.type &&
|
175
|
+
@left == other.left && @right == other.right &&
|
176
|
+
@operator == other.operator
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def type_of expr
|
182
|
+
t = expr.type
|
183
|
+
return (t.c_function? ? t.type : t)
|
184
|
+
end
|
185
|
+
|
186
|
+
def analyse_left_and_right_nodes local_scope, tree
|
187
|
+
if tree.respond_to?(:left)
|
188
|
+
analyse_left_and_right_nodes local_scope, tree.left
|
189
|
+
|
190
|
+
if !@@analyse_visited.include?(tree.left.object_id)
|
191
|
+
if tree.right.type
|
192
|
+
tree.left.analyse_for_target_type(tree.right.type, local_scope)
|
193
|
+
else
|
194
|
+
tree.left.analyse_statement(local_scope)
|
195
|
+
end
|
196
|
+
@subexprs << tree.left
|
197
|
+
@@analyse_visited << tree.left.object_id
|
198
|
+
end
|
199
|
+
|
200
|
+
if !@@analyse_visited.include?(tree.right.object_id)
|
201
|
+
if tree.left.type
|
202
|
+
tree.right.analyse_for_target_type(tree.left.type, local_scope)
|
203
|
+
else
|
204
|
+
tree.right.analyse_statement(local_scope)
|
205
|
+
end
|
206
|
+
@subexprs << tree.right
|
207
|
+
@@analyse_visited << tree.right.object_id
|
208
|
+
end
|
209
|
+
|
210
|
+
@@analyse_visited << tree.object_id
|
211
|
+
|
212
|
+
analyse_left_and_right_nodes local_scope, tree.right
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def analyse_return_type local_scope, tree
|
217
|
+
if tree.respond_to? :left
|
218
|
+
analyse_return_type local_scope, tree.left
|
219
|
+
analyse_return_type local_scope, tree.right
|
220
|
+
|
221
|
+
if ['==', '<', '>', '<=', '>=', '||', '&&', '!='].include? tree.operator
|
222
|
+
if type_of(tree.left).object? || type_of(tree.right).object?
|
223
|
+
tree.type = Rubex::DataType::Boolean.new
|
224
|
+
else
|
225
|
+
tree.type = Rubex::DataType::CBoolean.new
|
226
|
+
end
|
227
|
+
else
|
228
|
+
if tree.left.type.bool? || tree.right.type.bool?
|
229
|
+
raise Rubex::TypeMismatchError, "Operation #{tree.operator} cannot"\
|
230
|
+
"be performed between #{tree.left} and #{tree.right}"
|
231
|
+
end
|
232
|
+
tree.type = Rubex::Helpers.result_type_for(
|
233
|
+
type_of(tree.left), type_of(tree.right))
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end # class Binary
|
238
|
+
|
239
|
+
class UnaryBase < Base
|
240
|
+
def initialize expr
|
241
|
+
@expr = expr
|
242
|
+
end
|
243
|
+
|
244
|
+
def analyse_statement local_scope
|
245
|
+
@expr.analyse_statement local_scope
|
246
|
+
@type = @expr.type
|
247
|
+
@expr.allocate_temps local_scope
|
248
|
+
@expr.allocate_temp local_scope, @type
|
249
|
+
@expr.release_temps local_scope
|
250
|
+
@expr.release_temp local_scope
|
251
|
+
@expr = @expr.to_ruby_object if @type.object?
|
252
|
+
end
|
253
|
+
|
254
|
+
def generate_evaluation_code code, local_scope
|
255
|
+
@expr.generate_evaluation_code code, local_scope
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class UnaryNot < UnaryBase
|
260
|
+
attr_reader :type
|
261
|
+
|
262
|
+
def c_code local_scope
|
263
|
+
code = @expr.c_code(local_scope)
|
264
|
+
if @type.object?
|
265
|
+
"rb_funcall(#{code}, rb_intern(\"!\"), 0)"
|
266
|
+
else
|
267
|
+
"!#{code}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class UnarySub < UnaryBase
|
273
|
+
attr_reader :type
|
274
|
+
|
275
|
+
def c_code local_scope
|
276
|
+
code = @expr.c_code(local_scope)
|
277
|
+
if @type.object?
|
278
|
+
"rb_funcall(#{code}, rb_intern(\"-\"), 0)"
|
279
|
+
else
|
280
|
+
"-#{code}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
class Ampersand < UnaryBase
|
286
|
+
attr_reader :type
|
287
|
+
|
288
|
+
def analyse_statement local_scope
|
289
|
+
@expr.analyse_statement local_scope
|
290
|
+
@type = DataType::CPtr.new @expr.type
|
291
|
+
end
|
292
|
+
|
293
|
+
def c_code local_scope
|
294
|
+
"&#{@expr.c_code(local_scope)}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class UnaryBitNot < UnaryBase
|
299
|
+
attr_reader :type
|
300
|
+
|
301
|
+
def c_code local_scope
|
302
|
+
code = @expr.c_code(local_scope)
|
303
|
+
if @type.object?
|
304
|
+
"rb_funcall(#{code}, rb_intern(\"~\"), 0)"
|
305
|
+
else
|
306
|
+
"~#{code}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class Unary < Base
|
312
|
+
attr_reader :operator, :expr, :type
|
313
|
+
|
314
|
+
OP_CLASS_MAP = {
|
315
|
+
'&' => Ampersand,
|
316
|
+
'-' => UnarySub,
|
317
|
+
'!' => UnaryNot,
|
318
|
+
'~' => UnaryBitNot
|
319
|
+
}
|
320
|
+
|
321
|
+
def initialize operator, expr
|
322
|
+
@operator, @expr = operator, expr
|
323
|
+
end
|
324
|
+
|
325
|
+
def analyse_statement local_scope
|
326
|
+
@expr = OP_CLASS_MAP[@operator].new(@expr)
|
327
|
+
@expr.analyse_statement local_scope
|
328
|
+
@type = @expr.type
|
329
|
+
super
|
330
|
+
end
|
331
|
+
|
332
|
+
def generate_evaluation_code code, local_scope
|
333
|
+
@expr.generate_evaluation_code code, local_scope
|
334
|
+
end
|
335
|
+
|
336
|
+
def c_code local_scope
|
337
|
+
code = super
|
338
|
+
code << @expr.c_code(local_scope)
|
339
|
+
end
|
340
|
+
end # class Unary
|
341
|
+
|
342
|
+
class ElementRef < Base
|
343
|
+
attr_reader :entry, :pos, :type, :name, :object_ptr
|
344
|
+
|
345
|
+
def initialize name, pos
|
346
|
+
@name, @pos = name, pos
|
347
|
+
@subexprs = []
|
348
|
+
end
|
349
|
+
|
350
|
+
# FIXME: This method needs to be implemented for all exprs that are
|
351
|
+
# possible LHS candidates.
|
352
|
+
# def analyse_declaration rhs, local_scope
|
353
|
+
# analyse_statement local_scope
|
354
|
+
# @has_temp = false
|
355
|
+
# end
|
356
|
+
|
357
|
+
def analyse_statement local_scope, struct_scope=nil
|
358
|
+
if struct_scope.nil?
|
359
|
+
@entry = local_scope.find @name
|
360
|
+
else
|
361
|
+
@entry = struct_scope[@name]
|
362
|
+
end
|
363
|
+
|
364
|
+
@object_ptr = true if @entry.type.cptr? && @entry.type.type.object?
|
365
|
+
@type = @entry.type.object? ? @entry.type : @entry.type.type
|
366
|
+
|
367
|
+
if @type.object? && !@object_ptr
|
368
|
+
@has_temp = true
|
369
|
+
@pos.analyse_statement local_scope
|
370
|
+
if !(@type.ruby_array?)
|
371
|
+
@pos = @pos.to_ruby_object
|
372
|
+
end
|
373
|
+
@subexprs << @pos
|
374
|
+
else
|
375
|
+
@pos.analyse_statement local_scope
|
376
|
+
end
|
377
|
+
super(local_scope)
|
378
|
+
end
|
379
|
+
|
380
|
+
# This method will be called when [] ruby method or C array element
|
381
|
+
# reference is called.
|
382
|
+
# TODO: refactor this by creating separate classes for ruby object, object
|
383
|
+
# ptr, c type.
|
384
|
+
def generate_evaluation_code code, local_scope
|
385
|
+
if @type.object? && !@object_ptr
|
386
|
+
if @type.ruby_array?
|
387
|
+
code << "#{@c_code} = RARRAY_AREF(#{@entry.c_name}, #{@pos.c_code(local_scope)});"
|
388
|
+
elsif @type.ruby_hash?
|
389
|
+
@pos.generate_evaluation_code code, local_scope
|
390
|
+
code << "#{@c_code} = rb_hash_aref(#{@entry.c_name}, #{@pos.c_code(local_scope)});"
|
391
|
+
else
|
392
|
+
@pos.generate_evaluation_code code, local_scope
|
393
|
+
code << "#{@c_code} = rb_funcall(#{@entry.c_name}, rb_intern(\"[]\"), 1, "
|
394
|
+
code << "#{@pos.c_code(local_scope)});"
|
395
|
+
end
|
396
|
+
code.nl
|
397
|
+
@pos.generate_disposal_code code
|
398
|
+
else
|
399
|
+
@pos.generate_evaluation_code code, local_scope
|
400
|
+
@c_code = "#{@entry.c_name}[#{@pos.c_code(local_scope)}]"
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def generate_disposal_code code
|
405
|
+
if @type.object? && !@object_ptr
|
406
|
+
code << "#{@c_code} = 0;"
|
407
|
+
code.nl
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# This method will be called when []= ruby method or C array assignment
|
412
|
+
# takes place.
|
413
|
+
def generate_assignment_code rhs, code, local_scope
|
414
|
+
if @type.object? && !@object_ptr
|
415
|
+
@pos.generate_evaluation_code code, local_scope
|
416
|
+
if @type.ruby_hash?
|
417
|
+
code << "rb_hash_aset(#{@entry.c_name}, #{@pos.c_code(local_scope)}, #{rhs.c_code(local_scope)});"
|
418
|
+
else
|
419
|
+
code << "rb_funcall(#{@entry.c_name}, rb_intern(\"[]=\"), 2, "
|
420
|
+
code << "#{@pos.c_code(local_scope)}, #{rhs.c_code(local_scope)});"
|
421
|
+
end
|
422
|
+
@pos.generate_disposal_code code
|
423
|
+
else
|
424
|
+
code << "#{@entry.c_name}[#{@pos.c_code(local_scope)}] = "
|
425
|
+
code << "#{rhs.c_code(local_scope)};"
|
426
|
+
end
|
427
|
+
code.nl
|
428
|
+
end
|
429
|
+
|
430
|
+
# FIXME: This is jugaad. Change.
|
431
|
+
def generate_element_ref_code expr, code, local_scope
|
432
|
+
if !@object_ptr
|
433
|
+
@pos.generate_evaluation_code code, local_scope
|
434
|
+
str = "#{@c_code} = rb_funcall(#{expr.c_code(local_scope)}."
|
435
|
+
str << "#{@entry.c_name}, rb_intern(\"[]\"), 1, "
|
436
|
+
str << "#{@pos.c_code(local_scope)});"
|
437
|
+
code << str
|
438
|
+
code.nl
|
439
|
+
@pos.generate_disposal_code code
|
440
|
+
else
|
441
|
+
generate_evaluation_code code, local_scope
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def c_code local_scope
|
446
|
+
code = super
|
447
|
+
code << @c_code
|
448
|
+
code
|
449
|
+
end
|
450
|
+
end # class ElementRef
|
451
|
+
|
452
|
+
class Self < Base
|
453
|
+
def c_code local_scope
|
454
|
+
local_scope.self_name
|
455
|
+
end
|
456
|
+
|
457
|
+
def type
|
458
|
+
Rubex::DataType::RubyObject.new
|
459
|
+
end
|
460
|
+
end # class Self
|
461
|
+
|
462
|
+
class RubyConstant < Base
|
463
|
+
include Rubex::AST::Expression
|
464
|
+
attr_reader :name, :entry, :type
|
465
|
+
|
466
|
+
def initialize name
|
467
|
+
@name = name
|
468
|
+
end
|
469
|
+
|
470
|
+
def analyse_statement local_scope
|
471
|
+
@type = Rubex::DataType::RubyConstant.new @name
|
472
|
+
c_name = Rubex::DEFAULT_CLASS_MAPPINGS[@name]
|
473
|
+
@entry = Rubex::SymbolTable::Entry.new name, c_name, @type, nil
|
474
|
+
end
|
475
|
+
|
476
|
+
def c_code local_scope
|
477
|
+
if @entry.c_name # built-in constant.
|
478
|
+
@entry.c_name
|
479
|
+
else
|
480
|
+
"rb_const_get(CLASS_OF(#{local_scope.self_name}), rb_intern(\"#{@entry.name}\"))"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end # class RubyConstant
|
484
|
+
|
485
|
+
# Singular name node with no sub expressions.
|
486
|
+
class Name < Base
|
487
|
+
attr_reader :name, :entry, :type
|
488
|
+
|
489
|
+
def initialize name
|
490
|
+
@name = name
|
491
|
+
end
|
492
|
+
|
493
|
+
# Used when the node is a LHS of an assign statement.
|
494
|
+
def analyse_declaration rhs, local_scope
|
495
|
+
@entry = local_scope.find @name
|
496
|
+
unless @entry
|
497
|
+
local_scope.add_ruby_obj(name: @name,
|
498
|
+
c_name: Rubex::VAR_PREFIX + @name, value: @rhs)
|
499
|
+
@entry = local_scope[@name]
|
500
|
+
end
|
501
|
+
@type = @entry.type
|
502
|
+
end
|
503
|
+
|
504
|
+
def analyse_for_target_type target_type, local_scope
|
505
|
+
@entry = local_scope.find @name
|
506
|
+
|
507
|
+
if @entry && @entry.type.c_function? && target_type.c_function_ptr?
|
508
|
+
@type = @entry.type
|
509
|
+
else
|
510
|
+
analyse_statement local_scope
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
# Analyse a Name node. This can either be a variable name or a method call
|
515
|
+
# without parenthesis. Code in this method that creates a CommandCall
|
516
|
+
# node primarily exists because in Ruby methods without arguments can
|
517
|
+
# be called without parentheses. These names can potentially be Ruby
|
518
|
+
# methods that are not visible to Rubex, but are present in the Ruby
|
519
|
+
# run time.
|
520
|
+
def analyse_statement local_scope
|
521
|
+
@entry = local_scope.find @name
|
522
|
+
# FIXME: Figure out a way to perform compile time checking of expressions
|
523
|
+
# to see if the said Ruby methods are actually present in the Ruby
|
524
|
+
# runtime. Maybe read symbols in the Ruby interpreter and load them
|
525
|
+
# as a pre-compilation step?
|
526
|
+
|
527
|
+
# If entry is not present, assume its a Ruby method call to some method
|
528
|
+
# outside of the current Rubex scope or a Ruby constant.
|
529
|
+
if !@entry
|
530
|
+
# Check if first letter is a capital to check for Ruby constant.
|
531
|
+
if @name[0].match /[A-Z]/
|
532
|
+
@name = Expression::RubyConstant.new @name
|
533
|
+
@name.analyse_statement local_scope
|
534
|
+
@entry = @name.entry
|
535
|
+
else # extern Ruby method
|
536
|
+
@entry = local_scope.add_ruby_method(
|
537
|
+
name: @name,
|
538
|
+
c_name: @name,
|
539
|
+
extern: true,
|
540
|
+
scope: nil,
|
541
|
+
arg_list: [])
|
542
|
+
end
|
543
|
+
end
|
544
|
+
# If the entry is a RubyMethod, it should be interpreted as a command
|
545
|
+
# call. So, make the @name a CommandCall Node.
|
546
|
+
if @entry.type.ruby_method? #|| @entry.type.c_function?
|
547
|
+
@name = Rubex::AST::Expression::CommandCall.new(
|
548
|
+
Expression::Self.new, @name, [])
|
549
|
+
@name.analyse_statement local_scope
|
550
|
+
end
|
551
|
+
|
552
|
+
if @entry.type.alias_type? || @entry.type.ruby_method? ||
|
553
|
+
@entry.type.c_function?
|
554
|
+
@type = @entry.type.type
|
555
|
+
else
|
556
|
+
@type = @entry.type
|
557
|
+
end
|
558
|
+
super
|
559
|
+
end
|
560
|
+
|
561
|
+
def generate_evaluation_code code, local_scope
|
562
|
+
if @name.respond_to? :generate_evaluation_code
|
563
|
+
@name.generate_evaluation_code code, local_scope
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
def generate_disposal_code code
|
568
|
+
if @name.respond_to? :generate_disposal_code
|
569
|
+
@name.generate_disposal_code code
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
def generate_assignment_code rhs, code, local_scope
|
574
|
+
code << "#{self.c_code(local_scope)} = #{rhs.c_code(local_scope)};"
|
575
|
+
code.nl
|
576
|
+
rhs.generate_disposal_code code
|
577
|
+
end
|
578
|
+
|
579
|
+
def c_code local_scope
|
580
|
+
code = super
|
581
|
+
if @name.is_a?(Rubex::AST::Expression::Base)
|
582
|
+
code << @name.c_code(local_scope)
|
583
|
+
else
|
584
|
+
code << @entry.c_name
|
585
|
+
end
|
586
|
+
|
587
|
+
code
|
588
|
+
end
|
589
|
+
end # class Name
|
590
|
+
|
591
|
+
class MethodCall < Base
|
592
|
+
attr_reader :method_name, :type
|
593
|
+
|
594
|
+
def initialize method_name, invoker, arg_list
|
595
|
+
@method_name, @invoker, @arg_list = method_name, invoker, arg_list
|
596
|
+
end
|
597
|
+
|
598
|
+
# Analyse a method call. If the method that is being called is defined
|
599
|
+
# in a class in a Rubex file, it can easily be interpreted as a Ruby
|
600
|
+
# method. However, in case it is not, a new symtab entry will be created
|
601
|
+
# which will mark the method as 'extern' so that future calls to that
|
602
|
+
# same method can be simply pulled from the symtab.
|
603
|
+
# local_scope is the local method scope.
|
604
|
+
def analyse_statement local_scope
|
605
|
+
entry = local_scope.find(@method_name)
|
606
|
+
if !entry
|
607
|
+
entry = local_scope.add_ruby_method(
|
608
|
+
name: @method_name,
|
609
|
+
c_name: @method_name,
|
610
|
+
extern: true,
|
611
|
+
arg_list: @arg_list,
|
612
|
+
scope: nil)
|
613
|
+
end
|
614
|
+
|
615
|
+
if method_not_within_scope? local_scope
|
616
|
+
raise Rubex::NoMethodError, "Cannot call #{@name} from this method."
|
617
|
+
end
|
618
|
+
|
619
|
+
# FIXME: Print a warning during compilation if a symbol is being
|
620
|
+
# interpreted as a Ruby method call due it not being found in the
|
621
|
+
# symbol table.
|
622
|
+
|
623
|
+
# A symtab entry for a predeclared extern C func.
|
624
|
+
if entry && entry.type.base_type.c_function?
|
625
|
+
@type = entry.type.base_type
|
626
|
+
# All C functions have compulsory last arg as self. This does not
|
627
|
+
# apply to extern functions as they are usually not made for accepting
|
628
|
+
# a VALUE last arg.
|
629
|
+
@arg_list << Expression::Self.new if !entry.extern?
|
630
|
+
type_check_arg_types entry
|
631
|
+
else
|
632
|
+
@type = Rubex::DataType::RubyObject.new
|
633
|
+
end
|
634
|
+
|
635
|
+
if entry.type.ruby_method? && !entry.extern? && @arg_list.size > 0
|
636
|
+
@arg_list_var = entry.c_name + Rubex::ACTUAL_ARGS_SUFFIX
|
637
|
+
|
638
|
+
args_size = entry.type.arg_list&.size || 0
|
639
|
+
local_scope.add_carray(name: @arg_list_var, c_name: @arg_list_var,
|
640
|
+
dimension: Literal::Int.new("#{args_size}"),
|
641
|
+
type: Rubex::DataType::RubyObject.new)
|
642
|
+
end
|
643
|
+
super
|
644
|
+
end
|
645
|
+
|
646
|
+
def generate_evaluation_code code, local_scope
|
647
|
+
end
|
648
|
+
|
649
|
+
def generate_disposal_code code
|
650
|
+
end
|
651
|
+
|
652
|
+
def c_code local_scope
|
653
|
+
code = super
|
654
|
+
entry = local_scope.find(@method_name)
|
655
|
+
if entry.type.ruby_method?
|
656
|
+
code << code_for_ruby_method_call(local_scope)
|
657
|
+
else
|
658
|
+
code << code_for_c_method_call(local_scope, entry)
|
659
|
+
end
|
660
|
+
|
661
|
+
code
|
662
|
+
end
|
663
|
+
|
664
|
+
private
|
665
|
+
|
666
|
+
def type_check_arg_types entry
|
667
|
+
@arg_list.map!.with_index do |arg, idx|
|
668
|
+
Helpers.to_lhs_type(entry.type.base_type.arg_list[idx], arg)
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
# Checks if method being called is of the same type of the caller. For
|
673
|
+
# example, only instance methods can call instance methods and only
|
674
|
+
# class methods can call class methods. C functions are accessible from
|
675
|
+
# both instance methods and class methods.
|
676
|
+
#
|
677
|
+
# Since there is no way to determine whether methods outside the scope
|
678
|
+
# of the compiled Rubex file are singletons or not, Rubex will assume
|
679
|
+
# that they belong to the correct scope and will compile a call to those
|
680
|
+
# methods anyway. Error, if any, will be caught only at runtime.
|
681
|
+
def method_not_within_scope? local_scope
|
682
|
+
entry = local_scope.find @method_name
|
683
|
+
caller_entry = local_scope.find local_scope.name
|
684
|
+
if ( caller_entry.singleton? && entry.singleton?) ||
|
685
|
+
(!caller_entry.singleton? && !entry.singleton?) ||
|
686
|
+
entry.c_function?
|
687
|
+
false
|
688
|
+
else
|
689
|
+
true
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
def code_for_c_method_call local_scope, entry
|
694
|
+
str = "#{entry.c_name}("
|
695
|
+
str << @arg_list.map { |a| a.c_code(local_scope) }.join(",")
|
696
|
+
str << ")"
|
697
|
+
str
|
698
|
+
end
|
699
|
+
|
700
|
+
def code_for_ruby_method_call local_scope
|
701
|
+
entry = local_scope.find @method_name
|
702
|
+
str = ""
|
703
|
+
if entry.extern?
|
704
|
+
str << "rb_funcall(#{@invoker.c_code(local_scope)}, "
|
705
|
+
str << "rb_intern(\"#{@method_name}\"), "
|
706
|
+
str << "#{@arg_list.size}"
|
707
|
+
@arg_list.each do |arg|
|
708
|
+
str << " ,#{arg.type.to_ruby_object(arg.c_code(local_scope))}"
|
709
|
+
end
|
710
|
+
str << ", NULL" if @arg_list.empty?
|
711
|
+
str << ")"
|
712
|
+
else
|
713
|
+
str << populate_method_args_into_value_array(local_scope)
|
714
|
+
str << actual_ruby_method_call(local_scope, entry)
|
715
|
+
end
|
716
|
+
str
|
717
|
+
end
|
718
|
+
|
719
|
+
def actual_ruby_method_call local_scope, entry
|
720
|
+
str = "#{entry.c_name}(#{@arg_list.size}, #{@arg_list_var || "NULL"},"
|
721
|
+
str << "#{local_scope.self_name})"
|
722
|
+
end
|
723
|
+
|
724
|
+
def populate_method_args_into_value_array local_scope
|
725
|
+
str = ""
|
726
|
+
@arg_list.each_with_index do |arg, idx|
|
727
|
+
str = "#{@arg_list_var}[#{idx}] = "
|
728
|
+
str << "#{arg.type.to_ruby_object(arg.c_code(local_scope))}"
|
729
|
+
str << ";\n"
|
730
|
+
end
|
731
|
+
|
732
|
+
str
|
733
|
+
end
|
734
|
+
end # class MethodCall
|
735
|
+
|
736
|
+
class CommandCall < Base
|
737
|
+
attr_reader :expr, :command, :arg_list, :type
|
738
|
+
|
739
|
+
def initialize expr, command, arg_list
|
740
|
+
@expr, @command, @arg_list = expr, command, arg_list
|
741
|
+
@subexprs = []
|
742
|
+
end
|
743
|
+
|
744
|
+
# Analyse the command call. If the @command is found in the symbol table,
|
745
|
+
# it is either a struct member or a method call. If not found, it is
|
746
|
+
# assumed to be a Ruby method call and passed on the MethodCall node
|
747
|
+
# where it is interpreted likewise. The upside is that Rubex can call
|
748
|
+
# arbitrary Ruby methods that are defined in external Ruby scripts and
|
749
|
+
# not visible to Rubex at compile time. The downside is that errors with
|
750
|
+
# such methods will be visible to the programmer only during runtime.
|
751
|
+
def analyse_statement local_scope
|
752
|
+
@arg_list.each do |arg|
|
753
|
+
arg.analyse_statement local_scope
|
754
|
+
@subexprs << arg
|
755
|
+
end
|
756
|
+
# Case for implicit 'self' when a method in the class itself is being called.
|
757
|
+
if @expr.nil?
|
758
|
+
entry = local_scope.find(@command)
|
759
|
+
@expr = Expression::Self.new if entry && !entry.extern?
|
760
|
+
else
|
761
|
+
@expr.analyse_statement(local_scope)
|
762
|
+
@expr.allocate_temps local_scope
|
763
|
+
@expr.allocate_temp local_scope, @expr.type
|
764
|
+
end
|
765
|
+
analyse_command_type local_scope
|
766
|
+
super
|
767
|
+
end
|
768
|
+
|
769
|
+
# FIXME: refactor this method (or class). Too many ifs. Too much jagaad.
|
770
|
+
def generate_evaluation_code code, local_scope
|
771
|
+
@c_code = ""
|
772
|
+
@arg_list.each do |arg|
|
773
|
+
arg.generate_evaluation_code code, local_scope
|
774
|
+
end
|
775
|
+
@expr.generate_evaluation_code(code, local_scope) if @expr
|
776
|
+
|
777
|
+
if @expr && @type.object? && @command.is_a?(Rubex::AST::Expression::ElementRef) &&
|
778
|
+
!@command.object_ptr
|
779
|
+
@command.generate_element_ref_code @expr, code, local_scope
|
780
|
+
@c_code << "#{@command.c_code(local_scope)}"
|
781
|
+
else
|
782
|
+
@command.generate_evaluation_code code, local_scope
|
783
|
+
# Interpreted as a method call
|
784
|
+
if @command.is_a? Rubex::AST::Expression::MethodCall
|
785
|
+
@c_code << @command.c_code(local_scope)
|
786
|
+
else # interpreted as referencing the contents of a struct
|
787
|
+
op = @expr.type.cptr? ? "->" : "."
|
788
|
+
|
789
|
+
@c_code << "#{@expr.c_code(local_scope)}#{op}#{@command.c_code(local_scope)}"
|
790
|
+
end
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
def generate_disposal_code code
|
795
|
+
@expr.generate_disposal_code(code) if @expr
|
796
|
+
# @command.generate_disposal_code code
|
797
|
+
@arg_list.each do |arg|
|
798
|
+
arg.generate_disposal_code code
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
def generate_assignment_code rhs, code, local_scope
|
803
|
+
generate_evaluation_code code, local_scope
|
804
|
+
code << "#{self.c_code(local_scope)} = #{rhs.c_code(local_scope)};"
|
805
|
+
code.nl
|
806
|
+
end
|
807
|
+
|
808
|
+
def c_code local_scope
|
809
|
+
code = super
|
810
|
+
code << @c_code
|
811
|
+
code
|
812
|
+
end
|
813
|
+
|
814
|
+
private
|
815
|
+
|
816
|
+
def analyse_command_type local_scope
|
817
|
+
if @expr && ((@expr.type.cptr? && @expr.type.type.struct_or_union?) ||
|
818
|
+
(@expr.type.struct_or_union?))
|
819
|
+
scope = @expr.type.base_type.scope
|
820
|
+
if @command.is_a? String
|
821
|
+
@command = Expression::Name.new @command
|
822
|
+
@command.analyse_statement scope
|
823
|
+
end
|
824
|
+
|
825
|
+
if !scope.has_entry?(@command.name)
|
826
|
+
raise "Entry #{@command.name} does not exist in #{@expr}."
|
827
|
+
end
|
828
|
+
|
829
|
+
if @command.is_a? Rubex::AST::Expression::ElementRef
|
830
|
+
@command.analyse_statement local_scope, scope
|
831
|
+
end
|
832
|
+
else
|
833
|
+
@command = Expression::MethodCall.new @command, @expr, @arg_list
|
834
|
+
@command.analyse_statement local_scope
|
835
|
+
end
|
836
|
+
@type = @command.type
|
837
|
+
@command.allocate_temps local_scope
|
838
|
+
@command.allocate_temp local_scope, @type
|
839
|
+
@command.release_temps local_scope
|
840
|
+
@command.release_temp local_scope
|
841
|
+
end
|
842
|
+
end # class CommandCall
|
843
|
+
|
844
|
+
|
845
|
+
class ArgDeclaration < Base
|
846
|
+
attr_reader :entry, :type
|
847
|
+
|
848
|
+
# data_hash - a Hash containing data about the variable.
|
849
|
+
def initialize data_hash
|
850
|
+
@data_hash = data_hash
|
851
|
+
end
|
852
|
+
|
853
|
+
def analyse_statement local_scope, inside_func_ptr: false, extern: false
|
854
|
+
# FIXME: Support array of function pointers and array in arguments.
|
855
|
+
var = @data_hash[:variables][0]
|
856
|
+
dtype = @data_hash[:dtype]
|
857
|
+
ident = var[:ident]
|
858
|
+
ptr_level = var[:ptr_level]
|
859
|
+
value = var[:value]
|
860
|
+
|
861
|
+
if ident.is_a?(Hash) # function pointer
|
862
|
+
cfunc_return_type = Helpers.determine_dtype(dtype,
|
863
|
+
ident[:return_ptr_level])
|
864
|
+
arg_list = ident[:arg_list].analyse_statement(local_scope,
|
865
|
+
inside_func_ptr: true)
|
866
|
+
ptr_level = "*" if ptr_level.empty?
|
867
|
+
|
868
|
+
if inside_func_ptr
|
869
|
+
name, c_name = nil, nil
|
870
|
+
else
|
871
|
+
name = ident[:name]
|
872
|
+
c_name = Rubex::ARG_PREFIX + name
|
873
|
+
end
|
874
|
+
|
875
|
+
@type = Helpers.determine_dtype(
|
876
|
+
DataType::CFunction.new(name, c_name, arg_list, cfunc_return_type, nil),
|
877
|
+
ptr_level)
|
878
|
+
else
|
879
|
+
if !inside_func_ptr
|
880
|
+
name, c_name = ident, Rubex::ARG_PREFIX + ident
|
881
|
+
end
|
882
|
+
@type = Helpers.determine_dtype(dtype, ptr_level)
|
883
|
+
end
|
884
|
+
|
885
|
+
value.analyse_statement(local_scope) if value
|
886
|
+
|
887
|
+
if !extern && !inside_func_ptr
|
888
|
+
@entry = local_scope.add_arg(name: name, c_name: c_name, type: @type,
|
889
|
+
value: value)
|
890
|
+
end
|
891
|
+
end # def analyse_statement
|
892
|
+
end # class ArgDeclaration
|
893
|
+
|
894
|
+
class CoerceObject < Base
|
895
|
+
attr_reader :expr
|
896
|
+
|
897
|
+
extend Forwardable
|
898
|
+
|
899
|
+
def_delegators :@expr, :generate_evaluation_code, :generate_disposal_code,
|
900
|
+
:generate_assignment_code, :allocate_temp, :allocate_temps,
|
901
|
+
:release_temp, :release_temps, :type
|
10
902
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
903
|
+
|
904
|
+
# Internal class to typecast from a C type to another C type.
|
905
|
+
class TypecastTo < CoerceObject
|
906
|
+
def initialize dtype
|
907
|
+
|
908
|
+
end
|
909
|
+
# TODO
|
910
|
+
end
|
911
|
+
|
912
|
+
# internal node for converting to ruby object.
|
913
|
+
class ToRubyObject < CoerceObject
|
914
|
+
attr_reader :type
|
915
|
+
|
916
|
+
def initialize expr
|
917
|
+
@expr = expr
|
918
|
+
@type = Rubex::DataType::RubyObject.new
|
919
|
+
end
|
920
|
+
|
921
|
+
def c_code local_scope
|
922
|
+
t = @expr.type
|
923
|
+
t = (t.c_function? || t.alias_type?) ? t.type : t
|
924
|
+
"#{t.to_ruby_object(@expr.c_code(local_scope))}"
|
925
|
+
end
|
926
|
+
end
|
927
|
+
|
928
|
+
# internal node for converting from ruby object.
|
929
|
+
class FromRubyObject < CoerceObject
|
930
|
+
# expr - Expression to convert
|
931
|
+
# from_node - LHS expression. Of type Rubex::AST::Expression
|
932
|
+
def initialize expr, from_node
|
933
|
+
@expr = expr
|
934
|
+
@type = @expr.type
|
935
|
+
@from_node = from_node
|
936
|
+
end
|
937
|
+
|
938
|
+
def c_code local_scope
|
939
|
+
"#{@from_node.type.from_ruby_object(@expr.c_code(local_scope))}"
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
|
+
class BlockGiven < Base
|
944
|
+
attr_reader :type
|
945
|
+
|
946
|
+
def analyse_statement local_scope
|
947
|
+
@type = DataType::CBoolean.new
|
948
|
+
end
|
949
|
+
|
950
|
+
def c_code local_scope
|
951
|
+
"rb_block_given_p()"
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
# Internal node that denotes empty expression for a statement for example
|
956
|
+
# the `return` for a C function with return type `void`.
|
957
|
+
class Empty < Base
|
958
|
+
attr_reader :type
|
959
|
+
|
960
|
+
def analyse_statement local_scope
|
961
|
+
@type = DataType::Void.new
|
962
|
+
end
|
963
|
+
end
|
964
|
+
|
965
|
+
module Literal
|
966
|
+
class Base < Rubex::AST::Expression::Base
|
967
|
+
attr_reader :name, :type
|
968
|
+
|
969
|
+
def initialize name
|
970
|
+
@name = name
|
971
|
+
end
|
972
|
+
|
973
|
+
def c_code local_scope
|
974
|
+
code = super
|
975
|
+
code << @name
|
976
|
+
end
|
977
|
+
|
978
|
+
def c_name
|
979
|
+
@name
|
980
|
+
end
|
981
|
+
|
982
|
+
def literal?; true; end
|
983
|
+
|
984
|
+
def == other
|
985
|
+
self.class == other.class && @name == other.name
|
986
|
+
end
|
987
|
+
end # class Base
|
988
|
+
|
989
|
+
class ArrayLit < Literal::Base
|
990
|
+
include Enumerable
|
991
|
+
|
992
|
+
attr_accessor :c_array
|
993
|
+
|
994
|
+
def each &block
|
995
|
+
@array_list.each(&block)
|
996
|
+
end
|
997
|
+
|
998
|
+
def initialize array_list
|
999
|
+
@array_list = array_list
|
1000
|
+
@subexprs = []
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
def analyse_statement local_scope
|
1004
|
+
@has_temp = true
|
1005
|
+
@type = DataType::RubyObject.new
|
1006
|
+
@array_list.map! do |e|
|
1007
|
+
e.analyse_statement local_scope
|
1008
|
+
e = e.to_ruby_object
|
1009
|
+
@subexprs << e
|
1010
|
+
e
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def generate_evaluation_code code, local_scope
|
1015
|
+
code << "#{@c_code} = rb_ary_new2(#{@array_list.size});"
|
1016
|
+
code.nl
|
1017
|
+
@array_list.each do |e|
|
1018
|
+
code << "rb_ary_push(#{@c_code}, #{e.c_code(local_scope)});"
|
1019
|
+
code.nl
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
def generate_disposal_code code
|
1024
|
+
code << "#{@c_code} = 0;"
|
1025
|
+
code.nl
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
def c_code local_scope
|
1029
|
+
@c_code
|
1030
|
+
end
|
1031
|
+
end # class ArrayLit
|
1032
|
+
|
1033
|
+
class HashLit < Literal::Base
|
1034
|
+
def initialize key_val_pairs
|
1035
|
+
@key_val_pairs = key_val_pairs
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def analyse_statement local_scope
|
1039
|
+
@has_temp = true
|
1040
|
+
@type = Rubex::DataType::RubyObject.new
|
1041
|
+
@key_val_pairs.map! do |k, v|
|
1042
|
+
k.analyse_for_target_type(@type, local_scope)
|
1043
|
+
v.analyse_for_target_type(@type, local_scope)
|
1044
|
+
[k.to_ruby_object, v.to_ruby_object]
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def generate_evaluation_code code, local_scope
|
1049
|
+
code << "#{@c_code} = rb_hash_new();"
|
1050
|
+
code.nl
|
1051
|
+
@key_val_pairs.each do |k, v|
|
1052
|
+
k.generate_evaluation_code(code, local_scope)
|
1053
|
+
v.generate_evaluation_code(code, local_scope)
|
1054
|
+
|
1055
|
+
code << "rb_hash_aset(#{@c_code}, #{k.c_code(local_scope)}, "
|
1056
|
+
code << "#{v.c_code(local_scope)});"
|
1057
|
+
code.nl
|
1058
|
+
|
1059
|
+
k.generate_disposal_code code
|
1060
|
+
v.generate_disposal_code code
|
1061
|
+
code.nl
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def allocate_temps local_scope
|
1066
|
+
@key_val_pairs.each do |k,v|
|
1067
|
+
k.allocate_temp local_scope, k.type
|
1068
|
+
v.allocate_temp local_scope, v.type
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
def release_temps local_scope
|
1073
|
+
@key_val_pairs.each do |k,v|
|
1074
|
+
k.release_temp local_scope
|
1075
|
+
v.release_temp local_scope
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
def generate_disposal_code code
|
1080
|
+
code << "#{@c_code} = 0;"
|
1081
|
+
code.nl
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
def c_code local_scope
|
1085
|
+
@c_code
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
class RubySymbol < Literal::Base
|
1090
|
+
def initialize name
|
1091
|
+
super(name[1..-1])
|
1092
|
+
@type = Rubex::DataType::RubySymbol.new
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
def generate_evaluation_code code, local_scope
|
1096
|
+
@c_code = "ID2SYM(rb_intern(\"#{@name}\"))"
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
def c_code local_scope
|
1100
|
+
@c_code
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
class Double < Literal::Base
|
1105
|
+
def initialize name
|
1106
|
+
super
|
1107
|
+
@type = Rubex::DataType::F64.new
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
class Int < Literal::Base
|
1112
|
+
def initialize name
|
1113
|
+
super
|
1114
|
+
@type = Rubex::DataType::Int.new
|
1115
|
+
end
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
class StringLit < Literal::Base
|
1119
|
+
def initialize name
|
1120
|
+
super
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
def analyse_for_target_type target_type, local_scope
|
1124
|
+
if target_type.char_ptr?
|
1125
|
+
@type = Rubex::DataType::CStr.new
|
1126
|
+
elsif target_type.object?
|
1127
|
+
@type = Rubex::DataType::RubyString.new
|
1128
|
+
analyse_statement local_scope
|
1129
|
+
else
|
1130
|
+
raise Rubex::TypeError, "Cannot assign #{target_type} to string."
|
1131
|
+
end
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def analyse_statement local_scope
|
1135
|
+
@type = Rubex::DataType::RubyString.new unless @type
|
1136
|
+
@has_temp = 1
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def generate_evaluation_code code, local_scope
|
1140
|
+
if @type.cstr?
|
1141
|
+
@c_code = "\"#{@name}\""
|
1142
|
+
else
|
1143
|
+
code << "#{@c_code} = rb_str_new2(\"#{@name}\");"
|
1144
|
+
code.nl
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def generate_disposal_code code
|
1149
|
+
if @type.object?
|
1150
|
+
code << "#{@c_code} = 0;"
|
1151
|
+
code.nl
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
def c_code local_scope
|
1156
|
+
@c_code
|
1157
|
+
end
|
1158
|
+
end # class StringLit
|
1159
|
+
|
1160
|
+
class Char < Literal::Base
|
1161
|
+
def initialize name
|
1162
|
+
super
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
def analyse_for_target_type target_type, local_scope
|
1166
|
+
if target_type.char?
|
1167
|
+
@type = Rubex::DataType::Char.new
|
1168
|
+
elsif target_type.object?
|
1169
|
+
@type = Rubex::DataType::RubyString.new
|
1170
|
+
analyse_statement local_scope
|
1171
|
+
else
|
1172
|
+
raise Rubex::TypeError, "Cannot assign #{target_type} to string."
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
def analyse_statement local_scope
|
1177
|
+
@type = Rubex::DataType::RubyString.new unless @type
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
def generate_evaluation_code code, local_scope
|
1181
|
+
if @type.char?
|
1182
|
+
@c_code = @name
|
1183
|
+
else
|
1184
|
+
@c_code = "rb_str_new2(\"#{@name[1]}\")"
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
def c_code local_scope
|
1189
|
+
@c_code
|
1190
|
+
end
|
1191
|
+
end # class Char
|
1192
|
+
|
1193
|
+
class True < Literal::Base
|
1194
|
+
def initialize name
|
1195
|
+
super
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
def analyse_for_target_type target_type, local_scope
|
1199
|
+
if target_type.object?
|
1200
|
+
@type = Rubex::DataType::TrueType.new
|
1201
|
+
else
|
1202
|
+
@type = Rubex::DataType::CBoolean.new
|
1203
|
+
end
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
def analyse_statement local_scope
|
1207
|
+
@type = Rubex::DataType::TrueType.new
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
def c_code local_scope
|
1211
|
+
if @type.object?
|
1212
|
+
@name
|
1213
|
+
else
|
1214
|
+
"1"
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
end # class True
|
1218
|
+
|
1219
|
+
class False < Literal::Base
|
1220
|
+
def initialize name
|
1221
|
+
super
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
def analyse_for_target_type target_type, local_scope
|
1225
|
+
if target_type.object?
|
1226
|
+
@type = Rubex::DataType::FalseType.new
|
1227
|
+
else
|
1228
|
+
@type = Rubex::DataType::CBoolean.new
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
def analyse_statement local_scope
|
1233
|
+
@type = Rubex::DataType::FalseType.new
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
def c_code local_scope
|
1237
|
+
if @type.object?
|
1238
|
+
@name
|
1239
|
+
else
|
1240
|
+
"0"
|
1241
|
+
end
|
1242
|
+
end
|
1243
|
+
end # class False
|
1244
|
+
|
1245
|
+
class Nil < Literal::Base
|
1246
|
+
def initialize name
|
1247
|
+
super
|
1248
|
+
@type = Rubex::DataType::NilType.new
|
1249
|
+
end
|
1250
|
+
end # class Nil
|
1251
|
+
|
1252
|
+
class CNull < Literal::Base
|
1253
|
+
def initialize name
|
1254
|
+
# Rubex treats NULL's dtype as void*
|
1255
|
+
super
|
1256
|
+
@type = Rubex::DataType::CPtr.new(Rubex::DataType::Void.new)
|
1257
|
+
end
|
1258
|
+
end # class CNull
|
1259
|
+
end # module Literal
|
1260
|
+
end # module Expression
|
1261
|
+
end # module AST
|
1262
|
+
end # module Rubex
|