edn_turbo 0.3.0 → 0.3.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.
@@ -1,9 +1,9 @@
1
- #ifndef EDN_TURBO_PARSER_H
2
- #define EDN_TURBO_PARSER_H
1
+ #ifndef EDN_RAGEL_PARSER_H
2
+ #define EDN_RAGEL_PARSER_H
3
3
 
4
4
  #include <string>
5
5
  #include <sstream>
6
- #include <stack>
6
+ #include <vector>
7
7
 
8
8
  #include <ruby/ruby.h>
9
9
 
@@ -14,8 +14,10 @@ namespace edn
14
14
  extern VALUE EDNT_MAKE_EDN_SYMBOL;
15
15
  extern VALUE EDNT_MAKE_SET_METHOD;
16
16
  extern VALUE EDNT_TAGGED_ELEM;
17
+ extern VALUE EDNT_BIND_META_TO_VALUE;
17
18
  extern VALUE EDNT_STR_INT_TO_BIGNUM;
18
19
  extern VALUE EDNT_STR_DBL_TO_BIGNUM;
20
+ extern VALUE EDNT_EOF;
19
21
 
20
22
  //
21
23
  // C-extension EDN Parser class representation
@@ -24,11 +26,15 @@ namespace edn
24
26
  public:
25
27
  Parser() : p(NULL), pe(NULL), eof(NULL), line_number(1) { }
26
28
 
29
+ // change input source
27
30
  void set_source(const char* src, std::size_t len);
28
31
 
29
- bool is_eof() const { return (p != NULL && p == pe); }
32
+ bool is_eof() const { return (p == eof); }
33
+
34
+ // parses an entire stream
30
35
  VALUE parse(const char* s, std::size_t len);
31
- // VALUE read(const std::string& data) { return parse(data.c_str(), data.length()); }
36
+
37
+ // returns the next element in the current stream
32
38
  VALUE next();
33
39
 
34
40
  static void throw_error(int error);
@@ -39,9 +45,10 @@ namespace edn
39
45
  const char* pe;
40
46
  const char* eof;
41
47
  std::size_t line_number;
42
- std::stack<VALUE> discard;
48
+ std::vector<VALUE> discard;
49
+ std::vector<VALUE> metadata;
43
50
 
44
- void reset();
51
+ void reset_state();
45
52
 
46
53
  const char* parse_value (const char *p, const char *pe, VALUE& v);
47
54
  const char* parse_string (const char *p, const char *pe, VALUE& v);
@@ -58,6 +65,9 @@ namespace edn
58
65
  const char* parse_set (const char *p, const char *pe, VALUE& v);
59
66
  const char* parse_discard (const char *p, const char *pe);
60
67
  const char* parse_tagged (const char *p, const char *pe, VALUE& v);
68
+ const char* parse_meta (const char *p, const char *pe);
69
+
70
+ bool parse_next(VALUE& value);
61
71
 
62
72
  // defined in edn_parser_unicode.cc
63
73
  static bool to_utf8(const char *s, std::size_t len, std::string& rslt);
@@ -73,6 +83,10 @@ namespace edn
73
83
  static VALUE make_ruby_set(VALUE elems);
74
84
  static VALUE tagged_element(VALUE name, VALUE data);
75
85
 
86
+ // metadata
87
+ VALUE ruby_meta();
88
+ VALUE bind_meta_to_value(VALUE value);
89
+
76
90
  // utility method to convert a primitive in string form to a
77
91
  // ruby type
78
92
  template <class T>
@@ -1,6 +1,6 @@
1
1
  #include <iostream>
2
2
  #include <string>
3
- #include <stack>
3
+ #include <vector>
4
4
  #include <exception>
5
5
 
6
6
  #include "edn_parser.h"
@@ -25,18 +25,19 @@
25
25
 
26
26
  operators = [/\.\*!_\?$%&<>\=+\-];
27
27
  symbol_start = alpha;
