added expansion-test
[mala.git] / std / std_parsers.c
blob6dbb08801e8e0957378a11e698955fb544681fb4
1 /*
2 std_parsers.c - MaLa standard module parsers
4 Copyright (C) 2005, Christian Thaeter <chth@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, contact me.
20 #include <stdio.h>
21 #include <string.h>
23 #include "mala.h"
24 #include "std.h"
26 static size_t
27 reserve_string (char ** s, size_t actual, size_t needed);
29 static void
30 mala_macrocheck_list (MalaStringList list, int * max_arg, int argt[10]);
32 static void
33 mala_macrocheck_string (MalaString string, int * max_arg, int argt[10]);
36 int
37 mala_substitute_parser (MalaEngine eng,
38 MalaStringListNode_ref pptr,
39 void * data)
41 if (!mala_stringlist_after_new (&eng->program, *pptr, (MalaString) data))
42 return MALA_EALLOC;
44 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
46 return MALA_SUCCESS;
49 int
50 mala_expand_parser (MalaEngine eng,
51 MalaStringListNode_ref pptr,
52 void * data)
54 MalaStringListNode itr;
55 MalaStringListNode end;
57 end = mala_stringlist_next (*pptr);
59 for (itr = mala_stringlist_tail ((MalaStringList) data);
60 !mala_stringlist_is_end ((MalaStringList) data, itr);
61 mala_stringlist_rev (&itr))
63 if (!mala_stringlist_after_new (&eng->program, *pptr, itr->string))
64 goto ealloc_node;
67 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
69 return MALA_SUCCESS;
71 ealloc_node:
72 /* remove already added things */
73 for (itr = mala_stringlist_next (*pptr);
74 itr != end;
75 mala_stringlist_elem_delete_fwd (&eng->program, &itr));
77 return MALA_EALLOC;
81 int
82 mala_macro_parser (MalaEngine eng,
83 MalaStringListNode_ref pptr,
84 void * data)
86 MalaStringListNode itr;
87 MalaStringListNode last = NULL;
88 MalaStringList list;
89 const char* c;
90 int i;
91 int max_arg = 0;
92 int argt[10] = {0};
93 char *p;
94 MalaString args[10];
95 MalaString str = NULL;
98 // scan how much args are needed
99 mala_macrocheck_list ((MalaStringList) data, &max_arg, argt);
101 // evaluate args, fill arg-array
102 args[0] = (*pptr)->string;
104 for (i = 1; i <= max_arg; ++i)
106 if (!(last = mala_engine_arg_eval (eng, pptr, i, argt[i] == 1 ? -1 : 0, NULL))
107 || eng->state > MALA_EFAULT)
108 return eng->state;
110 args[i] = last->string;
113 // build list with substitutions
114 list = mala_stringlist_new ();
115 if (!list)
116 goto ealloc_list;
118 for (itr = mala_stringlist_head ((MalaStringList) data);
119 !mala_stringlist_is_end ((MalaStringList) data, itr);
120 mala_stringlist_fwd (&itr))
122 size_t size = 64;
123 if (!(p = malloc (size)))
124 goto ealloc_cstr;
126 for (c = mala_string_cstr (itr->string), i = 0; *c; ++c,++i)
128 if (*c == '%')
130 ++c;
131 if (*c == '%')
133 // %% -> %
134 goto normal_char;
136 else if (*c >= '0' && *c <= '9')
138 // %0..%9
139 if (size <= i+mala_string_length (args[*c-'0'])
140 && !(size = reserve_string (&p,
141 size,
142 1 + i + mala_string_length (args[*c-'0']))))
143 goto ealloc_reserve;
144 strcpy(p+i, mala_string_cstr (args[*c-'0']));
145 i += (mala_string_length (args[*c-'0']) - 1);
147 else if (*c == '-')
149 ++c;
150 if (*c >= '0' && *c <= '9')
152 // %0..%9
153 if (size <= i+mala_string_length (args[*c-'0'])
154 && !(size = reserve_string (&p,
155 size,
156 1 + i +
157 mala_string_length (args[*c-'0']))))
158 goto ealloc_reserve;
159 strcpy(p+i, mala_string_cstr (args[*c-'0']));
160 i += (mala_string_length (args[*c-'0']) - 1);
164 else
166 normal_char:
167 // normal char
168 if (size <= (size_t) i && !(size = reserve_string (&p, size, (size_t) i+1)))
169 goto ealloc_reserve;
170 p[i] = *c;
174 p[i] = '\0';
176 char* ptmp;
177 if (!(ptmp = realloc (p, size)))
178 goto ealloc_realloc;
180 str = mala_string_new_cstr_attach (ptmp, &eng->words); /*TODO try without bucket?*/
181 if (!str)
182 goto ealloc_string;
184 if (!mala_stringlist_tail_new (list, str))
185 goto ealloc_node;
187 mala_string_free (str);
191 // insert list
192 for (itr = mala_stringlist_tail (list);
193 !mala_stringlist_is_end (list, itr);
194 mala_stringlist_rev (&itr))
196 if (!mala_stringlist_after_new (&eng->program, last, itr->string))
197 goto ealloc_program;
200 // cleanup
201 mala_stringlist_free (list);
204 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
205 while (max_arg--);
207 return MALA_SUCCESS;
209 ealloc_program:
210 ealloc_node:
211 mala_string_free (str);
212 ealloc_string:
213 ealloc_realloc:
214 ealloc_reserve:
215 free (p);
216 ealloc_cstr:
217 mala_stringlist_free (list);
218 ealloc_list:
219 return MALA_EALLOC;
223 mala_null_parser (MalaEngine eng,
224 MalaStringListNode_ref pptr,
225 void * data)
227 (void) data;
228 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
229 return eng->state;
233 mala_newline_parser (MalaEngine eng,
234 MalaStringListNode_ref pptr,
235 void * data)
237 (void) data;
239 if (eng->state > MALA_EFAULT)
240 return eng->state;
242 putchar('\n');
244 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
245 return MALA_SUCCESS;
249 mala_print_parser (MalaEngine eng,
250 MalaStringListNode_ref pptr,
251 void * data)
253 (void) data;
255 mala_engine_arg_eval (eng, pptr, 1, -1, NULL);
257 if (mala_stringlist_is_tail (&eng->program, *pptr))
258 return mala_engine_exception (eng, pptr, *pptr,
259 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
261 if (eng->state > MALA_EFAULT)
262 return eng->state;
264 // TODO print blocks ?
266 printf("%s",mala_string_cstr (mala_stringlist_next (*pptr)->string));
268 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
269 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
270 return MALA_SUCCESS;
274 mala_literal_parser (MalaEngine eng,
275 MalaStringListNode_ref pptr,
276 void * data)
278 (void) data;
280 if (eng->state > MALA_EFAULT)
281 return eng->state;
283 if (mala_stringlist_is_tail (&eng->program, *pptr))
284 return mala_engine_exception (eng, pptr, *pptr,
285 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
287 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
288 return MALA_LITERAL;
292 mala_not_parser (MalaEngine eng,
293 MalaStringListNode_ref pptr,
294 void * data)
296 (void) data;
298 if (eng->state > MALA_EFAULT)
299 return eng->state;
301 if (mala_stringlist_is_tail (&eng->program, *pptr))
302 return mala_engine_exception (eng, pptr, *pptr,
303 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
305 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
306 eng->negated = !eng->negated;
307 return MALA_SUCCESS;
311 mala_begin_parser (MalaEngine eng,
312 MalaStringListNode_ref pptr,
313 void * data)
315 MalaStringListNode itr;
316 MalaStringListNode end;
317 MalaStringList list;
318 unsigned depth = 1;
319 MalaString name = NULL;
321 (void) data;
323 if (mala_stringlist_is_tail (&eng->program, *pptr))
324 return mala_engine_exception (eng, pptr, *pptr,
325 eng->common_string[MALA_STRING_ERROR_MISSING_END]);
327 // find matching --END
328 for (end = mala_stringlist_next (*pptr);
329 !mala_stringlist_is_end (&eng->program, end);
330 mala_stringlist_fwd (&end))
332 if (mala_string_same(end->string, eng->common_string[MALA_STRING_BEGIN]))
333 ++depth;
334 else if (mala_string_same(end->string, eng->common_string[MALA_STRING_END]))
335 --depth;
336 if (!depth)
337 break;
340 if (depth)
341 return mala_engine_exception (eng, pptr, mala_stringlist_prev (end),
342 eng->common_string[MALA_STRING_ERROR_MISSING_END]);
344 list = mala_stringlist_new ();
345 if (!list)
346 return MALA_EALLOC;
348 // copy the block content to list
349 for (itr = mala_stringlist_next (*pptr); itr != end; mala_stringlist_fwd (&itr))
350 if (!mala_stringlist_tail_new (list, itr->string))
351 goto ealloc_node;
353 // allocate new block name
354 do {
355 mala_string_free (name);
356 name = mala_string_new_print (&eng->words, "--BLOCK_%08X", ++eng->blockcnt);
357 if (!name)
358 goto ealloc_name;
359 } while (mala_actiondesc_top ((MalaActionDesc)mala_string_user_get (name)));
361 if (MALA_SUCCESS != mala_engine_add_action (eng, name, list,
362 mala_block_parser,
363 (MalaDataFactory)mala_stringlist_factory,
364 NULL))
365 goto ealloc_action;
367 // insert new --BLOCK_... in program
368 mala_stringlist_after_new (&eng->program, end, name);
369 mala_string_free (name);
370 // and remove its definition
371 while (*pptr != end)
372 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
373 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
375 return MALA_DELAY;
377 ealloc_action:
378 mala_string_free (name);
379 ealloc_name:
380 ealloc_node:
381 mala_stringlist_free (list);
382 return MALA_EALLOC;
386 mala_block_parser (MalaEngine eng,
387 MalaStringListNode_ref pptr,
388 void * data)
390 (void) data;
392 if (eng->state > MALA_EFAULT)
393 return eng->state;
395 mala_actiondesc_pop_delete (mala_string_user_get ((*pptr)->string));
397 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
398 return MALA_SUCCESS;
402 mala_macrodelete_parser (MalaEngine eng,
403 MalaStringListNode_ref pptr,
404 void * data)
406 (void) data;
408 if (eng->state > MALA_EFAULT)
409 return eng->state;
411 mala_engine_arg_eval (eng, pptr, 1, -1, NULL);
413 if (mala_stringlist_is_tail (&eng->program, *pptr))
414 return mala_engine_exception (eng, pptr, *pptr,
415 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
417 mala_actiondesc_pop_delete (mala_string_user_get (mala_stringlist_next (*pptr)->string));
419 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
420 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
421 return MALA_SUCCESS;
425 mala_exception_parser (MalaEngine eng,
426 MalaStringListNode_ref pptr,
427 void * data)
429 // TODO needs better semantics --EXCEPTION n error -> --ERROR-error 1 .. n --HERE
431 (void) data;
433 mala_engine_arg_eval (eng, pptr, 1, -1, NULL);
435 if (mala_stringlist_is_tail (&eng->program, *pptr))
436 return mala_engine_exception (eng, pptr, *pptr,
437 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
439 if (eng->state > MALA_EFAULT)
440 return eng->state;
442 MalaString ex = mala_string_new_print (&eng->words, "--ERROR-%s",
443 mala_string_cstr (mala_stringlist_next (*pptr)->string));
445 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
446 int state = mala_engine_exception (eng, pptr, *pptr, ex);
447 mala_stringlist_elem_delete (&eng->program, mala_stringlist_next (*pptr));
449 mala_string_free (ex);
451 return state;
455 mala_end_parser (MalaEngine eng,
456 MalaStringListNode_ref pptr,
457 void * data)
459 (void) data;
461 return mala_engine_exception (eng, pptr, *pptr,
462 eng->common_string[MALA_STRING_ERROR_END_WITHOUT_BEGIN]);
465 static void
466 mala_macrocheck_list (MalaStringList list, int * max_arg, int argt[10])
468 MalaStringListNode itr;
469 for (itr = mala_stringlist_head (list);
470 !mala_stringlist_is_end (list, itr);
471 mala_stringlist_fwd (&itr))
473 mala_macrocheck_string (itr->string, max_arg, argt);
474 if (*max_arg == -1)
475 return;
479 static void
480 mala_macrocheck_string (MalaString string, int * max_arg, int argt[10])
482 const char * c;
483 for (c = mala_string_cstr (string); *c; ++c)
485 // "foo%1%-2"
486 if (*c == '%')
488 // %
489 ++c;
490 if (*c == '%')
491 // %%
492 continue;
493 if (*c == '-')
495 // %-
496 ++c;
497 if (*c >= '0' && *c <= '9')
499 // %-0 .. %-9
500 if (argt[*c - '0'] == 1)
501 goto esyntax;
502 if (*c > (char) *max_arg + '0')
503 *max_arg = *c - '0';
504 argt[*c - '0'] = -1;
506 else
507 goto esyntax;
509 else if (*c >= '0' && *c <= '9')
511 // %0 .. %9
512 if (argt[*c - '0'] == -1)
513 goto esyntax;
514 if (*c > (char) *max_arg + '0')
515 *max_arg = *c - '0';
516 argt[*c - '0'] = 1;
518 else
519 goto esyntax;
522 return;
524 esyntax:
525 *max_arg = -1;
526 return;
530 mala_macrodef_parser (MalaEngine eng,
531 MalaStringListNode_ref pptr,
532 void * data)
534 int i;
535 MalaStringListNode itr;
536 MalaStringListNode arg[2];
537 MalaString name;
538 MalaAction act;
540 (void) data;
543 // evaluate both args
544 if (!mala_engine_arg_eval (eng, pptr, 1, -1, NULL))
545 return eng->state;
546 if (!mala_engine_arg_eval (eng, pptr, 2, -1, NULL))
547 return eng->state;
549 // test if 2 arguments left and assign them to arg[], else error
550 for (i = 0, itr = *pptr; i<2; ++i, mala_stringlist_fwd(&itr))
552 if (mala_stringlist_is_tail (&eng->program, itr))
553 return mala_engine_exception (eng, pptr, itr,
554 eng->common_string[MALA_STRING_ERROR_MISSING_ARGUMENT]);
555 arg[i] = mala_stringlist_next (itr);
558 // if name is a block then error
559 name = arg[0]->string;
560 act = mala_actiondesc_top ((MalaActionDesc) mala_string_user_get (name));
561 if (act && act->parser == mala_block_parser)
562 return mala_engine_exception (eng, pptr, arg[0],
563 eng->common_string[MALA_STRING_ERROR_BLOCK_NOT_ALLOWED]);
565 //expansion check and optimize block
566 act = mala_actiondesc_top ((MalaActionDesc) mala_string_user_get (arg[1]->string));
567 if (act && act->factory == (MalaDataFactory) mala_stringlist_factory)
569 int max_arg = 0;
570 int argt[10] = {0};
571 mala_macrocheck_list ((MalaStringList) act->data, &max_arg, argt);
572 // convert block to expansion
573 if (max_arg > 0)
575 //macro
576 MalaActionDesc desc;
577 desc = mala_actiondesc_ensure (name);
578 if (!desc)
579 return MALA_EALLOC;
581 act = mala_actiondesc_pop ((MalaActionDesc) mala_string_user_get (arg[1]->string));
582 act->name = name;
583 act->parser = mala_macro_parser;
584 mala_actiondesc_push_action (desc, act);
586 else if (mala_stringlist_is_single ((MalaStringList) act->data))
588 //substitute
589 MalaString subst;
590 subst = mala_string_copy (mala_stringlist_head((MalaStringList) act->data)->string);
592 if (MALA_SUCCESS != mala_engine_add_action (eng, name, subst,
593 mala_substitute_parser,
594 (MalaDataFactory) mala_string_factory,
595 NULL))
596 return MALA_EALLOC;
598 mala_action_free(act);
600 else if (max_arg == 0)
602 //expand
603 MalaActionDesc desc;
604 desc = mala_actiondesc_ensure (name);
605 if (!desc)
606 return MALA_EALLOC;
608 act = mala_actiondesc_pop ((MalaActionDesc) mala_string_user_get (arg[1]->string));
609 act->name = name;
610 act->parser = mala_expand_parser;
611 mala_actiondesc_push_action (desc, act);
613 else
614 return mala_engine_exception (eng, pptr, arg[1],
615 eng->common_string[MALA_STRING_ERROR_PARAMETER_SYNTAX]);
617 else //if (act && act->factory == (MalaDataFactory)mala_string_factory)
619 // single word
620 int max_arg = 0;
621 int argt[10] = {0};
622 mala_macrocheck_string (arg[1]->string, &max_arg, argt);
623 if (max_arg == 0)
625 // substitute
626 if (MALA_SUCCESS != mala_engine_add_action (eng, name,
627 mala_string_copy (arg[1]->string),
628 mala_substitute_parser,
629 (MalaDataFactory) mala_string_factory,
630 NULL))
631 return MALA_EALLOC;
633 else if (max_arg > 0)
635 // macro
636 MalaStringList list;
638 list = mala_stringlist_new ();
639 if (!list)
640 return MALA_EALLOC;
642 mala_stringlist_tail_new (list, arg[1]->string);
644 if (MALA_SUCCESS != mala_engine_add_action (eng, name, list,
645 mala_macro_parser,
646 (MalaDataFactory) mala_stringlist_factory,
647 NULL))
648 return MALA_EALLOC;
650 else // syntax error
651 return mala_engine_exception (eng, pptr, arg[1],
652 eng->common_string[MALA_STRING_ERROR_PARAMETER_SYNTAX]);
655 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
656 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
657 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
658 return MALA_SUCCESS;
662 mala_foreach_word_parser (MalaEngine eng,
663 MalaStringListNode_ref pptr,
664 void * data)
666 MalaAction act;
667 MalaStringListNode first;
668 MalaStringListNode second;
669 MalaStringListNode last;
670 MalaStringListNode itr;
671 (void) data;
673 first = mala_engine_arg_eval (eng, pptr, 1, -1, NULL);
674 if (!first)
675 return eng->state;
676 act = mala_actiondesc_top ((MalaActionDesc) mala_string_user_get (first->string));
677 // TODO allow blocks as first arg (define macro and delete it at later)
678 if (act && act->parser == mala_block_parser)
679 return mala_engine_exception (eng, pptr, first,
680 eng->common_string[MALA_STRING_ERROR_BLOCK_NOT_ALLOWED]);
682 second = mala_engine_arg_eval (eng, pptr, 2, -1, (MalaDataFactory) mala_stringlist_factory);
683 if (!second)
684 return eng->state;
686 last = mala_stringlist_next (second);
688 act = mala_actiondesc_top ((MalaActionDesc) mala_string_user_get (second->string));
690 // expand second
691 if (eng->state != MALA_LITERAL)
692 for (itr = mala_stringlist_tail ((MalaStringList) act->data);
693 !mala_stringlist_is_end ((MalaStringList) act->data, itr);
694 mala_stringlist_rev (&itr))
696 if (!mala_stringlist_after_new (&eng->program, second, itr->string))
697 goto ealloc_node;
698 if (!mala_stringlist_after_new (&eng->program, second, first->string))
699 goto ealloc_node;
701 else
703 if (!mala_stringlist_after_new (&eng->program, second, second->string))
704 goto ealloc_node;
705 if (!mala_stringlist_after_new (&eng->program, second, first->string))
706 goto ealloc_node;
710 // was a block? delete it
711 if (act && act->parser == mala_block_parser)
712 mala_actiondesc_pop_delete (mala_string_user_get (second->string));
714 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
715 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
716 mala_stringlist_elem_delete_fwd (&eng->program, pptr);
718 return MALA_SUCCESS;
719 ealloc_node:
720 for (itr = mala_stringlist_next (second);
721 itr != last;
722 mala_stringlist_fwd (&itr))
724 mala_stringlist_elem_delete (&eng->program, itr);
727 return MALA_EALLOC; // TODO exception instead (needs pools, no allocation possible further)
734 realloc a string to at least needed size
735 return the amount really reserved or 0 on error
737 static size_t
738 reserve_string (char ** s, size_t actual, size_t needed)
740 size_t n;
741 char * r;
743 for (n = actual>64?actual:64; n <= needed; n += (n>>1)); /*n = n * 1.5*/
745 r = realloc (*s, n);
746 if (!r)
748 /* that was to much, try conservatively */
749 r = realloc (*s, n = needed);
750 if (!r)
751 return 0;
753 *s = r;
754 return n;
759 // Local Variables:
760 // mode: C
761 // c-file-style: "gnu"
762 // End:
763 // arch-tag: 687e1195-8aad-4425-983d-9767a25c3793
764 // end_of_file