prolan-m.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /* An implementation of the PROLAN/M programming language.
  2. This software is public domain; anyone obtaining a copy may deal
  3. in it without restriction, including without limitation the rights
  4. to use, copy, modify, merge, publish, distribute, and/or sell this
  5. software.
  6. The author provides no warranty on this software.
  7. -- Catatonic Porpoise <graue@oceanbase.org>, October 2005
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. /* Read all the characters up to, but not including, the first
  13. occurrence of endch. If EOF is read before endch is found, NULL
  14. is returned. The returned string must be freed by the caller. */
  15. static char *readto(FILE *fp, char endch)
  16. {
  17. char *str = NULL;
  18. int strindex = 0;
  19. int strsize = 0;
  20. int c;
  21. strsize = 10;
  22. str = malloc(strsize + 1);
  23. if (!str)
  24. {
  25. fprintf(stderr, "out of memory\n");
  26. exit(1);
  27. }
  28. while ((c = getc(fp)) != endch)
  29. {
  30. if (c == EOF)
  31. {
  32. if (str)
  33. free(str);
  34. return NULL;
  35. }
  36. if (strindex == strsize)
  37. {
  38. char *newstr;
  39. strsize *= 2;
  40. newstr = realloc(str, strsize + 1);
  41. if (newstr == NULL)
  42. {
  43. fprintf(stderr, "out of memory\n");
  44. exit(1);
  45. }
  46. str = newstr;
  47. }
  48. str[strindex] = c;
  49. strindex++;
  50. }
  51. str[strindex] = '\0';
  52. return str;
  53. }
  54. typedef struct
  55. {
  56. char *before;
  57. char *after;
  58. int beforelen;
  59. int afterlen;
  60. } rule_t;
  61. /* Read a rule_t or return NULL if there isn't one left in the file.
  62. "(,)" doesn't count. The caller must free the returned rule_t. */
  63. static rule_t *readrule(FILE *fp)
  64. {
  65. char *before = NULL;
  66. char *after = NULL;
  67. rule_t *rule = NULL;
  68. (void) readto(fp, '('); /* devour anything before the next rule */
  69. before = readto(fp, ',');
  70. if (before == NULL)
  71. return NULL;
  72. after = readto(fp, ')');
  73. if (after == NULL || strlen(before) == 0)
  74. return NULL;
  75. rule = malloc(sizeof (rule_t));
  76. if (!rule)
  77. {
  78. fprintf(stderr, "out of memory\n");
  79. exit(1);
  80. }
  81. rule->before = before;
  82. rule->after = after;
  83. /* store the lengths so we don't have to call strlen() all
  84. the time */
  85. rule->beforelen = strlen(before);
  86. rule->afterlen = strlen(after);
  87. return rule;
  88. }
  89. /* Applies rule rule to ostr, returning a pointer to the modified
  90. version, possibly at a different memory location. (That is,
  91. ostr may be freed if necessary.) The caller should eventually
  92. free the returned pointer.
  93. NULL is returned if the string is unaffected by the rule. */
  94. static char *applyrule(const rule_t *rule, char *ostr)
  95. {
  96. char *p = NULL;
  97. char *str = NULL;
  98. int len;
  99. /* does the rule affect this string? */
  100. p = strstr(ostr, rule->before);
  101. if (p == NULL)
  102. return NULL; /* nope */
  103. len = strlen(ostr);
  104. if (rule->beforelen > rule->afterlen)
  105. {
  106. /* string must shrink */
  107. char *dst = NULL;
  108. char *src = NULL;
  109. strcpy(p, rule->after);
  110. src = p + rule->beforelen;
  111. dst = p + rule->afterlen;
  112. memmove(dst, src, len - (p - ostr) - rule->beforelen + 1);
  113. str = ostr;
  114. }
  115. else if (rule->beforelen == rule->afterlen)
  116. {
  117. /* string stays the same size */
  118. strncpy(p, rule->after, rule->afterlen);
  119. str = ostr;
  120. }
  121. else
  122. {
  123. char *dst = NULL;
  124. char *src = NULL;
  125. /* string must enlarge; first give it enough space */
  126. str = realloc(ostr, len
  127. + rule->afterlen - rule->beforelen);
  128. if (str == NULL)
  129. {
  130. fprintf(stderr, "out of memory\n");
  131. exit(1);
  132. }
  133. /* if the memory has moved, update p */
  134. if (str != ostr)
  135. {
  136. ostr = NULL; /* just to be extra safe */
  137. p = strstr(str, rule->before);
  138. }
  139. /* move the old stuff after the substitution down */
  140. src = p + rule->beforelen;
  141. dst = p + rule->afterlen;
  142. memmove(dst, src, len - (p - str) - rule->beforelen + 1);
  143. /* substitute */
  144. strncpy(p, rule->after, rule->afterlen);
  145. }
  146. return str;
  147. }
  148. /* Apply rules. ostr may be freed (as in applyrule(), which this function
  149. uses). Returns NULL if no rules apply at all. */
  150. static char *applyrules(rule_t *const *rules, int numrules, char *ostr)
  151. {
  152. char *retval = NULL;
  153. int i;
  154. for (i = 0; i < numrules; i++)
  155. {
  156. retval = applyrule(rules[i], ostr);
  157. if (retval != NULL)
  158. return retval;
  159. }
  160. return NULL;
  161. }
  162. /* Read rules from filename. Set *paprule to a pointer to an array
  163. of pointers to rules. Return the number of rules read. */
  164. static int readrules(rule_t ***paprule, const char *filename)
  165. {
  166. rule_t **rules = NULL;
  167. rule_t *arule = NULL;
  168. int numrules = 0;
  169. int rulespace = 0;
  170. FILE *fp;
  171. rulespace = 10;
  172. rules = malloc(rulespace * sizeof (rule_t *));
  173. if (!rules)
  174. {
  175. fprintf(stderr, "out of memory\n");
  176. return NULL;
  177. }
  178. if (!strcmp(filename, "-"))
  179. fp = stdin;
  180. else
  181. {
  182. fp = fopen(filename, "r");
  183. if (!fp)
  184. {
  185. fprintf(stderr, "cannot open %s\n", filename);
  186. exit(1);
  187. }
  188. }
  189. while ((arule = readrule(fp)) != NULL)
  190. {
  191. if (numrules == rulespace)
  192. {
  193. rule_t **newrules;
  194. rulespace *= 2;
  195. newrules = realloc(rules,
  196. rulespace * sizeof (rule_t *));
  197. if (newrules == NULL)
  198. {
  199. fprintf(stderr, "out of memory\n");
  200. exit(1);
  201. }
  202. rules = newrules;
  203. }
  204. rules[numrules] = arule;
  205. numrules++;
  206. }
  207. fclose(fp);
  208. *paprule = rules;
  209. return numrules;
  210. }
  211. static void usage(void)
  212. {
  213. fprintf(stderr, "usage: prolan-m [source file] [input string]\n");
  214. exit(1);
  215. }
  216. int main(int argc, char *argv[])
  217. {
  218. rule_t **rules;
  219. int numrules;
  220. char *str;
  221. if (argc != 3)
  222. usage();
  223. numrules = readrules(&rules, argv[1]);
  224. str = strdup(argv[2]);
  225. if (str == NULL)
  226. {
  227. fprintf(stderr, "out of memory\n");
  228. exit(1);
  229. }
  230. /* run the program */
  231. for (;;)
  232. {
  233. char *res;
  234. res = applyrules(rules, numrules, str);
  235. if (res == NULL) /* no rules matched */
  236. break;
  237. str = res;
  238. }
  239. /* print the output */
  240. printf("%s\n", str);
  241. return 0;
  242. }