1 /* -*- c++ -*- */ 2 /* 3 * Copyright 2010 Intel Corporation 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <assert.h> 26 #include <stdio.h> 27 #include <math.h> 28 #include "s_expression.h" 29 30 s_symbol::s_symbol(const char *str, size_t n) 31 { 32 /* Assume the given string is already nul-terminated and in memory that 33 * will live as long as this node. 34 */ 35 assert(str[n] == '\0'); 36 this->str = str; 37 } 38 39 s_list::s_list() 40 { 41 } 42 43 static void 44 skip_whitespace(const char *&src, char *&symbol_buffer) 45 { 46 size_t n = strspn(src, " \v\t\r\n"); 47 src += n; 48 symbol_buffer += n; 49 /* Also skip Scheme-style comments: semi-colon 'til end of line */ 50 if (src[0] == ';') { 51 n = strcspn(src, "\n"); 52 src += n; 53 symbol_buffer += n; 54 skip_whitespace(src, symbol_buffer); 55 } 56 } 57 58 static s_expression * 59 read_atom(void *ctx, const char *&src, char *&symbol_buffer) 60 { 61 s_expression *expr = NULL; 62 63 skip_whitespace(src, symbol_buffer); 64 65 size_t n = strcspn(src, "( \v\t\r\n);"); 66 if (n == 0) 67 return NULL; // no atom 68 69 // Check for the special symbol '+INF', which means +Infinity. Note: C99 70 // requires strtof to parse '+INF' as +Infinity, but we still support some 71 // non-C99-compliant compilers (e.g. MSVC). 72 if (n == 4 && strncmp(src, "+INF", 4) == 0) { 73 expr = new(ctx) s_float(INFINITY); 74 } else { 75 // Check if the atom is a number. 76 char *float_end = NULL; 77 float f = _mesa_strtof(src, &float_end); 78 if (float_end != src) { 79 char *int_end = NULL; 80 int i = strtol(src, &int_end, 10); 81 // If strtof matched more characters, it must have a decimal part 82 if (float_end > int_end) 83 expr = new(ctx) s_float(f); 84 else 85 expr = new(ctx) s_int(i); 86 } else { 87 // Not a number; return a symbol. 88 symbol_buffer[n] = '\0'; 89 expr = new(ctx) s_symbol(symbol_buffer, n); 90 } 91 } 92 93 src += n; 94 symbol_buffer += n; 95 96 return expr; 97 } 98 99 static s_expression * 100 __read_expression(void *ctx, const char *&src, char *&symbol_buffer) 101 { 102 s_expression *atom = read_atom(ctx, src, symbol_buffer); 103 if (atom != NULL) 104 return atom; 105 106 skip_whitespace(src, symbol_buffer); 107 if (src[0] == '(') { 108 ++src; 109 ++symbol_buffer; 110 111 s_list *list = new(ctx) s_list; 112 s_expression *expr; 113 114 while ((expr = __read_expression(ctx, src, symbol_buffer)) != NULL) { 115 list->subexpressions.push_tail(expr); 116 } 117 skip_whitespace(src, symbol_buffer); 118 if (src[0] != ')') { 119 printf("Unclosed expression (check your parenthesis).\n"); 120 return NULL; 121 } 122 ++src; 123 ++symbol_buffer; 124 return list; 125 } 126 return NULL; 127 } 128 129 s_expression * 130 s_expression::read_expression(void *ctx, const char *&src) 131 { 132 assert(src != NULL); 133 134 /* When we encounter a Symbol, we need to save a nul-terminated copy of 135 * the string. However, ralloc_strndup'ing every individual Symbol is 136 * extremely expensive. We could avoid this by simply overwriting the 137 * next character (guaranteed to be whitespace, parens, or semicolon) with 138 * a nul-byte. But overwriting non-whitespace would mess up parsing. 139 * 140 * So, just copy the whole buffer ahead of time. Walk both, leaving the 141 * original source string unmodified, and altering the copy to contain the 142 * necessary nul-bytes whenever we encounter a symbol. 143 */ 144 char *symbol_buffer = ralloc_strdup(ctx, src); 145 return __read_expression(ctx, src, symbol_buffer); 146 } 147 148 void s_int::print() 149 { 150 printf("%d", this->val); 151 } 152 153 void s_float::print() 154 { 155 printf("%f", this->val); 156 } 157 158 void s_symbol::print() 159 { 160 printf("%s", this->str); 161 } 162 163 void s_list::print() 164 { 165 printf("("); 166 foreach_in_list(s_expression, expr, &this->subexpressions) { 167 expr->print(); 168 if (!expr->next->is_tail_sentinel()) 169 printf(" "); 170 } 171 printf(")"); 172 } 173 174 // -------------------------------------------------- 175 176 bool 177 s_pattern::match(s_expression *expr) 178 { 179 switch (type) 180 { 181 case EXPR: *p_expr = expr; break; 182 case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break; 183 case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break; 184 case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break; 185 case INT: if (expr->is_int()) *p_int = (s_int *) expr; break; 186 case STRING: 187 s_symbol *sym = SX_AS_SYMBOL(expr); 188 if (sym != NULL && strcmp(sym->value(), literal) == 0) 189 return true; 190 return false; 191 }; 192 193 return *p_expr == expr; 194 } 195 196 bool 197 s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial) 198 { 199 s_list *list = SX_AS_LIST(top); 200 if (list == NULL) 201 return false; 202 203 unsigned i = 0; 204 foreach_in_list(s_expression, expr, &list->subexpressions) { 205 if (i >= n) 206 return partial; /* More actual items than the pattern expected */ 207 208 if (expr == NULL || !pattern[i].match(expr)) 209 return false; 210 211 i++; 212 } 213 214 if (i < n) 215 return false; /* Less actual items than the pattern expected */ 216 217 return true; 218 } 219