28
- symbol_chars = symbol_start | digit | [\#:_\-\.];
28
+ symbol_chars = symbol_start | digit | [\#:_\-\.\'];
29
29
 
30
30
  begin_dispatch = '#';
31
31
  begin_keyword = ':';
32
32
  begin_char = '\\';
33
- begin_value = alnum | [:\"\{\[\(\\\#] | operators;
34
- begin_symbol = symbol_start;
35
33
  begin_vector = '[';
36
34
  begin_map = '{';
37
35
  begin_list = '(';
36
+ begin_meta = '^';
38
37
  string_delim = '"';
39
38
  begin_number = digit;
39
+ begin_value = alnum | [:\"\{\[\(\\\#^] | operators;
40
+ begin_symbol = symbol_start;
40
41
 
41
42
  symbol_name = symbol_start (symbol_chars)*;
42
43
  symbol = (symbol_name ('/' symbol_name)?);
@@ -145,6 +146,12 @@
145
146
  if (np == NULL) { fhold; fbreak; } else fexec np;
146
147
  }
147
148
 
149
+ action parse_meta {
150
+ // ^
151
+ const char *np = parse_meta(fpc, pe);
152
+ if (np == NULL) { fhold; fbreak; } else fexec np;
153
+ }
154
+
148
155
  action parse_dispatch {
149
156
  // handles tokens w/ leading # ("#_", "#{", and tagged elems)
150
157
  const char *np = parse_dispatch(fpc + 1, pe, v);
@@ -162,6 +169,7 @@
162
169
  begin_vector >parse_vector |
163
170
  begin_list >parse_list |
164
171
  begin_map >parse_map |
172
+ begin_meta >parse_meta |
165
173
  begin_dispatch >parse_dispatch
166
174
  ) %*exit;
167
175
  }%%
@@ -223,7 +231,6 @@ const char *edn::Parser::parse_value(const char *p, const char *pe, VALUE& v)
223
231
 
224
232
  const char* edn::Parser::parse_string(const char *p, const char *pe, VALUE& v)
225
233
  {
226
- // std::cerr << __FUNCTION__ << " - p: '" << p << "'" << std::endl;
227
234
  static const char* EDN_TYPE = "string";
228
235
  int cs;
229
236
  bool encode = false;
@@ -551,7 +558,7 @@ const char* edn::Parser::parse_symbol(const char *p, const char *pe, VALUE& s)
551
558
  // object is not meant to be kept due to a #_ so don't
552
559
  // push it into the list of elements
553
560
  if (!discard.empty()) {
554
- discard.pop();
561
+ discard.pop_back();
555
562
  }
556
563
  else {
557
564
  // otherwise we add it to the list of elements for the
@@ -834,17 +841,23 @@ const char* edn::Parser::parse_set(const char *p, const char *pe, VALUE& v)
834
841
  // this token is to be discard it so store it in the
835
842
  // discard stack - we really don't need to save it so this
836
843
  // could be simplified
837
- discard.push(v);
844
+ discard.push_back(v);
838
845
  fexec np;
839
846
  } else {
840
847
  fhold; fbreak;
841
848
  }
842
849
  }
843
850
 
851
+ action discard_err {
852
+ std::stringstream s;
853
+ s << "discard sequence without element to discard";
854
+ error(__FUNCTION__, s.str());
855
+ fhold; fbreak;
856
+ }
844
857
 
845
858
  main := begin_discard ignore* (
846
859
  begin_value >discard_value
847
- ) @exit;
860
+ ) @err(discard_err) @exit;
848
861
  }%%
849
862
 
850
863
 
@@ -942,6 +955,51 @@ const char* edn::Parser::parse_tagged(const char *p, const char *pe, VALUE& v)
942
955
 
943
956
 
944
957
 
