parent
							
								
									e82951fe23
								
							
						
					
					
						commit
						beaf3e90af
					
				
					 6 changed files with 217 additions and 0 deletions
				
			
		|  | @ -257,6 +257,22 @@ stdenv.mkDerivation { | |||
|   </varlistentry> | ||||
| 
 | ||||
| 
 | ||||
|   <varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term> | ||||
| 
 | ||||
|     <listitem><para>Convert a JSON string to a Nix | ||||
|     value. For example, | ||||
| 
 | ||||
| <programlisting> | ||||
| builtins.fromJSON ''{"x": [1, 2, 3], "y": null}'' | ||||
| </programlisting> | ||||
| 
 | ||||
|     returns the value <literal>{ x = [ 1 2 3 ]; y = null; | ||||
|     }</literal>. Floating point numbers are not | ||||
|     supported.</para></listitem> | ||||
| 
 | ||||
|   </varlistentry> | ||||
| 
 | ||||
| 
 | ||||
|   <varlistentry><term><function>builtins.getAttr</function> | ||||
|   <replaceable>s</replaceable> <replaceable>set</replaceable></term> | ||||
| 
 | ||||
|  | @ -762,6 +778,7 @@ in foo</programlisting> | |||
| 
 | ||||
|   </varlistentry> | ||||
| 
 | ||||
| 
 | ||||
|   <varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term> | ||||
| 
 | ||||
|     <listitem><para>Convert the string value | ||||
|  |  | |||
							
								
								
									
										144
									
								
								src/libexpr/json-to-value.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/libexpr/json-to-value.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,144 @@ | |||
| #include "config.h" | ||||
| #include "json-to-value.hh" | ||||
| 
 | ||||
| #include <cstring> | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| 
 | ||||
| static void skipWhitespace(const char * & s) | ||||
| { | ||||
|     while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #if HAVE_BOEHMGC | ||||
| typedef std::vector<Value *, gc_allocator<Value *> > ValueVector; | ||||
| #else | ||||
| typedef std::vector<Value *> ValueVector; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| static string parseJSONString(const char * & s) | ||||
| { | ||||
|     string res; | ||||
|     if (*s++ != '"') throw JSONParseError("expected JSON string"); | ||||
|     while (*s != '"') { | ||||
|         if (!*s) throw JSONParseError("got end-of-string in JSON string"); | ||||
|         if (*s == '\\') { | ||||
|             s++; | ||||
|             if (*s == '"') res += '"'; | ||||
|             else if (*s == '\\') res += '\\'; | ||||
|             else if (*s == '/') res += '/'; | ||||
|             else if (*s == '/') res += '/'; | ||||
|             else if (*s == 'b') res += '\b'; | ||||
|             else if (*s == 'f') res += '\f'; | ||||
|             else if (*s == 'n') res += '\n'; | ||||
|             else if (*s == 'r') res += '\r'; | ||||
|             else if (*s == 't') res += '\t'; | ||||
|             else if (*s == 'u') throw JSONParseError("\\u characters in JSON strings are currently not supported"); | ||||
|             else throw JSONParseError("invalid escaped character in JSON string"); | ||||
|             s++; | ||||
|         } else | ||||
|             res += *s++; | ||||
|     } | ||||
|     s++; | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void parseJSON(EvalState & state, const char * & s, Value & v) | ||||
| { | ||||
|     skipWhitespace(s); | ||||
| 
 | ||||
|     if (!*s) throw JSONParseError("expected JSON value"); | ||||
| 
 | ||||
|     if (*s == '[') { | ||||
|         s++; | ||||
|         ValueVector values; | ||||
|         values.reserve(128); | ||||
|         skipWhitespace(s); | ||||
|         while (1) { | ||||
|             if (values.empty() && *s == ']') break; | ||||
|             Value * v2 = state.allocValue(); | ||||
|             parseJSON(state, s, *v2); | ||||
|             values.push_back(v2); | ||||
|             skipWhitespace(s); | ||||
|             if (*s == ']') break; | ||||
|             if (*s != ',') throw JSONParseError("expected `,' or `]' after JSON array element"); | ||||
|             s++; | ||||
|         } | ||||
|         s++; | ||||
|         state.mkList(v, values.size()); | ||||
|         for (size_t n = 0; n < values.size(); ++n) | ||||
|             v.list.elems[n] = values[n]; | ||||
|     } | ||||
| 
 | ||||
|     else if (*s == '{') { | ||||
|         s++; | ||||
|         state.mkAttrs(v, 1); | ||||
|         while (1) { | ||||
|             skipWhitespace(s); | ||||
|             if (v.attrs->empty() && *s == '}') break; | ||||
|             string name = parseJSONString(s); | ||||
|             skipWhitespace(s); | ||||
|             if (*s != ':') throw JSONParseError("expected `:' in JSON object"); | ||||
|             s++; | ||||
|             Value * v2 = state.allocValue(); | ||||
|             parseJSON(state, s, *v2); | ||||
|             v.attrs->push_back(Attr(state.symbols.create(name), v2)); | ||||
|             skipWhitespace(s); | ||||
|             if (*s == '}') break; | ||||
|             if (*s != ',') throw JSONParseError("expected `,' or `}' after JSON member"); | ||||
|             s++; | ||||
|         } | ||||
|         v.attrs->sort(); | ||||
|         s++; | ||||
|     } | ||||
| 
 | ||||
|     else if (*s == '"') { | ||||
|         mkString(v, parseJSONString(s)); | ||||
|     } | ||||
| 
 | ||||
|     else if (isdigit(*s) || *s == '-') { | ||||
|         bool neg = false; | ||||
|         if (*s == '-') { | ||||
|             neg = true; | ||||
|             if (!*++s) throw JSONParseError("unexpected end of JSON number"); | ||||
|         } | ||||
|         NixInt n = 0; | ||||
|         // FIXME: detect overflow
 | ||||
|         while (isdigit(*s)) n = n * 10 + (*s++ - '0'); | ||||
|         if (*s == '.' || *s == 'e') throw JSONParseError("floating point JSON numbers are not supported"); | ||||
|         mkInt(v, neg ? -n : n); | ||||
|     } | ||||
| 
 | ||||
|     else if (strncmp(s, "true", 4) == 0) { | ||||
|         s += 4; | ||||
|         mkBool(v, true); | ||||
|     } | ||||
| 
 | ||||
|     else if (strncmp(s, "false", 5) == 0) { | ||||
|         s += 5; | ||||
|         mkBool(v, false); | ||||
|     } | ||||
| 
 | ||||
|     else if (strncmp(s, "null", 4) == 0) { | ||||
|         s += 4; | ||||
|         mkNull(v); | ||||
|     } | ||||
| 
 | ||||
|     else throw JSONParseError("unrecognised JSON value"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void parseJSON(EvalState & state, const string & s_, Value & v) | ||||
| { | ||||
|     const char * s = s_.c_str(); | ||||
|     parseJSON(state, s, v); | ||||
|     skipWhitespace(s); | ||||
|     if (*s) throw JSONParseError(format("expected end-of-string while parsing JSON value: %1%") % s); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/libexpr/json-to-value.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/libexpr/json-to-value.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "eval.hh" | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| MakeError(JSONParseError, EvalError) | ||||
| 
 | ||||
| void parseJSON(EvalState & state, const string & s, Value & v); | ||||
| 
 | ||||
| } | ||||
|  | @ -6,6 +6,7 @@ | |||
| #include "archive.hh" | ||||
| #include "value-to-xml.hh" | ||||
| #include "value-to-json.hh" | ||||
| #include "json-to-value.hh" | ||||
| #include "names.hh" | ||||
| #include "eval-inline.hh" | ||||
| 
 | ||||
|  | @ -775,6 +776,14 @@ static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Valu | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Parse a JSON string to a value. */ | ||||
| static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||
| { | ||||
|     string s = state.forceStringNoCtx(*args[0], pos); | ||||
|     parseJSON(state, s, v); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Store a string in the Nix store as a source file that can be used
 | ||||
|    as an input by derivations. */ | ||||
| static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||
|  | @ -1396,6 +1405,7 @@ void EvalState::createBaseEnv() | |||
|     // Creating files
 | ||||
|     addPrimOp("__toXML", 1, prim_toXML); | ||||
|     addPrimOp("__toJSON", 1, prim_toJSON); | ||||
|     addPrimOp("__fromJSON", 1, prim_fromJSON); | ||||
|     addPrimOp("__toFile", 2, prim_toFile); | ||||
|     addPrimOp("__filterSource", 2, prim_filterSource); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-fromjson.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-fromjson.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| true | ||||
							
								
								
									
										32
									
								
								tests/lang/eval-okay-fromjson.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tests/lang/eval-okay-fromjson.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| # RFC 7159, section 13. | ||||
| builtins.fromJSON | ||||
|   '' | ||||
|     { | ||||
|       "Image": { | ||||
|           "Width":  800, | ||||
|           "Height": 600, | ||||
|           "Title":  "View from 15th Floor", | ||||
|           "Thumbnail": { | ||||
|               "Url":    "http://www.example.com/image/481989943", | ||||
|               "Height": 125, | ||||
|               "Width":  100 | ||||
|           }, | ||||
|           "Animated" : false, | ||||
|           "IDs": [116, 943, 234, 38793, true  ,false,null, -100] | ||||
|         } | ||||
|     } | ||||
|   '' | ||||
| == | ||||
|   { Image = | ||||
|     { Width = 800; | ||||
|       Height = 600; | ||||
|       Title = "View from 15th Floor"; | ||||
|       Thumbnail = | ||||
|         { Url = http://www.example.com/image/481989943; | ||||
|           Height = 125; | ||||
|           Width = 100; | ||||
|         }; | ||||
|       Animated = false; | ||||
|       IDs = [ 116 943 234 38793 true false null (0-100) ]; | ||||
|     }; | ||||
|   } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue