edn_turbo 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,9 +2,7 @@
2
2
  #include <iomanip>
3
3
  #include <string>
4
4
  #include <limits>
5
-
6
- #include <rice/String.hpp>
7
- #include <rice/Array.hpp>
5
+ #include <exception>
8
6
 
9
7
  #include <ruby/ruby.h>
10
8
  #include <ruby/encoding.h>
@@ -23,43 +21,121 @@ namespace edn
23
21
  return s.str().length();
24
22
  }
25
23
 
26
- static const std::size_t LL_max_chars = get_max_chars<>((long long) 1);
27
- static const std::size_t LD_max_chars = get_max_chars<>((long double) 1);
24
+ static const std::size_t LL_max_chars = get_max_chars<>((long) 1);
25
+ static const std::size_t LD_max_chars = get_max_chars<>((double) 1);
26
+
27
+
28
+ // =================================================================
29
+ // reset parsing state
30
+ //
31
+ void Parser::reset()
32
+ {
33
+ line_number = 1;
34
+ while (!discard.empty())
35
+ discard.pop();
36
+ }
37
+
38
+ //
39
+ // set a new source
40
+ void Parser::set_source(const char* src, std::size_t len)
41
+ {
42
+ reset();
43
+ // set ragel state
44
+ p = src;
45
+ pe = src + len;
46
+ eof = pe;
47
+ }
48
+
49
+
50
+ // =================================================================
51
+ // work-around for idiotic rb_protect convention in order to avoid
52
+ // using ruby/rice
53
+ //
54
+ typedef VALUE (edn_rb_f_type)( VALUE arg );
55
+
56
+ // we're using at most 2 args
57
+ struct prot_args {
58
+ prot_args(ID m, VALUE arg) :
59
+ method(m), count(1) {
60
+ args[0] = arg;
61
+ }
62
+ prot_args(ID m, VALUE arg1, VALUE arg2) :
63
+ method(m), count(2) {
64
+ args[0] = arg1;
65
+ args[1] = arg2;
66
+ }
67
+
68
+ ID method;
69
+ VALUE count;
70
+ VALUE args[2];
71
+ };
72
+
73
+ // this allows us to wrap with rb_protect()
74
+ static inline VALUE edn_wrap_funcall2( VALUE arg )
75
+ {
76
+ prot_args* a = reinterpret_cast<prot_args*>(arg);
77
+ return rb_funcall2( edn::rb_mEDNT, a->method, a->count, a->args );
78
+ }
79
+
80
+ static inline VALUE edn_prot_rb_funcall( edn_rb_f_type func, VALUE args )
81
+ {
82
+ int error;
83
+ VALUE s = rb_protect( func, args, &error );
84
+ if (error) Parser::throw_error(error);
85
+ return s;
86
+ }
87
+
88
+ static inline VALUE edn_prot_rb_new_str(const char* str) {
89
+ int error;
90
+ VALUE s = rb_protect( reinterpret_cast<VALUE (*)(VALUE)>(rb_str_new_cstr),
91
+ reinterpret_cast<VALUE>(str), &error );
92
+ if (error) Parser::throw_error(error);
93
+ return s;
94
+ }
95
+
96
+ static inline VALUE edn_rb_enc_associate_utf8(VALUE str)
97
+ {
98
+ return rb_enc_associate(str, rb_utf8_encoding() );
99
+ }
100
+
101
+ // =================================================================
102
+ // utils
28
103
 
29
104
  //
30
105
  // convert to int.. if string rep has more digits than long can
31
106
  // hold, call into ruby to get a big num