958
+ // ============================================================
959
+ // metadata - looks like ruby just discards this but we'll track it
960
+ // and provide a means to retrive after each parse op - might be
961
+ // useful?
962
+ //
963
+ %%{
964
+ machine EDN_meta;
965
+ include EDN_common;
966
+
967
+ write data;
968
+
969
+ action parse_meta {
970
+ const char *np = parse_value(fpc, pe, v);
971
+ if (np) { fexec np; } else { fhold; fbreak; }
972
+ }
973
+
974
+ main := begin_meta (
975
+ begin_value >parse_meta
976
+ ) @exit;
977
+ }%%
978
+
979
+
980
+ const char* edn::Parser::parse_meta(const char *p, const char *pe)
981
+ {
982
+ int cs;
983
+ VALUE v;
984
+
985
+ %% write init;
986
+ %% write exec;
987
+
988
+ if (cs >= EDN_meta_first_final) {
989
+ metadata.push_back(v);
990
+ return p + 1;
991
+ }
992
+ else if (cs == EDN_meta_error) {
993
+ error(__FUNCTION__, *p);
994
+ return pe;
995
+ }
996
+ else if (cs == EDN_meta_en_main) {} // silence ragel warning
997
+
998
+ return NULL;
999
+ }
1000
+
1001
+
1002
+
945
1003
  // ============================================================
946
1004
  // parses entire input but expects single valid token at the
947
1005
  // top-level, therefore, does not tokenize source stream
@@ -953,8 +1011,21 @@ const char* edn::Parser::parse_tagged(const char *p, const char *pe, VALUE& v)
953
1011
  write data;
954
1012
 
955
1013
  action parse_value {
1014
+ // save the count of metadata items before we parse this value
1015
+ // so we can determine if we've read another metadata value or
1016
+ // an actual data item
1017
+ std::size_t meta_size = metadata.size();
956
1018
  const char* np = parse_value(fpc, pe, result);
957
- if (np == NULL) { fexec pe; fbreak; } else fexec np;
1019
+ if (np == NULL) { fexec pe; fbreak; } else {
1020
+ // if we have metadata saved and it matches the count we
1021
+ // saved before we parsed a value, then we must bind the
1022
+ // metadata sequence to it
1023
+ if (!metadata.empty() && metadata.size() == meta_size) {
1024
+ // this will empty the metadata sequence too
1025
+ result = bind_meta_to_value(result);
1026
+ }
1027
+ fexec np;
1028
+ }
958
1029
  }
959
1030
 
960
1031
  element = begin_value >parse_value;
@@ -970,23 +1041,17 @@ VALUE edn::Parser::parse(const char* src, std::size_t len)
970
1041
  int cs;
971
1042
  VALUE result = Qnil;
972
1043
 
973
- // reset line counter & discard stack
974
- reset();
975
-
976
1044
  %% write init;
977
- p = src;
978
- pe = p + len;
979
- eof = pe;
1045
+ set_source(src, len);
980
1046
  %% write exec;
981
1047
 
982
1048
  if (cs == EDN_parser_error) {
983
- error(__FUNCTION__, *p);
984
- return Qnil;
1049
+ if (p)
1050
+ error(__FUNCTION__, *p);
1051
+ return EDNT_EOF;
985
1052
  }
986
1053
  else if (cs == EDN_parser_first_final) {
987
- // whole source is parsed so reset
988
1054
  p = pe = eof = NULL;
989
- reset();
990
1055
  }
991
1056
  else if (cs == EDN_parser_en_main) {} // silence ragel warning
992
1057
  return result;
@@ -1000,32 +1065,64 @@ VALUE edn::Parser::parse(const char* src, std::size_t len)
1000
1065
  machine EDN_tokens;
1001
1066
  include EDN_common;
1002
1067
 
1003
- write data noerror nofinal;
1068
+ write data nofinal;
1004
1069
 
1005
1070
  action parse_value {
1006
- const char* np = parse_value(fpc, pe, result);
1007
- if (np == NULL) { fhold; fbreak; } else { fexec np; }
1008
- }
1071
+ // we won't know if we've parsed a discard or a metadata until
1072
+ // after parse_value() is done. Save the current number of
1073
+ // elements in the metadata sequence; then we can check if it
1074
+ // grew or if the discard sequence grew
1075
+ meta_size = metadata.size();
1009
1076
 
