edn_turbo 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/Rakefile +2 -3
- data/bin/ppedn +11 -12
- data/bin/ppedn-ruby +6 -5
- data/ext/edn_turbo/depend +5 -3
- data/ext/edn_turbo/edn_parser.cc +297 -295
- data/ext/edn_turbo/edn_parser.rl +20 -18
- data/ext/edn_turbo/extconf.rb +7 -11
- data/ext/edn_turbo/main.cc +28 -27
- data/ext/edn_turbo/{edn_parser.h → parser.h} +0 -42
- data/ext/edn_turbo/parser_def.cc +197 -0
- data/ext/edn_turbo/util.cc +240 -0
- data/ext/edn_turbo/util.h +39 -0
- data/ext/edn_turbo/util_unicode.cc +36 -0
- data/ext/edn_turbo/util_unicode.h +14 -0
- data/lib/edn_turbo/edn_parser.rb +6 -3
- data/lib/edn_turbo/version.rb +4 -2
- data/lib/edn_turbo.rb +2 -0
- data/test/test_output_diff.rb +38 -49
- metadata +9 -7
- data/ext/edn_turbo/edn_parser_util.cc +0 -424
- data/ext/edn_turbo/edn_parser_util.h +0 -11
- data/ext/edn_turbo/edn_parser_util_unicode.cc +0 -33
data/ext/edn_turbo/edn_parser.rl
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
#include <iostream>
|
2
2
|
#include <string>
|
3
|
+
#include <sstream>
|
3
4
|
#include <vector>
|
4
5
|
#include <exception>
|
5
6
|
#include <cstring>
|
6
7
|
|
7
8
|
#include <ruby/ruby.h>
|
8
9
|
|
9
|
-
#include "
|
10
|
+
#include "util.h"
|
11
|
+
#include "parser.h"
|
10
12
|
|
11
13
|
//
|
12
14
|
// EDN spec at: https://github.com/edn-format/edn
|
@@ -120,7 +122,7 @@
|
|
120
122
|
else if (std::strcmp(RSTRING_PTR(sym), "false") == 0) { v = Qfalse; }
|
121
123
|
else if (std::strcmp(RSTRING_PTR(sym), "nil") == 0) { v = Qnil; }
|
122
124
|
else {
|
123
|
-
v =
|
125
|
+
v = edn::util::call_module_fn(rb_mEDN, EDN_MAKE_SYMBOL_METHOD, sym);
|
124
126
|
}
|
125
127
|
fexec np;
|
126
128
|
}
|
@@ -207,7 +209,7 @@ const char *edn::Parser::parse_value(const char *p, const char *pe, VALUE& v)
|
|
207
209
|
write data;
|
208
210
|
|
209
211
|
action parse_chars {
|
210
|
-
if (
|
212
|
+
if (edn::util::parse_byte_stream(p_save + 1, p, v, encode)) {
|
211
213
|
fexec p + 1;
|
212
214
|
} else {
|
213
215
|
fhold; fbreak;
|
@@ -322,7 +324,7 @@ const char* edn::Parser::parse_decimal(const char *p, const char *pe, VALUE& v)
|
|
322
324
|
%% write exec;
|
323
325
|
|
324
326
|
if (cs >= EDN_decimal_first_final) {
|
325
|
-
v =
|
327
|
+
v = edn::util::float_to_ruby(p_save, p - p_save);
|
326
328
|
return p + 1;
|
327
329
|
}
|
328
330
|
else if (cs == EDN_decimal_en_main) {} // silence ragel warning
|
@@ -354,7 +356,7 @@ const char* edn::Parser::parse_integer(const char *p, const char *pe, VALUE& v)
|
|
354
356
|
%% write exec;
|
355
357
|
|
356
358
|
if (cs >= EDN_integer_first_final) {
|
357
|
-
v =
|
359
|
+
v = edn::util::integer_to_ruby(p_save, p - p_save);
|
358
360
|
return p + 1;
|
359
361
|
}
|
360
362
|
else if (cs == EDN_integer_en_main) {} // silence ragel warning
|
@@ -382,7 +384,7 @@ const char* edn::Parser::parse_integer(const char *p, const char *pe, VALUE& v)
|
|
382
384
|
const char *np = parse_symbol(p_save, pe, sym);
|
383
385
|
if (np == NULL) { fexec pe; } else {
|
384
386
|
if (sym != Qnil)
|
385
|
-
v =
|
387
|
+
v = edn::util::call_module_fn(rb_mEDN, EDN_MAKE_SYMBOL_METHOD, sym);
|
386
388
|
fexec np;
|
387
389
|
}
|
388
390
|
}
|
@@ -414,7 +416,7 @@ const char* edn::Parser::parse_integer(const char *p, const char *pe, VALUE& v)
|
|
414
416
|
// stand-alone operators (-, +, /, ... etc)
|
415
417
|
char op[2] = { *p_save, 0 };
|
416
418
|
VALUE sym = rb_str_new2(op);
|
417
|
-
v =
|
419
|
+
v = edn::util::call_module_fn(rb_mEDN, EDN_MAKE_SYMBOL_METHOD, sym);
|
418
420
|
}
|
419
421
|
|
420
422
|
valid_non_numeric_chars = alpha|operators|':'|'#';
|
@@ -479,7 +481,7 @@ const char* edn::Parser::parse_esc_char(const char *p, const char *pe, VALUE& v)
|
|
479
481
|
|
480
482
|
if (cs >= EDN_escaped_char_first_final) {
|
481
483
|
// convert the escaped value to a character
|
482
|
-
if (!
|
484
|
+
if (!edn::util::parse_escaped_char(p_save + 1, p, v)) {
|
483
485
|
return pe;
|
484
486
|
}
|
485
487
|
return p;
|
@@ -601,7 +603,7 @@ const char* edn::Parser::parse_symbol(const char *p, const char *pe, VALUE& s)
|
|
601
603
|
// parse_value() read an element we care
|
602
604
|
// about. Bind the metadata to it and add it to
|
603
605
|
// the sequence
|
604
|
-
e =
|
606
|
+
e = edn::util::call_module_fn(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, e, ruby_meta());
|
605
607
|
rb_ary_push(elems, e);
|
606
608
|
}
|
607
609
|
} else {
|
@@ -692,7 +694,7 @@ const char* edn::Parser::parse_list(const char *p, const char *pe, VALUE& v)
|
|
692
694
|
if (cs >= EDN_list_first_final) {
|
693
695
|
v = elems;
|
694
696
|
// TODO: replace with this but first figure out why array is not unrolled by EDN::list()
|
695
|
-
// v =
|
697
|
+
// v = edn::util::call_module_fn(EDN_MAKE_LIST_METHOD, elems);
|
696
698
|
return p + 1;
|
697
699
|
}
|
698
700
|
else if (cs == EDN_list_error) {
|
@@ -853,7 +855,7 @@ const char* edn::Parser::parse_set(const char *p, const char *pe, VALUE& v)
|
|
853
855
|
|
854
856
|
if (cs >= EDN_set_first_final) {
|
855
857
|
// all elements collected; now convert to a set
|
856
|
-
v =
|
858
|
+
v = edn::util::call_module_fn(rb_mEDN, EDN_MAKE_SET_METHOD, elems);
|
857
859
|
return p + 1;
|
858
860
|
}
|
859
861
|
else if (cs == EDN_set_error) {
|
@@ -998,14 +1000,14 @@ const char* edn::Parser::parse_tagged(const char *p, const char *pe, VALUE& v)
|
|
998
1000
|
|
999
1001
|
if (!sym_ok || !data_ok) {
|
1000
1002
|
error(__FUNCTION__, "tagged element symbol error", *p);
|
1001
|
-
v =
|
1003
|
+
v = EDN_EOF_CONST;
|
1002
1004
|
return NULL;
|
1003
1005
|
}
|
1004
1006
|
|
1005
1007
|
try {
|
1006
1008
|
// tagged_element makes a call to ruby which may throw an
|
1007
1009
|
// exception when parsing the data
|
1008
|
-
v =
|
1010
|
+
v = edn::util::call_module_fn(rb_mEDN, EDN_TAGGED_ELEM_METHOD, sym_name, data);
|
1009
1011
|
return p + 1;
|
1010
1012
|
} catch (std::exception& e) {
|
1011
1013
|
error(__FUNCTION__, e.what());
|
@@ -1016,7 +1018,7 @@ const char* edn::Parser::parse_tagged(const char *p, const char *pe, VALUE& v)
|
|
1016
1018
|
error(__FUNCTION__, "tagged element symbol error", *p);
|
1017
1019
|
}
|
1018
1020
|
else if (cs == EDN_tagged_en_main) {} // silence ragel warning
|
1019
|
-
v =
|
1021
|
+
v = EDN_EOF_CONST;
|
1020
1022
|
return NULL;
|
1021
1023
|
}
|
1022
1024
|
|
@@ -1090,7 +1092,7 @@ const char* edn::Parser::parse_meta(const char *p, const char *pe)
|
|
1090
1092
|
// metadata sequence to it
|
1091
1093
|
if (!meta_empty() && meta_size() == meta_sz) {
|
1092
1094
|
// this will empty the metadata sequence too
|
1093
|
-
result =
|
1095
|
+
result = edn::util::call_module_fn(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, result, ruby_meta());
|
1094
1096
|
}
|
1095
1097
|
fexec np;
|
1096
1098
|
}
|
@@ -1107,7 +1109,7 @@ const char* edn::Parser::parse_meta(const char *p, const char *pe)
|
|
1107
1109
|
VALUE edn::Parser::parse(const char* src, std::size_t len)
|
1108
1110
|
{
|
1109
1111
|
int cs;
|
1110
|
-
VALUE result =
|
1112
|
+
VALUE result = EDN_EOF_CONST;
|
1111
1113
|
|
1112
1114
|
%% write init;
|
1113
1115
|
set_source(src, len);
|
@@ -1115,7 +1117,7 @@ VALUE edn::Parser::parse(const char* src, std::size_t len)
|
|
1115
1117
|
|
1116
1118
|
if (cs == EDN_parser_error) {
|
1117
1119
|
error(__FUNCTION__, *p);
|
1118
|
-
return
|
1120
|
+
return EDN_EOF_CONST;
|
1119
1121
|
}
|
1120
1122
|
else if (cs == EDN_parser_first_final) {
|
1121
1123
|
p = pe = eof = NULL;
|
@@ -1152,7 +1154,7 @@ VALUE edn::Parser::parse(const char* src, std::size_t len)
|
|
1152
1154
|
else {
|
1153
1155
|
// a value was read and there's a pending metadata
|
1154
1156
|
// sequence. Bind them.
|
1155
|
-
value =
|
1157
|
+
value = edn::util::call_module_fn(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, value, ruby_meta());
|
1156
1158
|
state = TOKEN_OK;
|
1157
1159
|
}
|
1158
1160
|
} else if (!discard.empty()) {
|
data/ext/edn_turbo/extconf.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mkmf'
|
2
4
|
|
3
5
|
HEADER_DIRS = [
|
@@ -13,17 +15,11 @@ LIB_DIRS = [
|
|
13
15
|
|
14
16
|
dir_config('edn_ext', HEADER_DIRS, LIB_DIRS)
|
15
17
|
|
16
|
-
# feels very hackish to do this
|
17
|
-
if RUBY_PLATFORM
|
18
|
-
$CXXFLAGS << ' -stdlib=libc++ -std=c++11'
|
19
|
-
end
|
18
|
+
# feels very hackish to do this but the new icu4c needs it on MacOS
|
19
|
+
$CXXFLAGS << ' -stdlib=libc++ -std=c++11' if RUBY_PLATFORM.match?(/darwin/)
|
20
20
|
|
21
|
-
unless find_header('unicode/uversion.h')
|
22
|
-
abort "icu4c headers missing"
|
23
|
-
end
|
21
|
+
abort 'icu4c headers missing' unless find_header('unicode/uversion.h')
|
24
22
|
|
25
|
-
unless have_library('icuuc')
|
26
|
-
abort "ic4c lib missing"
|
27
|
-
end
|
23
|
+
abort 'ic4c lib missing' unless have_library('icuuc')
|
28
24
|
|
29
|
-
create_makefile(
|
25
|
+
create_makefile('edn_turbo/edn_turbo')
|
data/ext/edn_turbo/main.cc
CHANGED
@@ -6,28 +6,27 @@
|
|
6
6
|
#include <ruby/ruby.h>
|
7
7
|
#include <ruby/io.h>
|
8
8
|
|
9
|
-
#include "
|
9
|
+
#include "parser.h"
|
10
10
|
|
11
11
|
namespace edn {
|
12
12
|
|
13
|
+
VALUE rb_mEDN;
|
13
14
|
VALUE rb_mEDNT;
|
14
15
|
|
15
16
|
// Symbols used to call into the ruby world.
|
16
|
-
VALUE
|
17
|
+
VALUE EDN_MAKE_SYMBOL_METHOD = Qnil;
|
18
|
+
VALUE EDN_MAKE_LIST_METHOD = Qnil;
|
19
|
+
VALUE EDN_MAKE_SET_METHOD = Qnil;
|
20
|
+
VALUE EDN_MAKE_BIG_DECIMAL_METHOD = Qnil;
|
21
|
+
VALUE EDN_TAGGED_ELEM_METHOD = Qnil;
|
22
|
+
VALUE EDNT_EXTENDED_VALUE_METHOD = Qnil;
|
17
23
|
|
18
|
-
VALUE
|
19
|
-
VALUE
|
20
|
-
VALUE
|
21
|
-
VALUE EDNT_MAKE_BIG_DECIMAL_METHOD = Qnil;
|
22
|
-
VALUE EDNT_TAGGED_ELEM_METHOD = Qnil;
|
23
|
-
VALUE EDNT_EXTENDED_VALUE_METHOD = Qnil;
|
24
|
-
|
25
|
-
VALUE EDNT_STRING_TO_I_METHOD = Qnil;
|
26
|
-
VALUE EDNT_STRING_TO_F_METHOD = Qnil;
|
27
|
-
VALUE EDNT_READ_METHOD = Qnil;
|
24
|
+
VALUE RUBY_STRING_TO_I_METHOD = Qnil;
|
25
|
+
VALUE RUBY_STRING_TO_F_METHOD = Qnil;
|
26
|
+
VALUE RUBY_READ_METHOD = Qnil;
|
28
27
|
|
29
28
|
// returned when EOF - defined as a constant in EDN module
|
30
|
-
VALUE
|
29
|
+
VALUE EDN_EOF_CONST = Qnil;
|
31
30
|
|
32
31
|
//
|
33
32
|
// Wrappers to hook the class w/ the C-api.
|
@@ -108,7 +107,7 @@ namespace edn {
|
|
108
107
|
// this is very inefficient as it'll require read()
|
109
108
|
// calls from the ruby side (involves a lot of data
|
110
109
|
// wrapping, etc)
|
111
|
-
if (rb_respond_to(data,
|
110
|
+
if (rb_respond_to(data, RUBY_READ_METHOD)) {
|
112
111
|
p->set_source(data);
|
113
112
|
break;
|
114
113
|
}
|
@@ -174,6 +173,7 @@ void Init_edn_turbo(void)
|
|
174
173
|
rb_raise(rb_eRuntimeError, "Extension init error calling setlocale() - It appears your system's locale is not configured correctly.\n");
|
175
174
|
}
|
176
175
|
|
176
|
+
edn::rb_mEDN = rb_const_get(rb_cObject, rb_intern("EDN"));
|
177
177
|
edn::rb_mEDNT = rb_define_module("EDNT");
|
178
178
|
|
179
179
|
// bind the ruby Parser class to the C++ one
|
@@ -187,19 +187,20 @@ void Init_edn_turbo(void)
|
|
187
187
|
rb_define_method(rb_cParser, "read", (VALUE(*)(ANYARGS)) &edn::next, 0 );
|
188
188
|
|
189
189
|
// bind ruby methods we'll call - these should be defined in edn_turbo.rb
|
190
|
-
edn::
|
191
|
-
edn::
|
192
|
-
edn::
|
193
|
-
edn::
|
194
|
-
edn::
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
edn::
|
190
|
+
edn::EDN_MAKE_SYMBOL_METHOD = rb_intern("symbol");
|
191
|
+
edn::EDN_MAKE_LIST_METHOD = rb_intern("list");
|
192
|
+
edn::EDN_MAKE_SET_METHOD = rb_intern("set");
|
193
|
+
edn::EDN_MAKE_BIG_DECIMAL_METHOD = rb_intern("big_decimal");
|
194
|
+
edn::EDN_TAGGED_ELEM_METHOD = rb_intern("tagged_element");
|
195
|
+
|
196
|
+
// defined in EDNT - see edn_parser.rb
|
197
|
+
edn::EDNT_EXTENDED_VALUE_METHOD = rb_intern("extend_for_meta");
|
198
|
+
|
199
|
+
// ruby methods
|
200
|
+
edn::RUBY_STRING_TO_I_METHOD = rb_intern("to_i");
|
201
|
+
edn::RUBY_STRING_TO_F_METHOD = rb_intern("to_f");
|
202
|
+
edn::RUBY_READ_METHOD = rb_intern("read");
|
201
203
|
|
202
204
|
// so we can return EOF directly
|
203
|
-
|
204
|
-
edn::EDNT_EOF_CONST = rb_const_get(edn_module, rb_intern("EOF"));
|
205
|
+
edn::EDN_EOF_CONST = rb_const_get(edn::rb_mEDN, rb_intern("EOF"));
|
205
206
|
}
|
@@ -1,29 +1,12 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
3
|
#include <string>
|
4
|
-
#include <sstream>
|
5
4
|
#include <vector>
|
6
5
|
#include <stack>
|
7
6
|
|
8
7
|
|
9
8
|
namespace edn
|
10
9
|
{
|
11
|
-
extern VALUE rb_mEDNT;
|
12
|
-
extern VALUE EDN_MODULE_SYMBOL;
|
13
|
-
extern VALUE EDNT_MAKE_SYMBOL_METHOD;
|
14
|
-
extern VALUE EDNT_MAKE_LIST_METHOD;
|
15
|
-
extern VALUE EDNT_MAKE_SET_METHOD;
|
16
|
-
extern VALUE EDNT_MAKE_BIG_DECIMAL_METHOD;
|
17
|
-
extern VALUE EDNT_TAGGED_ELEM_METHOD;
|
18
|
-
extern VALUE EDNT_EXTENDED_VALUE_METHOD;
|
19
|
-
|
20
|
-
extern VALUE EDNT_STRING_TO_I_METHOD;
|
21
|
-
extern VALUE EDNT_STRING_TO_F_METHOD;
|
22
|
-
extern VALUE EDNT_READ_METHOD;
|
23
|
-
|
24
|
-
extern VALUE EDNT_EOF_CONST;
|
25
|
-
|
26
|
-
|
27
10
|
//
|
28
11
|
// C-extension EDN Parser class representation
|
29
12
|
class Parser
|
@@ -50,8 +33,6 @@ namespace edn
|
|
50
33
|
// returns the next element in the current stream
|
51
34
|
VALUE next();
|
52
35
|
|
53
|
-
static void throw_error(int error);
|
54
|
-
|
55
36
|
private:
|
56
37
|
// ragel needs these
|
57
38
|
const char* p;
|
@@ -89,18 +70,6 @@ namespace edn
|
|
89
70
|
|
90
71
|
eTokenState parse_next(VALUE& value);
|
91
72
|
|
92
|
-
// defined in edn_parser_util.cc
|
93
|
-
static VALUE integer_to_ruby(const char* str, std::size_t len);
|
94
|
-
static VALUE float_to_ruby (const char* str, std::size_t len);
|
95
|
-
static VALUE ruby_io_read(VALUE io);
|
96
|
-
|
97
|
-
static bool parse_byte_stream (const char *p, const char *pe, VALUE& rslt, bool encode);
|
98
|
-
static bool parse_escaped_char(const char *p, const char *pe, VALUE& rslt);
|
99
|
-
|
100
|
-
static VALUE make_edn_type(ID method, VALUE value);
|
101
|
-
static VALUE make_edn_type(ID method, VALUE value1, VALUE value2);
|
102
|
-
static VALUE make_edn_type(VALUE module, ID method, VALUE value1, VALUE value2);
|
103
|
-
|
104
73
|
// metadata
|
105
74
|
VALUE ruby_meta();
|
106
75
|
void new_meta_list() { metadata.push( new std::vector<VALUE>() ); }
|
@@ -109,17 +78,6 @@ namespace edn
|
|
109
78
|
bool meta_empty() const { return metadata.top()->empty(); }
|
110
79
|
std::size_t meta_size() const { return metadata.top()->size(); }
|
111
80
|
|
112
|
-
// utility method to convert a primitive in string form to a
|
113
|
-
// ruby type
|
114
|
-
template <class T>
|
115
|
-
static inline T buftotype(const char* p, std::size_t len) {
|
116
|
-
T val;
|
117
|
-
std::string buf;
|
118
|
-
buf.append(p, len);
|
119
|
-
std::istringstream(buf) >> val;
|
120
|
-
return val;
|
121
|
-
}
|
122
|
-
|
123
81
|
void error(const std::string& f, const std::string& err, char c) const;
|
124
82
|
void error(const std::string& f, char err_c) const { error(f, "", err_c); }
|
125
83
|
void error(const std::string& f, const std::string& err_msg) const { error(f, err_msg, '\0'); }
|
@@ -0,0 +1,197 @@
|
|
1
|
+
#include <iostream>
|
2
|
+
#include <string>
|
3
|
+
#include <stack>
|
4
|
+
|
5
|
+
#include <ruby/ruby.h>
|
6
|
+
|
7
|
+
#include "parser.h"
|
8
|
+
#include "util.h"
|
9
|
+
|
10
|
+
namespace edn
|
11
|
+
{
|
12
|
+
//
|
13
|
+
// parser destructor
|
14
|
+
Parser::~Parser()
|
15
|
+
{
|
16
|
+
reset_state();
|
17
|
+
del_top_meta_list();
|
18
|
+
|
19
|
+
if (io_buffer) {
|
20
|
+
free(reinterpret_cast<void*>(io_buffer));
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
// =================================================================
|
25
|
+
// for token-by-token parsing. If a discard or metadata is parsed,
|
26
|
+
// attempt to get the following value
|
27
|
+
//
|
28
|
+
VALUE Parser::next()
|
29
|
+
{
|
30
|
+
VALUE token = EDN_EOF_CONST;
|
31
|
+
|
32
|
+
// buffer if reading from an IO
|
33
|
+
if (core_io || (read_io != Qnil)) {
|
34
|
+
fill_buf();
|
35
|
+
}
|
36
|
+
|
37
|
+
while (!is_eof())
|
38
|
+
{
|
39
|
+
// fetch a token. If it's metadata or discard
|
40
|
+
VALUE v = EDN_EOF_CONST;
|
41
|
+
eTokenState state = parse_next(v);
|
42
|
+
|
43
|
+
if (state == TOKEN_OK) {
|
44
|
+
// valid token
|
45
|
+
token = v;
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
else if (state == TOKEN_ERROR) {
|
49
|
+
token = EDN_EOF_CONST;
|
50
|
+
break;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
return token;
|
55
|
+
}
|
56
|
+
|
57
|
+
// reset parsing state
|
58
|
+
//
|
59
|
+
void Parser::reset_state()
|
60
|
+
{
|
61
|
+
line_number = 1;
|
62
|
+
discard.clear();
|
63
|
+
|
64
|
+
// remove any remaining levels except for the first
|
65
|
+
while (metadata.size() > 1) {
|
66
|
+
del_top_meta_list();
|
67
|
+
}
|
68
|
+
// but clear any metadata on the first
|
69
|
+
metadata.top()->clear();
|
70
|
+
|
71
|
+
// clean up
|
72
|
+
core_io = NULL;
|
73
|
+
read_io = Qnil;
|
74
|
+
p = pe = eof = NULL;
|
75
|
+
}
|
76
|
+
|
77
|
+
//
|
78
|
+
// set a new source
|
79
|
+
void Parser::set_source(const char* src, std::size_t len)
|
80
|
+
{
|
81
|
+
reset_state();
|
82
|
+
// set ragel state
|
83
|
+
p = src;
|
84
|
+
pe = src + len;
|
85
|
+
eof = pe;
|
86
|
+
}
|
87
|
+
|
88
|
+
void Parser::set_source(FILE* fp)
|
89
|
+
{
|
90
|
+
reset_state();
|
91
|
+
core_io = fp;
|
92
|
+
}
|
93
|
+
|
94
|
+
void Parser::set_source(VALUE str_io)
|
95
|
+
{
|
96
|
+
reset_state();
|
97
|
+
read_io = str_io;
|
98
|
+
}
|
99
|
+
|
100
|
+
//
|
101
|
+
// for IO sources, read and fill a buffer
|
102
|
+
void Parser::fill_buf()
|
103
|
+
{
|
104
|
+
std::string str_buf;
|
105
|
+
|
106
|
+
// read as much data available
|
107
|
+
if (core_io) {
|
108
|
+
// ruby core IO types
|
109
|
+
char c;
|
110
|
+
while (1)
|
111
|
+
{
|
112
|
+
c = fgetc(core_io);
|
113
|
+
if (c == EOF) {
|
114
|
+
break;
|
115
|
+
}
|
116
|
+
str_buf += c;
|
117
|
+
}
|
118
|
+
|
119
|
+
} else if (read_io != Qnil) {
|
120
|
+
// StringIO, etc. Call read() from ruby side
|
121
|
+
VALUE v = edn::util::ruby_io_read(read_io);
|
122
|
+
if (TYPE(v) == T_STRING) {
|
123
|
+
str_buf.assign( StringValuePtr(v), RSTRING_LEN(v));
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
// set the buffer to read from
|
128
|
+
if (str_buf.length() > 0) {
|
129
|
+
// first time when io_buffer is NULL, pe & p = 0
|
130
|
+
uintmax_t new_length = (pe - p) + str_buf.length();
|
131
|
+
if (new_length > (((uintmax_t) 1 << 32) - 1)) {
|
132
|
+
// icu -> 32-bit. TODO: handle
|
133
|
+
rb_raise(rb_eRuntimeError, "Unsupported string buffer length");
|
134
|
+
}
|
135
|
+
char* start = NULL;
|
136
|
+
|
137
|
+
// allocate or extend storage needed
|
138
|
+
if (!io_buffer) {
|
139
|
+
io_buffer = reinterpret_cast<char*>(malloc(new_length));
|
140
|
+
start = io_buffer;
|
141
|
+
} else if (io_buffer_len < new_length) {
|
142
|
+
// resize the buffer
|
143
|
+
io_buffer = reinterpret_cast<char*>(realloc(reinterpret_cast<void*>(io_buffer), new_length));
|
144
|
+
}
|
145
|
+
|
146
|
+
if (!start) {
|
147
|
+
// appending to the buffer but move the data not yet
|
148
|
+
// parsed first to the front
|
149
|
+
memmove(io_buffer, p, pe - p);
|
150
|
+
start = io_buffer + (pe - p);
|
151
|
+
}
|
152
|
+
|
153
|
+
// and copy
|
154
|
+
memcpy(start, str_buf.c_str(), str_buf.length());
|
155
|
+
io_buffer_len = (uint32_t) new_length;
|
156
|
+
|
157
|
+
// set ragel state
|
158
|
+
p = io_buffer;
|
159
|
+
pe = p + new_length;
|
160
|
+
eof = pe;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
// =================================================================
|
165
|
+
// METADATA
|
166
|
+
//
|
167
|
+
// returns an array of metadata value(s) saved in reverse order
|
168
|
+
// (right to left) - the ruby side will interpret this
|
169
|
+
VALUE Parser::ruby_meta()
|
170
|
+
{
|
171
|
+
VALUE m_ary = rb_ary_new();
|
172
|
+
|
173
|
+
// pop from the back of the top-most list
|
174
|
+
while (!metadata.top()->empty()) {
|
175
|
+
rb_ary_push(m_ary, metadata.top()->back());
|
176
|
+
metadata.top()->pop_back();
|
177
|
+
}
|
178
|
+
|
179
|
+
return m_ary;
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
// =================================================================
|
184
|
+
//
|
185
|
+
// error reporting
|
186
|
+
void Parser::error(const std::string& func, const std::string& err, char c) const
|
187
|
+
{
|
188
|
+
std::cerr << "Parse error "
|
189
|
+
// "from " << func << "() "
|
190
|
+
;
|
191
|
+
if (err.length() > 0)
|
192
|
+
std::cerr << "(" << err << ") ";
|
193
|
+
if (c != '\0')
|
194
|
+
std::cerr << "at '" << c << "' ";
|
195
|
+
std::cerr << "on line " << line_number << std::endl;
|
196
|
+
}
|
197
|
+
}
|