32
- Rice::Object Parser::integer_to_ruby(const char* str, std::size_t len)
107
+ VALUE Parser::integer_to_ruby(const char* str, std::size_t len)
33
108
  {
34
109
  if (len < LL_max_chars)
35
110
  {
36
- return buftotype<long>(str, len);
111
+ return LONG2NUM(buftotype<long>(str, len));
37
112
  }
38
113
 
39
114
  // value is outside of range of long type. Use ruby to convert it
40
- VALUE rb_s = Rice::protect(rb_str_new2, str);
41
- return Rice::protect(rb_funcall, rb_mEDNT, EDNT_STR_INT_TO_BIGNUM, 1, rb_s);
115
+ VALUE rb_s = edn_prot_rb_new_str( str );
116
+ prot_args args(EDNT_STR_INT_TO_BIGNUM, rb_s);
117
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
42
118
  }
43
119
 
44
120
  //
45
- // as above.. TODO: check exponential
46
- Rice::Object Parser::float_to_ruby(const char* str, std::size_t len)
121
+ // as above.. TODO: check exponential..
122
+ VALUE Parser::float_to_ruby(const char* str, std::size_t len)
47
123
  {
48
124
  if (len < LD_max_chars)
49
125
  {
50
- return buftotype<double>(str, len);
126
+ return rb_float_new(buftotype<double>(str, len));
51
127
  }
52
128
 
53
129
  // value is outside of range of long type. Use ruby to convert it
54
- VALUE rb_s = Rice::protect(rb_str_new2, str);
55
- return Rice::protect(rb_funcall, rb_mEDNT, EDNT_STR_DBL_TO_BIGNUM, 1, rb_s);
130
+ prot_args args(EDNT_STR_DBL_TO_BIGNUM, edn_prot_rb_new_str(str));
131
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
56
132
  }
57
133
 
58
134
 
59
135
  //
60
136
  // copies the string data, unescaping any present values that need to be replaced
61
137
  //
62
- bool Parser::parse_byte_stream(const char *p_start, const char *p_end, Rice::String& s,
138
+ bool Parser::parse_byte_stream(const char *p_start, const char *p_end, VALUE& v_utf8,
63
139
  bool encode)
64
140
  {
65
141
  if (p_end > p_start) {
@@ -74,9 +150,13 @@ namespace edn
74
150
  }
75
151
 
76
152
  // utf-8 encode
77
- VALUE vs = Rice::protect( rb_str_new2, buf.c_str() );
78
- VALUE s_utf8 = Rice::protect( rb_enc_associate, vs, rb_utf8_encoding() );
79
- s = Rice::String(s_utf8);
153
+ VALUE vs = edn_prot_rb_new_str(buf.c_str());
154
+ int error;
155
+ v_utf8 = rb_protect( edn_rb_enc_associate_utf8, vs, &error);
156
+ if (error) Parser::throw_error(error);
157
+ return true;
158
+ } else if (p_end == p_start) {
159
+ v_utf8 = rb_str_new("", 0);
80
160
  return true;
81
161
  }
82
162
 
@@ -86,7 +166,7 @@ namespace edn
86
166
  //
87
167
  // handles things like \c, \newline
88
168
  //
89
- bool Parser::parse_escaped_char(const char *p, const char *pe, Rice::Object& o)
169
+ bool Parser::parse_escaped_char(const char *p, const char *pe, VALUE& v)
90
170
  {
91
171
  std::string buf;
92
172
  std::size_t len = pe - p;
@@ -104,37 +184,51 @@ namespace edn
104
184
  else return false;
105
185
  }
106
186
 
107
- o = Rice::String(buf);
187
+ v = edn_prot_rb_new_str( buf.c_str() );
108
188
  return true;
109
189
  }
110
190
 
111
191
 
112
192
  //
113
193
  // get a set representation from the ruby side. See edn_turbo.rb
114
- Rice::Object Parser::make_edn_symbol(const std::string& name)
194
+ VALUE Parser::make_edn_symbol(VALUE sym)
115
195
  {
116
- VALUE rb_s = Rice::protect(rb_str_new2, name.c_str());
117
- return Rice::protect(rb_funcall, rb_mEDNT, EDNT_MAKE_EDN_SYMBOL, 1, rb_s);
196
+ prot_args args(edn::EDNT_MAKE_EDN_SYMBOL, sym);
197
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
118
198
  }