1010
- element = begin_value >parse_value;
1077
+ const char* np = parse_value(fpc, pe, value);
1078
+
1079
+ if (np == NULL) { fhold; fbreak; } else {
1080
+ if (metadata.size() > 0) {
1081
+ // was anotheran additional metadata entry read? if
1082
+ // so, don't return a value
1083
+ if (metadata.size() > meta_size) {
1084
+ is_value = false;
1085
+ }
1086
+ else {
1087
+ // a value was read and there's a pending metadata
1088
+ // sequence. Bind them.
1089
+ value = bind_meta_to_value(value);
1090
+ }
1091
+ } else if (!discard.empty()) {
1092
+ // a discard read. Don't return a value
1093
+ is_value = false;
1094
+ }
1095
+ fexec np;
1096
+ }
1097
+ }
1011
1098
 
1012
- main := ignore* element ignore*;
1099
+ main := ignore* begin_value >parse_value ignore*;
1013
1100
  }%%
1014
1101
 
1015
1102
 
1016
1103
  //
1017
1104
  //
1018
- VALUE edn::Parser::next()
1105
+ bool edn::Parser::parse_next(VALUE& value)
1019
1106
  {
1020
- VALUE result = Qnil;
1021
1107
  int cs;
1108
+ bool is_value = true;
1109
+ // need to track metadada read and bind it to the next value read
1110
+ // - but must account for sequences of metadata values
1111
+ std::size_t meta_size;
1112
+
1113
+ // clear any previously saved discards; only track if read during
1114
+ // this op
1115
+ discard.clear();
1022
1116
 
1023
1117
  %% write init;
1024
1118
  %% write exec;
1025
1119
 
1026
- if (cs == EDN_tokens_en_main) {} // silence ragel warning
1120
+ if (cs == EDN_parser_error) {
1121
+ value = EDNT_EOF;
1122
+ }
1123
+ else if (cs == EDN_tokens_en_main) {} // silence ragel warning
1027
1124
 
1028
- return result;
1125
+ return is_value;
1029
1126
  }
1030
1127
 
1031
1128
 
@@ -1,5 +1,4 @@
1
1
  #include <iostream>
2
- #include <iomanip>
3
2
  #include <string>
4
3
  #include <limits>
5
4
  #include <exception>
@@ -26,20 +25,42 @@ namespace edn
26
25
 
27
26
 
28
27
  // =================================================================
28
+ // for token-by-token parsing. If a discard or metadata is parsed,
29
+ // attempt to get the following value
30
+ //
31
+ VALUE Parser::next()
32
+ {
33
+ VALUE token = EDNT_EOF;
34
+
35
+ while (!is_eof())
36
+ {
37
+ // fetch a token. If it's metadata or discard
38
+ VALUE v;
39
+ if (parse_next(v))
40
+ {
41
+ // valid token
42
+ token = v;
43
+ break;
44
+ }
45
+ }
46
+
47
+ return token;
48
+ }
49
+
29
50
  // reset parsing state
30
51
  //
31
- void Parser::reset()
52
+ void Parser::reset_state()
32
53
  {
33
54
  line_number = 1;
34
- while (!discard.empty())
35
- discard.pop();
55
+ discard.clear();
56
+ metadata.clear();
36
57
  }
37
58
 
38
59
  //
39
60
  // set a new source
40
61
  void Parser::set_source(const char* src, std::size_t len)
