sexp.c (3811B)
1 /* 2 * ============================================================================ 3 * ███████╗████████╗ █████╗ ███╗ ███╗ █████╗ ██╗██╗ 4 * ██╔════╝╚══██╔══╝██╔══██╗████╗ ████║██╔══██╗██║██║ 5 * ███████╗ ██║ ███████║██╔████╔██║███████║██║██║ 6 * ╚════██║ ██║ ██╔══██║██║╚██╔╝██║██╔══██║██║██║ 7 * ███████║ ██║ ██║ ██║██║ ╚═╝ ██║██║ ██║██║███████╗ 8 * ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ 9 * ============================================================================ 10 * 11 * Copyright (C) 2026 Binkd. 12 * 13 * This file is part of stamail. 14 * 15 * stamail is free software: you can redistribute it and/or modify it under the 16 * terms of the GNU General Public License as published by the Free Software 17 * Foundation, either version 3 of the License, or (at your option) any later 18 * version. 19 * 20 * stamail is distributed in the hope that it will be useful, but WITHOUT ANY 21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 22 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 23 * details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with stamail. If not, see <https://www.gnu.org/licenses/>. 27 * 28 * ============================================================================= 29 */ 30 31 #include <stdlib.h> 32 #include <ctype.h> 33 #include <string.h> 34 #include "sexp.h" 35 36 static const char *p; 37 static const char *end; 38 39 static void skip_ws(void) { 40 while (p < end && isspace((unsigned char)*p)) 41 p++; 42 } 43 44 static struct sexp *parse_expr(void); 45 46 static struct sexp *new_sexp(enum sexp_type t, const char *s, int len) { 47 struct sexp *x = calloc(1, sizeof(*x)); 48 x->type = t; 49 x->start = s; 50 x->len = len; 51 return x; 52 } 53 54 static struct sexp *parse_list(void) { 55 p++; /* skip '(' */ 56 struct sexp *list = new_sexp(SEXP_LIST, NULL, 0); 57 struct sexp **tail = &list->child; 58 59 for (;;) { 60 skip_ws(); 61 if (p >= end) 62 break; 63 if (*p == ')') { 64 p++; 65 break; 66 } 67 struct sexp *e = parse_expr(); 68 if (!e) break; 69 *tail = e; 70 tail = &e->next; 71 } 72 return list; 73 } 74 75 static struct sexp *parse_string(void) { 76 const char *s = ++p; 77 while (p < end) { 78 if (*p == '"' && p[-1] != '\\') 79 break; 80 p++; 81 } 82 int len = p - s; 83 if (p < end) p++; 84 return new_sexp(SEXP_STRING, s, len); 85 } 86 87 static struct sexp *parse_atom(void) { 88 const char *s = p; 89 while (p < end && !isspace((unsigned char)*p) && *p != '(' && *p != ')') 90 p++; 91 int len = p - s; 92 93 int isnum = 1; 94 for (int i = 0; i < len; i++) { 95 if (!isdigit((unsigned char)s[i])) { 96 isnum = 0; 97 break; 98 } 99 } 100 101 return new_sexp(isnum ? SEXP_INT : SEXP_SYMBOL, s, len); 102 } 103 104 static struct sexp *parse_expr(void) { 105 skip_ws(); 106 if (p >= end) return NULL; 107 108 if (*p == '(') 109 return parse_list(); 110 if (*p == '"') 111 return parse_string(); 112 return parse_atom(); 113 } 114 115 struct sexp *sexp_parse(const char *buf, size_t len) { 116 p = buf; 117 end = buf + len; 118 return parse_expr(); 119 } 120 121 void sexp_free(struct sexp *s) { 122 if (!s) return; 123 sexp_free(s->child); 124 sexp_free(s->next); 125 free(s); 126 } 127