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.
@@ -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 "edn_parser.h"
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 = Parser::make_edn_type(EDNT_MAKE_SYMBOL_METHOD, sym);
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 (Parser::parse_byte_stream(p_save + 1, p, v, encode)) {
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 = Parser::float_to_ruby(p_save, p - p_save);
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 = Parser::integer_to_ruby(p_save, p - p_save);
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 = Parser::make_edn_type(EDNT_MAKE_SYMBOL_METHOD, sym);
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 = Parser::make_edn_type(EDNT_MAKE_SYMBOL_METHOD, sym);
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 (!Parser::parse_escaped_char(p_save + 1, p, v)) {
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 = Parser::make_edn_type(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, e, ruby_meta());
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 = Parser::make_edn_type(EDNT_MAKE_LIST_METHOD, elems);
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 = Parser::make_edn_type(EDNT_MAKE_SET_METHOD, elems);
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 = EDNT_EOF_CONST;
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 = Parser::make_edn_type(EDNT_TAGGED_ELEM_METHOD, sym_name, data);
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 = EDNT_EOF_CONST;
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 = Parser::make_edn_type(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, result, ruby_meta());
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 = EDNT_EOF_CONST;
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 EDNT_EOF_CONST;
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 = Parser::make_edn_type(rb_mEDNT, EDNT_EXTENDED_VALUE_METHOD, value, ruby_meta());
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()) {
@@ -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 =~ /darwin/
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("edn_turbo/edn_turbo")
25
+ create_makefile('edn_turbo/edn_turbo')
@@ -6,28 +6,27 @@
6
6
  #include <ruby/ruby.h>
7
7
  #include <ruby/io.h>
8
8
 
9
- #include "edn_parser.h"
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 EDN_MODULE_SYMBOL = Qnil;
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 EDNT_MAKE_SYMBOL_METHOD = Qnil;
19
- VALUE EDNT_MAKE_LIST_METHOD = Qnil;
20
- VALUE EDNT_MAKE_SET_METHOD = Qnil;
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 EDNT_EOF_CONST = Qnil;
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, EDNT_READ_METHOD)) {
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::EDN_MODULE_SYMBOL = rb_intern("EDN");
191
- edn::EDNT_MAKE_SYMBOL_METHOD = rb_intern("symbol");
192
- edn::EDNT_MAKE_LIST_METHOD = rb_intern("list");
193
- edn::EDNT_MAKE_SET_METHOD = rb_intern("set");
194
- edn::EDNT_MAKE_BIG_DECIMAL_METHOD = rb_intern("big_decimal");
195
- edn::EDNT_TAGGED_ELEM_METHOD = rb_intern("tagged_element");
196
- edn::EDNT_EXTENDED_VALUE_METHOD = rb_intern("extend_for_meta");
197
-
198
- edn::EDNT_STRING_TO_I_METHOD = rb_intern("to_i");
199
- edn::EDNT_STRING_TO_F_METHOD = rb_intern("to_f");
200
- edn::EDNT_READ_METHOD = rb_intern("read");
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
- VALUE edn_module = rb_const_get(rb_cObject, edn::EDN_MODULE_SYMBOL);
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
+ }