rubex 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +14 -0
  4. data/CONTRIBUTING.md +101 -0
  5. data/HISTORY.md +3 -0
  6. data/README.md +112 -297
  7. data/REFERENCE.md +753 -0
  8. data/Rakefile +4 -1
  9. data/TUTORIAL.md +234 -0
  10. data/bin/rubex +1 -1
  11. data/docs/_config.yml +1 -0
  12. data/docs/index.html +1 -0
  13. data/examples/c_struct_interface/c_struct_interface.rb +6 -0
  14. data/examples/c_struct_interface/c_struct_interface.rubex +47 -0
  15. data/examples/linked_list/linked_list.rubex +39 -0
  16. data/examples/linked_list/rb_linked_list.rb +8 -0
  17. data/examples/rcsv wrapper/rcsv/README.md +1 -0
  18. data/examples/rcsv wrapper/rcsv/Rakefile +7 -0
  19. data/examples/rcsv wrapper/rcsv/ext/rcsv/extconf.rb +3 -0
  20. data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.c +302 -0
  21. data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.rubex +124 -0
  22. data/examples/rcsv wrapper/rcsv/lib/rcsv.rb +8 -0
  23. data/examples/rcsv wrapper/rcsv/lib/rcsv.so +0 -0
  24. data/examples/rcsv wrapper/rcsv/lib/rcsv/version.rb +1 -0
  25. data/examples/rcsv wrapper/rcsv/rcsv.gemspec +27 -0
  26. data/examples/rcsv wrapper/rcsv/spec/rcsv.csv +5 -0
  27. data/examples/rcsv wrapper/rcsv/spec/rcsv_spec.rb +17 -0
  28. data/examples/rcsv wrapper/rcsv/spec/spec_helper.rb +6 -0
  29. data/{spec/fixtures/basic_ruby_method/Makefile → examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/Makefile } +20 -20
  30. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.o +0 -0
  31. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.so +0 -0
  32. data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/stage/lib/rcsv.so +0 -0
  33. data/lib/rubex.rb +6 -50
  34. data/lib/rubex/ast.rb +1 -3
  35. data/lib/rubex/ast/expression.rb +1257 -8
  36. data/lib/rubex/ast/node.rb +226 -28
  37. data/lib/rubex/ast/statement.rb +1162 -35
  38. data/lib/rubex/ast/top_statement.rb +815 -0
  39. data/lib/rubex/code_writer.rb +103 -26
  40. data/lib/rubex/compiler.rb +72 -0
  41. data/lib/rubex/compiler_config.rb +19 -0
  42. data/lib/rubex/constants.rb +145 -8
  43. data/lib/rubex/data_type.rb +667 -4
  44. data/lib/rubex/error.rb +15 -0
  45. data/lib/rubex/helpers.rb +154 -0
  46. data/lib/rubex/lexer.rex +186 -22
  47. data/lib/rubex/lexer.rex.rb +261 -35
  48. data/lib/rubex/parser.racc +876 -28
  49. data/lib/rubex/parser.racc.rb +2845 -90
  50. data/lib/rubex/rake_task.rb +34 -0
  51. data/lib/rubex/symbol_table/entry.rb +17 -3
  52. data/lib/rubex/symbol_table/scope.rb +298 -25
  53. data/lib/rubex/version.rb +1 -1
  54. data/rubex.gemspec +11 -3
  55. data/spec/basic_ruby_method_spec.rb +15 -21
  56. data/spec/binding_ptr_args_spec.rb +33 -0
  57. data/spec/bitwise_operators_spec.rb +40 -0
  58. data/spec/blocks_spec.rb +35 -0
  59. data/spec/c_bindings_spec.rb +36 -0
  60. data/spec/c_constants_spec.rb +33 -0
  61. data/spec/c_function_ptrs_spec.rb +38 -0
  62. data/spec/c_functions_spec.rb +35 -0
  63. data/spec/c_struct_interface_spec.rb +38 -0
  64. data/spec/call_by_reference_spec.rb +33 -0
  65. data/spec/class_methods_spec.rb +33 -0
  66. data/spec/class_spec.rb +40 -0
  67. data/spec/comments_spec.rb +33 -0
  68. data/spec/default_args_spec.rb +37 -0
  69. data/spec/error_handling_spec.rb +42 -0
  70. data/spec/examples_spec.rb +52 -0
  71. data/spec/expressions_spec.rb +33 -0
  72. data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +2 -0
  73. data/spec/fixtures/binding_ptr_args/binding_ptr_args.rubex +30 -0
  74. data/spec/fixtures/bitwise_operators/bitwise_operators.rubex +40 -0
  75. data/spec/fixtures/blocks/blocks.rubex +11 -0
  76. data/spec/fixtures/c_bindings/c_bindings.rubex +58 -0
  77. data/spec/fixtures/c_constants/c_constants.rubex +7 -0
  78. data/spec/fixtures/c_function_ptrs/c_function_ptrs.rubex +52 -0
  79. data/spec/fixtures/c_functions/c_functions.rubex +25 -0
  80. data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +34 -0
  81. data/spec/fixtures/call_by_reference/call_by_reference.rubex +30 -0
  82. data/spec/fixtures/class/class.rubex +20 -0
  83. data/spec/fixtures/class_methods/class_methods.rubex +12 -0
  84. data/spec/fixtures/comments/comments.rubex +9 -0
  85. data/spec/fixtures/default_args/default_args.rubex +11 -0
  86. data/spec/fixtures/error_handling/error_handling.rubex +54 -0
  87. data/spec/fixtures/examples/array_to_hash.rubex +14 -0
  88. data/spec/fixtures/examples/rcsv.csv +5 -0
  89. data/spec/fixtures/examples/rcsv.rubex +329 -0
  90. data/spec/fixtures/expressions/expressions.rubex +10 -0
  91. data/spec/fixtures/if_else/if_else.rubex +77 -0
  92. data/spec/fixtures/implicit_lib_include/implicit_lib_include.rubex +15 -0
  93. data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +17 -0
  94. data/spec/fixtures/loops/loops.rubex +33 -0
  95. data/spec/fixtures/recursion/recursion.rubex +9 -0
  96. data/spec/fixtures/ruby_constant_method_calls/ruby_constant_method_calls.rubex +17 -0
  97. data/spec/fixtures/ruby_operators/ruby_operators.rubex +29 -0
  98. data/spec/fixtures/ruby_raise/ruby_raise.rubex +13 -0
  99. data/spec/fixtures/ruby_strings/ruby_strings.rubex +19 -0
  100. data/spec/fixtures/ruby_strings/string_blank_bm.rb +37 -0
  101. data/spec/fixtures/ruby_symbols/ruby_symbols.rubex +12 -0
  102. data/spec/fixtures/ruby_types/ruby_types.rubex +15 -0
  103. data/spec/fixtures/statement_expression/statement_expression.rubex +23 -0
  104. data/spec/fixtures/static_array/static_array.rubex +20 -0
  105. data/spec/fixtures/string_literals/string_literals.rubex +15 -0
  106. data/spec/fixtures/struct/struct.rubex +82 -0
  107. data/spec/fixtures/typecasting/typecasting.rubex +23 -0
  108. data/spec/fixtures/var_declarations/var_declarations.rubex +39 -0
  109. data/spec/if_else_spec.rb +39 -0
  110. data/spec/implicit_lib_include_spec.rb +33 -0
  111. data/spec/init_ruby_objects_with_literal_syntax_spec.rb +39 -0
  112. data/spec/loops_spec.rb +34 -0
  113. data/spec/recursion_spec.rb +35 -0
  114. data/spec/ruby_constant_method_calls_spec.rb +35 -0
  115. data/spec/ruby_operators_spec.rb +40 -0
  116. data/spec/ruby_raise_spec.rb +35 -0
  117. data/spec/ruby_strings_spec.rb +33 -0
  118. data/spec/ruby_symbols_spec.rb +37 -0
  119. data/spec/ruby_types_spec.rb +35 -0
  120. data/spec/spec_helper.rb +54 -1
  121. data/spec/statement_expression_spec.rb +34 -0
  122. data/spec/static_array_spec.rb +33 -0
  123. data/spec/string_literals_spec.rb +34 -0
  124. data/spec/struct_spec.rb +36 -0
  125. data/spec/typecasting_spec.rb +38 -0
  126. data/spec/var_declarions_spec.rb +35 -0
  127. metadata +255 -29
  128. data/lib/rubex/ast/argument_list.rb +0 -20
  129. data/lib/rubex/ast/c_base_type.rb +0 -11
  130. data/lib/rubex/ast/ruby_method_def.rb +0 -84
  131. data/spec/fixtures/basic_ruby_method/basic.rb +0 -3
  132. data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +0 -16
  133. data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
  134. data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
  135. 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
