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.
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