edn_turbo 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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