119
199
 
120
200
  //
121
201
  // get a set representation from the ruby side. See edn_turbo.rb
122
- Rice::Object Parser::make_ruby_set(const Rice::Array& elems)
202
+ VALUE Parser::make_ruby_set(VALUE elems)
123
203
  {
124
- return Rice::protect(rb_funcall, rb_mEDNT, EDNT_MAKE_SET_METHOD, 1, elems.value());
204
+ prot_args args(edn::EDNT_MAKE_SET_METHOD, elems);
205
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
125
206
  }
126
207
 
127
208
  //
128
209
  // get an object representation from the ruby side using the given symbol name
129
- Rice::Object Parser::tagged_element(const std::string& name, const Rice::Object& data)
210
+ VALUE Parser::tagged_element(VALUE name, VALUE data)
130
211
  {
131
- VALUE rb_s = Rice::protect(rb_str_new2, name.c_str());
132
- return Rice::protect(rb_funcall, rb_mEDNT, EDNT_TAGGED_ELEM, 2, rb_s, data.value());
212
+ prot_args args(edn::EDNT_TAGGED_ELEM, name, data);
213
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
133
214
  }
134
215
 
135
216
 
136
217
  //
137
218
  // error reporting
219
+ void Parser::throw_error(int error)
220
+ {
221
+ if (error == 0)
222
+ return;
223
+
224
+ VALUE err = rb_errinfo();
225
+ VALUE klass = rb_class_path(CLASS_OF(err));
226
+ VALUE message = rb_obj_as_string(err);
227
+ std::stringstream msg;
228
+ msg << RSTRING_PTR(klass) << " exception: " << RSTRING_PTR(message);
229
+ throw std::runtime_error(msg.str());
230
+ }
231
+
138
232
  void Parser::error(const std::string& func, const std::string& err, char c) const
