1:- module(dict_schema, [
2 convert/4, 3 register_schema/2, 4 unregister_schema/1 5]).
13:- use_module(library(error)). 14
15:- dynamic(schema/2).
22register_schema(Name, Schema):-
23 must_be(atom, Name),
24 retractall(schema(Name, _)),
25 assertz(schema(Name, Schema)).
32unregister_schema(Name):-
33 retractall(schema(Name)).
42convert(In, Schema, Out, Errors):-
43 convert('#', Schema, In, Out, [], Errors).
44
45convert(Path, string, In, Out, EIn, EOut):- !,
46 convert(Path, _{ type: string }, In, Out, EIn, EOut).
47
48convert(Path, atom, In, Out, EIn, EOut):- !,
49 convert(Path, _{ type: atom }, In, Out, EIn, EOut).
50
51convert(Path, number, In, Out, EIn, EOut):- !,
52 convert(Path, _{ type: number }, In, Out, EIn, EOut).
53
54convert(Path, integer, In, Out, EIn, EOut):- !,
55 convert(Path, _{ type: integer }, In, Out, EIn, EOut).
56
57convert(Path, any, In, Out, EIn, EOut):- !,
58 convert(Path, _{ type: any }, In, Out, EIn, EOut).
59
60convert(Path, var, In, Out, EIn, EOut):- !,
61 convert(Path, _{ type: var }, In, Out, EIn, EOut).
62
63convert(Path, bool, In, Out, EIn, EOut):- !,
64 convert(Path, _{ type: bool }, In, Out, EIn, EOut).
65
66convert(Path, Name, In, Out, EIn, EOut):-
67 atom(Name), !,
68 ( schema(Name, Schema)
69 -> convert(Path, Schema, In, Out, EIn, EOut)
70 ; throw(error(no_schema(Path, Name)))).
71
72convert(Path, Union, In, Out, EIn, EOut):-
73 is_list(Union), !,
74 convert_union(Union, Path, In, Out, [], EIn, EOut).
75
76convert(Path, Schema, In, Out, EIn, EOut):-
77 Type = Schema.type,
78 convert_type(Type, Path, Schema, In, Out, EIn, EOut).
79
80convert_type(dict, Path, Schema, In, Out, EIn, EOut):- !,
81 validate_dict_schema(Path, Schema),
82 convert_dict(Path, Schema, In, Out, EIn, EOut).
83
84convert_type(string, Path, Schema, In, Out, EIn, EOut):- !,
85 validate_string_schema(Path, Schema),
86 convert_string(Path, Schema, In, Out, EIn, EOut).
87
88convert_type(atom, Path, Schema, In, Out, EIn, EOut):- !,
89 validate_atom_schema(Path, Schema),
90 convert_atom(Path, Schema, In, Out, EIn, EOut).
91
92convert_type(enum, Path, Schema, In, Out, EIn, EOut):- !,
93 validate_enum_schema(Path, Schema),
94 convert_enum(Path, Schema, In, Out, EIn, EOut).
95
96convert_type(integer, Path, Schema, In, Out, EIn, EOut):- !,
97 validate_integer_schema(Path, Schema),
98 convert_integer(Path, Schema, In, Out, EIn, EOut).
99
100convert_type(number, Path, Schema, In, Out, EIn, EOut):- !,
101 validate_number_schema(Path, Schema),
102 convert_number(Path, Schema, In, Out, EIn, EOut).
103
104convert_type(list, Path, Schema, In, Out, EIn, EOut):- !,
105 validate_list_schema(Path, Schema),
106 convert_list(Path, Schema, In, Out, EIn, EOut).
107
108convert_type(compound, Path, Schema, In, Out, EIn, EOut):- !,
109 validate_compound_schema(Path, Schema),
110 convert_compound(Path, Schema, In, Out, EIn, EOut).
111
112convert_type(any, Path, Schema, In, In, EIn, EIn):- !,
113 check_is_dict(Path, Schema),
114 allowed_attributes(any, Path, Schema, []).
115
116convert_type(var, Path, Schema, In, In, EIn, EOut):- !,
117 check_is_dict(Path, Schema),
118 allowed_attributes(var, Path, Schema, []),
119 ( var(In)
120 ; EOut = [not_variable(Path, In)|EIn]), !.
121
122convert_type(bool, Path, Schema, In, Out, EIn, EOut):- !,
123 check_is_dict(Path, Schema),
124 allowed_attributes(bool, Path, Schema, []),
125 convert_bool(Path, In, Out, EIn, EOut).
126
127convert_type(Type, Path, _, _, _, _, _):-
128 throw(error(unknown_type(Path, Type))).
129
131
132convert_union([Schema|Schemas], Path, In, Out, EReasons, EIn, EOut):-
133 convert(Path, Schema, In, Out, [], ETmp),
134 ( (
135 ETmp = [],
136 EOut = EIn)
137 ; convert_union(Schemas, Path, In, Out, [ETmp|EReasons], EIn, EOut)), !.
138
139convert_union([], Path, In, In, EReasons, EIn, EOut):-
140 EOut = [union_mismatch(Path, EReasons)|EIn].
141
144
145validate_dict_schema(Path, Schema):-
146 check_is_dict(Path, Schema),
147 allowed_attributes(dict, Path, Schema, [tag, keys, optional, additional]),
148 ( get_dict(keys, Schema, _)
149 -> true
150 ; throw(error(dict_no_keys(Path, Schema)))).
151
152validate_string_schema(Path, Schema):-
153 check_is_dict(Path, Schema),
154 allowed_attributes(string, Path, Schema, [min_length, max_length]).
155
156validate_atom_schema(Path, Schema):-
157 check_is_dict(Path, Schema),
158 allowed_attributes(atom, Path, Schema, [min_length, max_length]).
159
160validate_enum_schema(Path, Schema):-
161 check_is_dict(Path, Schema),
162 allowed_attributes(atom, Path, Schema, [values]),
163 ( get_dict(values, Schema, Values)
164 -> validate_enum_values(Values, Path)
165 ; throw(error(enum_no_values(Path, Schema)))).
166
167validate_integer_schema(Path, Schema):-
168 check_is_dict(Path, Schema),
169 allowed_attributes(integer, Path, Schema, [min, max]).
170
171validate_number_schema(Path, Schema):-
172 check_is_dict(Path, Schema),
173 allowed_attributes(number, Path, Schema, [min, max]).
174
175validate_list_schema(Path, Schema):-
176 check_is_dict(Path, Schema),
177 allowed_attributes(number, Path, Schema, [items, min_length, max_length]),
178 ( get_dict(items, Schema, _)
179 -> true
180 ; throw(error(missing_item_schema(Path, Schema)))).
181
182validate_compound_schema(Path, Schema):-
183 check_is_dict(Path, Schema),
184 allowed_attributes(compound, Path, Schema, [name, arguments]),
185 ( get_dict(name, Schema, _)
186 -> true
187 ; throw(error(missing_compound_name(Path, Schema)))),
188 ( get_dict(arguments, Schema, _)
189 -> true
190 ; throw(error(missing_compound_arguments(Path, Schema)))).
191
192validate_enum_values([Value|Values], Path):-
193 ( atom(Value)
194 -> validate_enum_values(Values, Path)
195 ; throw(error(invalid_enum_value(Path, Value)))).
196
197validate_enum_values([], _).
198
199allowed_attributes(Type, Path, Schema, Allowed):-
200 dict_pairs(Schema, _, Pairs),
201 check_attributes(Pairs, Path, Type, [type|Allowed]).
202
203check_attributes([Key-_|Pairs], Path, Type, Allowed):-
204 ( memberchk(Key, Allowed)
205 -> check_attributes(Pairs, Path, Type, Allowed)
206 ; throw(error(invalid_type_attribute(Path, Key, Type)))).
207
208check_attributes([], _, _, _).
209
210check_is_dict(Path, Schema):-
211 ( is_dict(Schema)
212 -> true
213 ; throw(error(schema_not_dict(Path, Schema)))).
214
216
217convert_bool(Path, In, In, EIn, EOut):-
218 var(In), !,
219 EOut = [not_ground(Path, In)|EIn].
220
221convert_bool(Path, In, In, EIn, EOut):-
222 ( (In = true ; In = false)
223 -> EOut = EIn
224 ; EOut = [not_bool(Path, In)]).
225
227
228convert_list(Path, _, In, In, EIn, EOut):-
229 var(In), !,
230 EOut = [not_ground(Path, In)|EIn].
231
232convert_list(Path, Schema, In, Out, EIn, EOut):-
233 is_list(In), !,
234 ItemSchema = Schema.items,
235 convert_list(In, Path, 0, ItemSchema, Out, EIn, ETmp),
236 validate_list(Path, Schema, Out, ETmp, EOut).
237
238convert_list(Path, _, In, In, EIn, EOut):-
239 EOut = [not_list(Path, In)|EIn].
240
241convert_list([In|Ins], Path, N, ItemSchema, [Out|Outs], EIn, EOut):-
242 convert(Path/[N], ItemSchema, In, Out, EIn, ETmp),
243 N1 is N + 1,
244 convert_list(Ins, Path, N1, ItemSchema, Outs, ETmp, EOut).
245
246convert_list([], _, _, _, [], EIn, EIn).
247
249
250validate_list(Path, Schema, List, EIn, EOut):-
251 validate_list_min_length(Path, Schema, List, EIn, ETmp),
252 validate_list_max_length(Path, Schema, List, ETmp, EOut).
253
254validate_list_min_length(Path, Schema, List, EIn, EOut):-
255 ( get_dict(min_length, Schema, MinLength)
256 -> ( length(List, Length),
257 Length < MinLength
258 -> EOut = [min_length(Path, List, MinLength)|EIn]
259 ; EOut = EIn)
260 ; EOut = EIn).
261
262validate_list_max_length(Path, Schema, List, EIn, EOut):-
263 ( get_dict(max_length, Schema, MaxLength)
264 -> ( length(List, Length),
265 Length > MaxLength
266 -> EOut = [max_length(Path, List, MaxLength)|EIn]
267 ; EOut = EIn)
268 ; EOut = EIn).
269
271
272convert_dict(Path, _, In, In, EIn, EOut):-
273 var(In), !,
274 EOut = [not_ground(Path, In)|EIn].
275
276convert_dict(Path, Schema, In, Out, EIn, EOut):-
277 is_dict(In, Tag), !,
278 convert_dict(Tag, Path, Schema, In, Out, EIn, EOut).
279
280convert_dict(Path, _, In, In, EIn, EOut):-
281 EOut = [not_dict(Path, In)|EIn].
282
283convert_dict(Tag, Path, Schema, In, Out, EIn, EOut):-
284 ( get_dict(optional, Schema, Optional)
285 ; Optional = []), !,
286 ( get_dict(tag, Schema, SchemaTag)
287 -> ( Tag = SchemaTag
288 -> Keys = Schema.keys,
289 dict_pairs(Keys, _, Pairs),
290 convert_keys(Pairs, Optional, Path, In, OutPairs, EIn, ETmp),
291 dict_pairs(Out, Tag, OutPairs),
292 validate_additional(Path, In, Schema, ETmp, EOut)
293 ; Out = In,
294 EOut = [invalid_tag(Path, Tag, SchemaTag)|EIn])
295 ; Keys = Schema.keys,
296 dict_pairs(Keys, _, Pairs),
297 convert_keys(Pairs, Optional, Path, In, OutPairs, EIn, ETmp),
298 dict_pairs(Out, Tag, OutPairs),
299 validate_additional(Path, In, Schema, ETmp, EOut)).
300
301convert_keys([Key-Schema|Pairs], Optional, Path, In, OutPairs, EIn, EOut):-
302 ( get_dict(Key, In, Value)
303 -> convert(Path/Key, Schema, Value, Out, EIn, ETmp),
304 OutPairs = [Key-Out|OutPairsRest],
305 convert_keys(Pairs, Optional, Path, In, OutPairsRest, ETmp, EOut)
306 ; ( memberchk(Key, Optional)
307 -> ETmp = EIn
308 ; ETmp = [no_key(Path, Key)|EIn]),
309 convert_keys(Pairs, Optional, Path, In, OutPairs, ETmp, EOut)).
310
311convert_keys([], _, _, _, [], Errors, Errors).
312
315
316validate_additional(_, _, Schema, EIn, EIn):-
317 get_dict(additional, Schema, true), !.
318
319validate_additional(Path, In, Schema, EIn, EOut):-
320 dict_pairs(In, _, Pairs),
321 get_dict(keys, Schema, Keys),
322 validate_additional_keys(Pairs, Path, Keys, EIn, EOut).
323
324validate_additional_keys([Key-_|Pairs], Path, Keys, EIn, EOut):-
325 ( get_dict(Key, Keys, _)
326 -> validate_additional_keys(Pairs, Path, Keys, EIn, EOut)
327 ; ETmp = [additional_key(Path, Key)|EIn],
328 validate_additional_keys(Pairs, Path, Keys, ETmp, EOut)).
329
330validate_additional_keys([], _, _, EIn, EIn).
331
333
334convert_string(Path, _, In, In, EIn, EOut):-
335 var(In), !,
336 EOut = [not_ground(Path, In)|EIn].
337
338convert_string(Path, Schema, In, In, EIn, EOut):-
339 string(In), !,
340 validate_string(Path, Schema, In, EIn, EOut).
341
342convert_string(Path, Schema, In, Out, EIn, EOut):-
343 atom(In), !,
344 atom_string(In, Out),
345 validate_string(Path, Schema, Out, EIn, EOut).
346
347convert_string(Path, _, In, In, EIn, EOut):-
348 EOut = [not_string(Path, In)|EIn].
349
351
352validate_string(Path, Schema, String, EIn, EOut):-
353 validate_string_min_length(Path, Schema, String, EIn, ETmp),
354 validate_string_max_length(Path, Schema, String, ETmp, EOut).
355
356validate_string_min_length(Path, Schema, String, EIn, EOut):-
357 ( get_dict(min_length, Schema, MinLength)
358 -> ( string_length(String, Length),
359 Length < MinLength
360 -> EOut = [min_length(Path, String, MinLength)|EIn]
361 ; EOut = EIn)
362 ; EOut = EIn).
363
364validate_string_max_length(Path, Schema, String, EIn, EOut):-
365 ( get_dict(max_length, Schema, MaxLength)
366 -> ( string_length(String, Length),
367 Length > MaxLength
368 -> EOut = [max_length(Path, String, MaxLength)|EIn]
369 ; EOut = EIn)
370 ; EOut = EIn).
371
373
374convert_atom(Path, _, In, In, EIn, EOut):-
375 var(In), !,
376 EOut = [not_ground(Path, In)|EIn].
377
378convert_atom(Path, Schema, In, In, EIn, EOut):-
379 atom(In), !,
380 validate_atom(Path, Schema, In, EIn, EOut).
381
382convert_atom(Path, Schema, In, Out, EIn, EOut):-
383 string(In), !,
384 atom_string(Out, In),
385 validate_atom(Path, Schema, Out, EIn, EOut).
386
387convert_atom(Path, _, In, In, EIn, EOut):-
388 EOut = [not_atom(Path, In)|EIn].
389
391
392validate_atom(Path, Schema, Atom, EIn, EOut):-
393 validate_atom_min_length(Path, Schema, Atom, EIn, ETmp),
394 validate_atom_max_length(Path, Schema, Atom, ETmp, EOut).
395
396validate_atom_min_length(Path, Schema, Atom, EIn, EOut):-
397 ( get_dict(min_length, Schema, MinLength)
398 -> ( atom_length(Atom, Length),
399 Length < MinLength
400 -> EOut = [min_length(Path, Atom, MinLength)|EIn]
401 ; EOut = EIn)
402 ; EOut = EIn).
403
404validate_atom_max_length(Path, Schema, Atom, EIn, EOut):-
405 ( get_dict(max_length, Schema, MaxLength)
406 -> ( atom_length(Atom, Length),
407 Length > MaxLength
408 -> EOut = [max_length(Path, Atom, MaxLength)|EIn]
409 ; EOut = EIn)
410 ; EOut = EIn).
411
413
414convert_integer(Path, _, In, In, EIn, EOut):-
415 var(In), !,
416 EOut = [not_ground(Path, In)|EIn].
417
418convert_integer(Path, Schema, In, In, EIn, EOut):-
419 integer(In), !,
420 validate_integer(Path, Schema, In, EIn, EOut).
421
422convert_integer(Path, _, In, In, EIn, EOut):-
423 EOut = [not_integer(Path, In)|EIn].
424
426
427validate_integer(Path, Schema, Int, EIn, EOut):-
428 validate_number_min(Path, Schema, Int, EIn, ETmp),
429 validate_number_max(Path, Schema, Int, ETmp, EOut).
430
431validate_number_min(Path, Schema, Num, EIn, EOut):-
432 ( get_dict(min, Schema, Min)
433 -> ( Num < Min
434 -> EOut = [min(Path, Num, Min)|EIn]
435 ; EOut = EIn)
436 ; EOut = EIn).
437
438validate_number_max(Path, Schema, Num, EIn, EOut):-
439 ( get_dict(max, Schema, Max)
440 -> ( Num > Max
441 -> EOut = [max(Path, Num, Max)|EIn]
442 ; EOut = EIn)
443 ; EOut = EIn).
444
446
447convert_number(Path, _, In, In, EIn, EOut):-
448 var(In), !,
449 EOut = [not_ground(Path, In)|EIn].
450
451convert_number(Path, Schema, In, In, EIn, EOut):-
452 number(In), !,
453 validate_number(Path, Schema, In, EIn, EOut).
454
455convert_number(Path, _, In, In, EIn, EOut):-
456 EOut = [not_number(Path, In)|EIn].
457
459
460validate_number(Path, Schema, Num, EIn, EOut):-
461 validate_number_min(Path, Schema, Num, EIn, ETmp),
462 validate_number_max(Path, Schema, Num, ETmp, EOut).
463
465
466convert_enum(Path, _, In, In, EIn, EOut):-
467 var(In), !,
468 EOut = [not_ground(Path, In)|EIn].
469
470convert_enum(Path, Schema, In, In, EIn, EOut):-
471 atom(In), !,
472 Values = Schema.values,
473 check_enum(Path, Values, In, EIn, EOut).
474
475convert_enum(Path, Schema, In, Out, EIn, EOut):-
476 string(In), !,
477 atom_string(Out, In),
478 Values = Schema.values,
479 check_enum(Path, Values, Out, EIn, EOut).
480
481convert_enum(Path, _, In, In, EIn, EOut):-
482 EOut = [invalid_enum_value(Path, In)|EIn].
483
484check_enum(_, Values, In, EIn, EIn):-
485 memberchk(In, Values), !.
486
487check_enum(Path, _, In, EIn, EOut):-
488 EOut = [invalid_enum_value(Path, In)|EIn].
489
491
492convert_compound(Path, _, In, In, EIn, EOut):-
493 var(In), !,
494 EOut = [not_ground(Path, In)|EIn].
495
496convert_compound(Path, Schema, In, Out, EIn, EOut):-
497 compound(In), !,
498 Name = Schema.name,
499 ArgSchemas = Schema.arguments,
500 In =.. [ActualName|ActualArgs],
501 ( Name = ActualName
502 -> length(ArgSchemas, ArgSchemasLen),
503 length(ActualArgs, ActualLen),
504 ( ArgSchemasLen = ActualLen
505 -> length(ConvertedArgs, ArgSchemasLen),
506 Out =.. [Name|ConvertedArgs],
507 convert_compound_args(ActualArgs, ArgSchemas, Name, 0, Path, ConvertedArgs, EIn, EOut)
508 ; EOut = [compound_args_length(Path, ActualLen, ArgSchemasLen)|EIn])
509 ; EOut = [compound_name(Path, ActualName, Name)|EIn]).
510
511convert_compound(Path, _, In, In, EIn, EOut):-
512 EOut = [invalid_compound(Path, In)|EIn].
513
514convert_compound_args([Actual|ActualArgs], [Schema|ArgSchemas], Name, N, Path, [Converted|ConvertedArgs], EIn, EOut):-
515 ArgPath =.. [Name, N],
516 convert(Path/ArgPath, Schema, Actual, Converted, EIn, ETmp),
517 N1 is N + 1,
518 convert_compound_args(ActualArgs, ArgSchemas, Name, N1, Path, ConvertedArgs, ETmp, EOut).
519
520convert_compound_args([], _, _, _, _, _, EIn, EIn)
Dict validation and conversion
Converts string to atoms and vice versa. Validates your dicts. See the project's
README
file for examples. */