41
62
  {
42
- reset();
63
+ reset_state();
43
64
  // set ragel state
44
65
  p = src;
45
66
  pe = src + len;
@@ -55,16 +76,20 @@ namespace edn
55
76
 
56
77
  // we're using at most 2 args
57
78
  struct prot_args {
58
- prot_args(ID m, VALUE arg) :
59
- method(m), count(1) {
79
+ prot_args(ID r, ID m, VALUE arg) :
80
+ receiver(r), method(m), count(1) {
60
81
  args[0] = arg;
61
82
  }
62
- prot_args(ID m, VALUE arg1, VALUE arg2) :
63
- method(m), count(2) {
83
+ prot_args(ID r, ID m, VALUE arg1, VALUE arg2) :
84
+ receiver(r), method(m), count(2) {
64
85
  args[0] = arg1;
65
86
  args[1] = arg2;
66
87
  }
67
88
 
89
+ VALUE call() const { return rb_funcall2( receiver, method, count, args ); }
90
+
91
+ private:
92
+ ID receiver;
68
93
  ID method;
69
94
  VALUE count;
70
95
  VALUE args[2];
@@ -73,8 +98,10 @@ namespace edn
73
98
  // this allows us to wrap with rb_protect()
74
99
  static inline VALUE edn_wrap_funcall2( VALUE arg )
75
100
  {
76
- prot_args* a = reinterpret_cast<prot_args*>(arg);
77
- return rb_funcall2( edn::rb_mEDNT, a->method, a->count, a->args );
101
+ const prot_args* a = reinterpret_cast<const prot_args*>(arg);
102
+ if (a)
103
+ return a->call();
104
+ return Qnil;
78
105
  }
79
106
 
80
107
  static inline VALUE edn_prot_rb_funcall( edn_rb_f_type func, VALUE args )
@@ -113,7 +140,7 @@ namespace edn
113
140
 
114
141
  // value is outside of range of long type. Use ruby to convert it
115
142
  VALUE rb_s = edn_prot_rb_new_str( str );
116
- prot_args args(EDNT_STR_INT_TO_BIGNUM, rb_s);
143
+ prot_args args(rb_mEDNT, EDNT_STR_INT_TO_BIGNUM, rb_s);
117
144
  return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
118
145
  }
119
146
 
@@ -127,7 +154,7 @@ namespace edn
127
154
  }
128
155
 
129
156
  // value is outside of range of long type. Use ruby to convert it
130
- prot_args args(EDNT_STR_DBL_TO_BIGNUM, edn_prot_rb_new_str(str));
157
+ prot_args args(rb_mEDNT, EDNT_STR_DBL_TO_BIGNUM, edn_prot_rb_new_str(str));
131
158
  return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
132
159
  }
133
160
 
@@ -193,7 +220,7 @@ namespace edn
193
220
  // get a set representation from the ruby side. See edn_turbo.rb
194
221
  VALUE Parser::make_edn_symbol(VALUE sym)
195
222
  {
196
- prot_args args(edn::EDNT_MAKE_EDN_SYMBOL, sym);
223
+ prot_args args(rb_mEDNT, EDNT_MAKE_EDN_SYMBOL, sym);
197
224
  return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
198
225
  }
199
226
 
@@ -201,7 +228,7 @@ namespace edn
201
228
  // get a set representation from the ruby side. See edn_turbo.rb
202
229
  VALUE Parser::make_ruby_set(VALUE elems)
203
230
  {
204
- prot_args args(edn::EDNT_MAKE_SET_METHOD, elems);
231
+ prot_args args(rb_mEDNT, EDNT_MAKE_SET_METHOD, elems);
205
232
  return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
206
233
  }
207
234
 
@@ -209,11 +236,38 @@ namespace edn
209
236
  // get an object representation from the ruby side using the given symbol name
210
237
  VALUE Parser::tagged_element(VALUE name, VALUE data)
211
238
  {
212
- prot_args args(edn::EDNT_TAGGED_ELEM, name, data);
239
+ prot_args args(rb_mEDNT, EDNT_TAGGED_ELEM, name, data);
213
240
  return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
214
241
  }
215
242
 
216
243
 
244
+ // =================================================================
245
+ // METADATA
246
+ //
247
+ // returns an array of metadata value(s) saved in reverse order
248
+ // (right to left) - the ruby side will interpret this
249
+ VALUE Parser::ruby_meta()
250
+ {
251
+ VALUE m = rb_ary_new();
252
+
253
+ while (!metadata.empty()) {
254
+ rb_ary_push(m, metadata.back());
255
+ metadata.pop_back();
256
+ }
257
+
258
+ return m;
259
+ }
260
+
261
+ //
262
+ // calls the ruby-side bind_meta to bind the metadata to the value
263
+ VALUE Parser::bind_meta_to_value(VALUE value)
264
+ {
265
+ prot_args args(rb_mEDNT, EDNT_BIND_META_TO_VALUE, value, ruby_meta());
266
+ return edn_prot_rb_funcall( edn_wrap_funcall2, reinterpret_cast<VALUE>(&args) );
267
+ }
268
+
269
+
270
+ // =================================================================
217
271
  //
218
272
  // error reporting
219
273
  void Parser::throw_error(int error)
@@ -12,11 +12,15 @@ namespace edn {
12
12
  VALUE rb_mEDNT;
13
13
 
14
14
  // methods on the ruby side we'll call from here
15
- VALUE EDNT_MAKE_EDN_SYMBOL = Qnil;
16
- VALUE EDNT_MAKE_SET_METHOD = Qnil;
17
- VALUE EDNT_TAGGED_ELEM = Qnil;
18
- VALUE EDNT_STR_INT_TO_BIGNUM = Qnil;
19
- VALUE EDNT_STR_DBL_TO_BIGNUM = Qnil;
15
+ VALUE EDNT_MAKE_EDN_SYMBOL = Qnil;
16
+ VALUE EDNT_MAKE_SET_METHOD = Qnil;
17
+ VALUE EDNT_TAGGED_ELEM = Qnil;
18
+ VALUE EDNT_BIND_META_TO_VALUE = Qnil;
19
+ VALUE EDNT_STR_INT_TO_BIGNUM = Qnil;
20
+ VALUE EDNT_STR_DBL_TO_BIGNUM = Qnil;
21
+
22
+ // returned when EOF - defined as a constant in EDN module
23
+ VALUE EDNT_EOF = Qnil;
20
24
 
21
25
  //
22
26
  // wrappers to hook the class w/ the C-api
@@ -82,7 +86,9 @@ namespace edn {
82
86
  static VALUE read(VALUE self, VALUE data)
83
87
  {
84
88
  const char* stream = StringValueCStr(data);
85
- return get_parser(self)->parse(stream, std::strlen(stream) );
89
+ if (stream)
90
+ return get_parser(self)->parse(stream, std::strlen(stream) );
91
+ return Qnil;
86
92
  }
87
93
 
88
94
  //
@@ -130,11 +136,15 @@ void Init_edn_turbo(void)
130
136
  rb_define_method(rb_cParser, "ext_next", (VALUE(*)(ANYARGS)) &edn::next, 0 );
131
137
 
132
138
  // bind ruby methods we'll call - these should be defined in edn_turbo.rb
133
- edn::EDNT_MAKE_EDN_SYMBOL = rb_intern("make_edn_symbol");
134
- edn::EDNT_MAKE_SET_METHOD = rb_intern("make_set");
135
- edn::EDNT_TAGGED_ELEM = rb_intern("tagged_element");
136
- edn::EDNT_STR_INT_TO_BIGNUM = rb_intern("string_int_to_bignum");
137
- edn::EDNT_STR_DBL_TO_BIGNUM = rb_intern("string_double_to_bignum");
139
+ edn::EDNT_MAKE_EDN_SYMBOL = rb_intern("make_edn_symbol");
140
+ edn::EDNT_MAKE_SET_METHOD = rb_intern("make_set");
141
+ edn::EDNT_TAGGED_ELEM = rb_intern("tagged_element");
142
+ edn::EDNT_BIND_META_TO_VALUE = rb_intern("bind_metadata_to_value");
143
+ edn::EDNT_STR_INT_TO_BIGNUM = rb_intern("string_int_to_bignum");
144
+ edn::EDNT_STR_DBL_TO_BIGNUM = rb_intern("string_double_to_bignum");
145
+
146
+ // so we can return EOF directly
147
+ edn::EDNT_EOF = rb_const_get(edn::rb_mEDNT, rb_intern("EOF"));
138
148
 
139
149
  // import whatever else we've defined in the ruby side
140
150
  rb_require("edn_turbo/edn_parser");
@@ -1,3 +1,5 @@
1
+ require 'edn'
2
+
1
3
  module EDNT
2
4
 
3
5
  EOF = Object.new
@@ -13,7 +15,6 @@ module EDNT
13
15
 
14
16
  # token-by-token read
15
17
  def read
16
- return EOF if ext_eof
17
18
  ext_next
18
19
  end
19
20
 
@@ -1,4 +1,4 @@
1
1
  module EDNT
2
- VERSION = '0.3.0'
3
- RELEASE_DATE = %q{2015-06-12}
2
+ VERSION = '0.3.1'
3
+ RELEASE_DATE = %q{2015-06-18}
4
4
  end
data/lib/edn_turbo.rb CHANGED
@@ -91,4 +91,34 @@ module EDNT
91
91
  str.to_f
92
92
  end
93
93
 
94
+ # ============================================================================
95
+ # emulate EDN::Metadata
96
+ module Metadata
97
+ attr_accessor :metadata
98
+ end
99
+
100
+ # ----------------------------------------------------------------------------
101
+ # bind the given meta to the value
102
+ #
103
+ def self.bind_metadata_to_value(value, ext_meta)
104
+
105
+ meta = ext_meta
106
+
107
+ metadata = meta.reduce({}) do |acc, m|
108
+ case m
109
+ when Symbol then acc.merge(m => true)
110
+ when EDN::Type::Symbol then acc.merge(:tag => m)
111
+ else acc.merge(m)
112
+ end
113
+ end
114
+
115
+ if !metadata.empty?
116
+ value.extend Metadata
117
+ value.metadata = metadata
118
+ end
119
+
120
+ value
121
+ end
122
+
123
+
94
124
  end # EDN namespace
@@ -84,6 +84,18 @@ class EDNT_Test < Minitest::Test
84
84
 
85
85
  end
86
86
 
87
+ def test_symbol
88
+
89
+ check_file('test/symbol.edn',
90
+ [
91
+ EDN::Type::Symbol.new("asymbol"),
92
+ EDN::Type::Symbol.new("with'_a_'"),
93
+ EDN::Type::Symbol.new("with.123"),
94
+ ]
95
+ )
96
+
97
+ end
98
+
87
99
  def test_unicode
88
100
 
89
101
  check_file('test/unicode.edn',
@@ -129,6 +141,17 @@ class EDNT_Test < Minitest::Test
129
141
  )
130
142
  end
131
143
 
144
+
145
+ def test_metadata
146
+ f = EDNT::read(File.open('test/metadata.edn').read)
147
+ assert_equal([98.6, 99.7], f)
148
+ assert_equal({:doc=>"This is my vector", :rel=>:temps}, f.metadata)
149
+
150
+ f = EDNT::read(File.open('test/metadata2.edn').read)
151
+ assert_equal([1, 2], f)
152
+ assert_equal({:foo=>true, :tag=>EDN::Type::Symbol.new('String'), :bar=>2}, f.metadata)
153
+ end
154
+
132
155
  #
133
156
  # for testing tagged element #edn_turbo/test_tagged
134
157
  class Tagged
@@ -151,7 +174,7 @@ class EDNT_Test < Minitest::Test
151
174
  assert_equal([345, :a], @parser.parse('#edn_turbo/test_tagged { :item 345 :other :a }'))
152
175
  end
153
176
 
154
- def test_symbols
177
+ def test_operators
155
178
 
156
179
  check_file('test/operators.edn',
157
180
  [EDN::Type::Symbol.new('/'),
@@ -168,6 +191,10 @@ class EDNT_Test < Minitest::Test
168
191
  EDN::Type::Symbol.new('='),
169
192
  EDN::Type::Symbol.new('-'),
170
193
  EDN::Type::Symbol.new('+'),
194
+ # [1, EDN::Type::Symbol.new('/'), 2],
195
+ # [3, EDN::Type::Symbol.new('/'), 4],
196
+ [5, EDN::Type::Symbol.new('/'), 6],
197
+ [7, EDN::Type::Symbol.new('/'), 8]
171
198
  ]
172
199
  )
173
200