rubex 0.0.1 → 0.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.
- 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
|