139
233
  {
140
234
  std::cerr << "Parse error "
@@ -1,4 +1,4 @@
1
- require 'mkmf-rice'
1
+ require 'mkmf'
2
2
 
3
3
  HEADER_DIRS = [
4
4
  '/usr/local/include',
@@ -2,16 +2,14 @@
2
2
  #include <iostream>
3
3
  #include <clocale>
4
4
 
5
- // always include rice headers before ruby.h
6
- #include <rice/Data_Type.hpp>
7
- #include <rice/Constructor.hpp>
8
-
9
5
  #include "edn_parser.h"
10
6
 
7
+ #include <ruby/ruby.h>
8
+
11
9
 
12
10
  namespace edn {
13
11
 
14
- Rice::Module rb_mEDNT;
12
+ VALUE rb_mEDNT;
15
13
 
16
14
  // methods on the ruby side we'll call from here
17
15
  VALUE EDNT_MAKE_EDN_SYMBOL = Qnil;
@@ -20,8 +18,83 @@ namespace edn {
20
18
  VALUE EDNT_STR_INT_TO_BIGNUM = Qnil;
21
19
  VALUE EDNT_STR_DBL_TO_BIGNUM = Qnil;
22
20
 
21
+ //
22
+ // wrappers to hook the class w/ the C-api
23
+ template<class T>
24
+ static void delete_obj(T *ptr) {
25
+ delete ptr;
26
+ }
27
+
28
+ template<class T>
29
+ static VALUE wrap_ptr(VALUE klass, T* ptr) {
30
+ return Data_Wrap_Struct(klass, 0, delete_obj<T>, ptr);
31
+ }
23
32
 
24
- void die(int sig)
33
+ static VALUE alloc_obj(VALUE self){
34
+ return wrap_ptr<edn::Parser>(self, new Parser());
35
+ }
36
+
37
+ static inline Parser* get_parser(VALUE self)
38
+ {
39
+ Parser *p;
40
+ Data_Get_Struct( self, edn::Parser, p );
41
+ return p;
42
+ }
43
+
44
+
45
+ //
46
+ // called by the constructor - sets the source if passed
47
+ static VALUE initialize(int argc, VALUE* argv, VALUE self)
48
+ {
49
+ Parser* p = get_parser(self);
50
+
51
+ if (argc > 0)
52
+ {
53
+ const char* stream = StringValueCStr(argv[0]);
54
+ if (stream)
55
+ p->set_source( stream, std::strlen(stream) );
56
+ }
57
+ return self;
58
+ }
59
+
60
+ //
61
+ // change the input source
62
+ static VALUE set_source(VALUE self, VALUE data)
63
+ {
64
+ Parser* p = get_parser(self);
65
+
66
+ const char* stream = StringValueCStr(data);
67
+ if (stream)
68
+ p->set_source( stream, std::strlen(stream) );
69
+
70
+ return self;
71
+ }
72
+
73
+ //
74
+ // eof?
75
+ static VALUE eof(VALUE self, VALUE data)
76
+ {
77
+ return get_parser(self)->is_eof();
78
+ }
79
+
80
+ //
81
+ // parses an entire stream
82
+ static VALUE read(VALUE self, VALUE data)
83
+ {
84
+ const char* stream = StringValueCStr(data);
85
+ return get_parser(self)->parse(stream, std::strlen(stream) );
86
+ }
87
+
88
+ //
89
+ // gets the next token in the current stream
90
+ static VALUE next(VALUE self, VALUE data)
91
+ {
92
+ return get_parser(self)->next();
93
+ }
94
+
95
+ //
96
+ // signal handler
97
+ static void die(int sig)
25
98
  {
26
99
  exit(-1);
27
100
  }
@@ -45,22 +118,24 @@ void Init_edn_turbo(void)
45
118
  return;
46
119
  }
47
120
 
48
- edn::rb_mEDNT = Rice::define_module("EDNT");
121
+ edn::rb_mEDNT = rb_define_module("EDNT");
49
122
 
50
- // bind methods we'll call - these should be defined in edn_turbo.rb
123
+ // bind the ruby Parser class to the C++ one
124
+ VALUE rb_cParser = rb_define_class_under(edn::rb_mEDNT, "Parser", rb_cObject);
125
+ rb_define_alloc_func(rb_cParser, edn::alloc_obj);
126
+ rb_define_method(rb_cParser, "initialize", (VALUE(*)(ANYARGS)) &edn::initialize, -1 );
127
+ rb_define_method(rb_cParser, "ext_set_stream", (VALUE(*)(ANYARGS)) &edn::set_source, 1 );
128
+ rb_define_method(rb_cParser, "ext_eof", (VALUE(*)(ANYARGS)) &edn::eof, 0 );
129
+ rb_define_method(rb_cParser, "ext_read", (VALUE(*)(ANYARGS)) &edn::read, 1 );
130
+ rb_define_method(rb_cParser, "ext_next", (VALUE(*)(ANYARGS)) &edn::next, 0 );
131
+
132
+ // bind ruby methods we'll call - these should be defined in edn_turbo.rb
51
133
  edn::EDNT_MAKE_EDN_SYMBOL = rb_intern("make_edn_symbol");
52
134
  edn::EDNT_MAKE_SET_METHOD = rb_intern("make_set");
53
135
  edn::EDNT_TAGGED_ELEM = rb_intern("tagged_element");
54
136
  edn::EDNT_STR_INT_TO_BIGNUM = rb_intern("string_int_to_bignum");
55
137
  edn::EDNT_STR_DBL_TO_BIGNUM = rb_intern("string_double_to_bignum");
56
138
 
57
- // bind the ruby Parser class to the C++ one
58
- Rice::Data_Type<edn::Parser> rb_cParser =
59
- Rice::define_class_under<edn::Parser>(edn::rb_mEDNT, "Parser")
60
- .define_constructor(Rice::Constructor<edn::Parser>())
61
- .define_method("ext_read", &edn::Parser::process, (Rice::Arg("data")))
62
- ;
63
-
64
139
  // import whatever else we've defined in the ruby side
65
140
  rb_require("edn_turbo/edn_parser");
66
141
  }
data/lib/edn_turbo.rb CHANGED
@@ -16,7 +16,7 @@ module EDNT
16
16
  begin
17
17
  data = input.instance_of?(String) ? input : input.read
18
18
 
19
- Parser.new.read(data)
19
+ Parser.new.parse(data)
20
20
  rescue EOFError
21
21
  end
22
22
 
@@ -1,10 +1,27 @@
1
1
  module EDNT
2
2
 
3
+ EOF = Object.new
4
+
3
5
  class Parser
4
- def read(data)
5
- # call the c-side method
6
+
7
+ # initialize() is defined in the c-side (main.cc)
8
+
9
+ # call the c-side method
10
+ def set_input(data)
11
+ ext_set_stream(data)
12
+ end
13
+
14
+ # token-by-token read
15
+ def read
16
+ return EOF if ext_eof
17
+ ext_next
18
+ end
19
+
20
+ # entire stream read
21
+ def parse(data)
6
22
  ext_read(data)
7
23
  end
24
+
8
25
  end
9
26
 
10
27
  end
@@ -1,4 +1,4 @@
1
1
  module EDNT
2
- VERSION = '0.2.2'
3
- RELEASE_DATE = %q{2015-06-03}
2
+ VERSION = '0.3.0'
3
+ RELEASE_DATE = %q{2015-06-12}
4
4
  end
@@ -13,17 +13,17 @@ class EDNT_Test < Minitest::Test
13
13
 
14
14
  def check_file(file, expected_output)
15
15
  File.open(file) { |file|
16
- assert_equal(expected_output, @parser.read(file.read))
16
+ assert_equal(expected_output, @parser.parse(file.read))
17
17
  }
18
18
  end
19
19
 
20
20
  def test_basic
21
21
 
22
- assert_equal(false, @parser.read('false'))
23
- assert_equal(true, @parser.read('true'))
24
- assert_equal("a string", @parser.read('"a string"'))
25
- assert_equal(:"namespace.of.some.length/keyword-name", @parser.read(':namespace.of.some.length/keyword-name'))
26
- assert_equal(:'/', @parser.read(':/'))
22
+ assert_equal(false, @parser.parse('false'))
23
+ assert_equal(true, @parser.parse('true'))
24
+ assert_equal("a string", @parser.parse('"a string"'))
25
+ assert_equal(:"namespace.of.some.length/keyword-name", @parser.parse(':namespace.of.some.length/keyword-name'))
26
+ assert_equal(:'/', @parser.parse(':/'))
27
27
  end
28
28
 
29
29
  def test_number
@@ -103,7 +103,7 @@ class EDNT_Test < Minitest::Test
103
103
  def test_read
104
104
 
105
105
  # check read for using string
106
- assert_equal({:a=>1, :b=>2}, @parser.read('{:a 1 :b 2}'))
106
+ assert_equal({:a=>1, :b=>2}, @parser.parse('{:a 1 :b 2}'))
107
107
 
108
108
  end
109
109
 
@@ -148,7 +148,7 @@ class EDNT_Test < Minitest::Test
148
148
  Tagged.new(data).to_s
149
149
  end
150
150
 
151
- assert_equal([345, :a], @parser.read('#edn_turbo/test_tagged { :item 345 :other :a }'))
151
+ assert_equal([345, :a], @parser.parse('#edn_turbo/test_tagged { :item 345 :other :a }'))
152
152
  end
153
153
 
154
154
  def test_symbols