2 * Copyright (c) 2015-2016 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #include <jeffpc/str.h>
28 #include <jeffpc/sexpr.h>
30 #include "sexpr_impl.h"
32 static char *escape_str(const char *in
)
40 for (s
= in
; *s
; s
++) {
56 out
= malloc(outlen
+ 1);
60 for (s
= in
, tmp
= out
; *s
; s
++, tmp
++) {
99 struct val
*sexpr_parse(const char *str
, size_t len
)
101 struct sexpr_parser_state x
;
107 sexpr_reader_lex_init(&x
.scanner
);
108 sexpr_reader_set_extra(&x
, x
.scanner
);
110 ASSERT(sexpr_reader_parse(&x
) == 0);
112 sexpr_reader_lex_destroy(x
.scanner
);
117 static struct str
*dump_cons(struct val
*lv
, bool raw
)
119 static struct str dot
= STR_STATIC_INITIALIZER(" . ");
120 static struct str space
= STR_STATIC_INITIALIZER(" ");
121 struct val
*head
= lv
->cons
.head
;
122 struct val
*tail
= lv
->cons
.tail
;
125 return str_cat(3, sexpr_dump(head
, raw
),
127 sexpr_dump(tail
, raw
));
128 else if (!head
&& !tail
)
130 else if (head
&& !tail
)
131 return sexpr_dump(head
, raw
);
132 else if (tail
->type
== VT_CONS
)
133 return str_cat(3, sexpr_dump(head
, raw
),
135 dump_cons(tail
, raw
));
137 return str_cat(3, sexpr_dump(head
, raw
),
139 sexpr_dump(tail
, raw
));
142 struct str
*sexpr_dump(struct val
*lv
, bool raw
)
144 static struct str dquote
= STR_STATIC_INITIALIZER("\"");
145 static struct str squote
= STR_STATIC_INITIALIZER("'");
146 static struct str poundt
= STR_STATIC_INITIALIZER("#t");
147 static struct str poundf
= STR_STATIC_INITIALIZER("#f");
148 static struct str oparen
= STR_STATIC_INITIALIZER("(");
149 static struct str cparen
= STR_STATIC_INITIALIZER(")");
150 static struct str empty
= STR_STATIC_INITIALIZER("()");
158 return str_getref(lv
->str
);
160 tmpstr
= escape_str(str_cstr(lv
->str
));
161 /* TODO: we leak tmpstr */
163 return str_cat(3, &dquote
, STR_DUP(tmpstr
), &dquote
);
165 return lv
->b
? £t
: £f
;
169 snprintf(tmp
, sizeof(tmp
), "%"PRIu64
, lv
->i
);
174 struct val
*head
= lv
->cons
.head
;
175 struct val
*tail
= lv
->cons
.tail
;
178 if (!raw
&& head
&& (head
->type
== VT_SYM
) &&
179 !strcmp(str_cstr(head
->str
), "quote")) {
180 /* we're dealing with a (quote) */
181 if (sexpr_is_null(tail
))
182 return str_cat(2, &squote
, &empty
);
184 /* we're dealing with a (quote ...) */
185 return str_cat(2, &squote
, dump_cons(tail
, raw
));
188 /* nothing to quote */
189 return str_cat(3, &oparen
, dump_cons(lv
, raw
), &cparen
);
196 void sexpr_dump_file(FILE *out
, struct val
*lv
, bool raw
)
200 tmp
= sexpr_dump(lv
, raw
);
202 fprintf(out
, "%s", str_cstr(tmp
));
208 * Convert a C array of vals into a sexpr list. E.g.,
210 * vals = { A, B, C }, nvals = 3
214 * '(A . (B . (C . ())))
216 * which is the same as:
220 struct val
*sexpr_array_to_list(struct val
**vals
, int nvals
)
222 struct val
*last
= NULL
;
225 for (nvals
--; nvals
>= 0; nvals
--, last
= tmp
)
226 tmp
= VAL_ALLOC_CONS(vals
[nvals
], last
);
232 * Convert a sexpr list into a C array of vals. E.g.,
238 * array = { A, B, C }, nvals = 3
240 * We fill in the passed in array with at most alen elements. The number of
241 * filled in elements is returned to the caller.
243 int sexpr_list_to_array(struct val
*list
, struct val
**array
, int alen
)
248 for (tmp
= list
; tmp
&& (alen
> nvals
); tmp
= tmp
->cons
.tail
, nvals
++) {
249 if (tmp
->type
!= VT_CONS
)
252 array
[nvals
] = val_getref(tmp
->cons
.head
);
255 if ((alen
== nvals
) && tmp
)
262 val_putref(array
[--nvals
]);
267 struct val
*sexpr_car(struct val
*lv
)
274 if (lv
->type
== VT_CONS
)
275 ret
= val_getref(lv
->cons
.head
);
284 struct val
*sexpr_cdr(struct val
*lv
)
291 if (lv
->type
== VT_CONS
)
292 ret
= val_getref(lv
->cons
.tail
);
301 ssize_t
sexpr_length(struct val
*lv
)
307 while (!sexpr_is_null(lv
)) {
308 if (lv
->type
!= VT_CONS
) {
323 struct val
*sexpr_nth(struct val
*lv
, uint64_t n
)
328 if (lv
->type
== VT_CONS
) {
330 * If this is not the one we want, follow the tail.
331 * Otherwise, grab the head.
334 tmp
= val_getref(lv
->cons
.tail
);
336 tmp
= val_getref(lv
->cons
.head
);
350 * Given a list, lookup a certain name.
352 * The input list looks like:
355 * '((a . b) . ((c . d) . ()))
357 * So, to check it, we examite the car of the list, if that's not the right
358 * key, we recurse on cdr of the list.
360 struct val
*sexpr_assoc(struct val
*lv
, const char *name
)
370 if (lv
->type
!= VT_CONS
)
373 head
= lv
->cons
.head
;
374 tail
= lv
->cons
.tail
;
377 * check the head of current cons cell: '(head . tail)
378 * (1) must be non-null
379 * (2) must be a cons cell, i.e., head == '(a . b)
380 * (3) (car head) must be a string or symbol
381 * (4) (car head) must be equal to the value passed in
383 if (head
&& (head
->type
== VT_CONS
) &&
385 ((head
->cons
.head
->type
== VT_STR
) ||
386 (head
->cons
.head
->type
== VT_SYM
)) &&
387 !strcmp(str_cstr(head
->cons
.head
->str
), name
))
388 return val_getref(head
);
390 return sexpr_assoc(tail
, name
);
393 bool sexpr_equal(struct val
*lhs
, struct val
*rhs
)
397 /* if they are the same object, they are equal - even if NULL */
403 /* if one is NULL, they are unequal */
405 /* ... unless we're comparing a NULL with a '() */
406 if ((!lhs
&& ((rhs
->type
!= VT_CONS
) ||
409 (!rhs
&& ((lhs
->type
!= VT_CONS
) ||
421 * At this point, we have two non-NULL values.
424 /* different type -> unequal */
425 if (lhs
->type
!= rhs
->type
) {
430 ret
= true; /* pacify gcc */
434 ret
= (lhs
->i
== rhs
->i
);
438 ret
= str_cmp(lhs
->str
, rhs
->str
) == 0;
441 ret
= (lhs
->b
== rhs
->b
);
444 ret
= sexpr_equal(val_getref(lhs
->cons
.head
),
445 val_getref(rhs
->cons
.head
)) &&
446 sexpr_equal(val_getref(lhs
->cons
.tail
),
447 val_getref(rhs
->cons
.tail
));
457 struct val
*sexpr_alist_lookup_val(struct val
*lv
, const char *name
)
462 return sexpr_cdr(sexpr_assoc(lv
, name
));
465 struct str
*sexpr_alist_lookup_str(struct val
*lv
, const char *name
)
473 v
= sexpr_cdr(sexpr_assoc(lv
, name
));
474 if (!v
|| (v
->type
!= VT_STR
))
477 ret
= str_getref(v
->str
);
484 uint64_t sexpr_alist_lookup_int(struct val
*lv
, const char *name
, bool *found
)
496 v
= sexpr_cdr(sexpr_assoc(lv
, name
));
497 ok
= v
&& (v
->type
== VT_INT
);
512 struct val
*sexpr_alist_lookup_list(struct val
*lv
, const char *name
)
520 v
= sexpr_cdr(sexpr_assoc(lv
, name
));
521 if (!v
|| (v
->type
!= VT_CONS
))