@@ -0,0 +1,8 @@
1
+ require 'rcsv.so'
2
+ require 'rcsv/version'
3
+
4
+ class Rcsv
5
+ def self.p file_name
6
+ Rcsv.parse(File.read(file_name), {})
7
+ end
8
+ end
@@ -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,5 @@
1
+ Name, age, sex
2
+ Sameer, 24, M
3
+ Ameya, 23, M
4
+ Neeraja, 23, F
5
+ Shounak, 24, M
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require 'rcsv'
@@ -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.0/include/ruby-2.3.0
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.0/include/ruby-2.3.0/i686-linux
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.0
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 = $(prefix)/etc
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,-R$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)
71
- LIBRUBYARG_STATIC = -Wl,-R$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static
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 = -D_FILE_OFFSET_BITS=64
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 = i686-linux
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 -c
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 = /basic_ruby_method
135
+ target_prefix = /ext/rcsv
136
136
  LOCAL_LIBS =
137
- LIBS = $(LIBRUBYARG_SHARED) -lpthread -lgmp -ldl -lcrypt -lm -lc
138
- ORIG_SRCS = basic_ruby_method.c
137
+ LIBS = $(LIBRUBYARG_SHARED) -lcsv -lpthread -lgmp -ldl -lcrypt -lm -lc
138
+ ORIG_SRCS = rcsv.c
139
139
  SRCS = $(ORIG_SRCS)
140
- OBJS = basic_ruby_method.o
140
+ OBJS = rcsv.o
141
141
  HDRS =
142
- TARGET = basic_ruby_method
143
- TARGET_NAME = basic_ruby_method
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.-.basic_ruby_method.time
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.-.basic_ruby_method.time:
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 basic_ruby_method/$(DLLIB)
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
 
@@ -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
- module Rubex
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'
@@ -1,6 +1,4 @@
1
1
  require 'rubex/ast/node'
2
- require 'rubex/ast/ruby_method_def'
3
- require 'rubex/ast/c_base_type'
4
- require 'rubex/ast/argument_list'
5
2
  require 'rubex/ast/statement'
6
3
  require 'rubex/ast/expression'
4
+ require 'rubex/ast/top_statement'
@@ -1,13 +1,1262 @@
1
1
  module Rubex
2
2
  module AST
3
- class Expression
4
- class Addition
5
- attr_reader :left, :right
3
+ module Expression
4
+ class Base
5
+ attr_accessor :typecast
6
6
 
7
- def initialize left, right
8
- @left, @right = left, right
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
- end
12
- end
13
- end
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