PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/ws24/simple-exptree/src/simple-exp-tree.c

https://gitlab.com/dedstroyer/mai-workshops-2013
C | 330 lines | 122 code | 40 blank | 168 comment | 19 complexity | c53aebb86e2acdd55f6bb5a7e04c9f7b MD5 | raw file
  1. /**
  2. * @mainpage Простейшее построение дерева выражений из инфиксной записи.
  3. *
  4. * @section ОПИСАНИЕ
  5. *
  6. * По входной строке-выражению (из stdin) программа строит
  7. * дерево выражений. Дерево выводится на стандартный поток вывода (stdout).
  8. * На стандартные поток ошибок (stderr) выводится отладочная информация.
  9. * Разбор строки происходит по следующей грамматике:
  10. *
  11. * expr ::= term op term
  12. * term ::= (expr) | alnum
  13. * op ::= <любые символы>
  14. * alnum ::= [a-z]|[0-9]
  15. *
  16. * Важная деталь:
  17. * На самом верхнем уровне выражения не может быть скобок.
  18. * Такая возможность убрана для простоты программы.
  19. *
  20. * @section ПРИМЕР
  21. *
  22. * $> gcc ./programme.c -std=c99 -Wall -pedantic -o programme
  23. * $>
  24. * $> ./programme
  25. * # 1 tree_build[47] start
  26. * # 2 node_expr[62] start
  27. * # 3 node_term[86] start
  28. * # 4 char_parse[122] start
  29. * 1 + 2
  30. * # 4 char_parse[140] end 3
  31. * # 4 node_make[148] start
  32. * # 4 node_make[155] end 3
  33. * # 3 node_term[110] end 5
  34. * # 3 char_parse[122] start
  35. * # 3 char_parse[140] end 3
  36. * # 3 node_term[86] start
  37. * # 4 char_parse[122] start
  38. * # 4 char_parse[140] end 3
  39. * # 4 node_make[148] start
  40. * # 4 node_make[155] end 3
  41. * # 3 node_term[110] end 5
  42. * # 3 node_make[148] start
  43. * # 3 node_make[155] end 3
  44. * # 2 node_expr[76] end
  45. * # 1 tree_build[50] end
  46. * # 1 tree_print[164] start
  47. *
  48. * 2
  49. * +
  50. * 1
  51. *
  52. * # 1 tree_print[171] end
  53. * # 1 tree_del[194] start
  54. * # 1 tree_del[197]: c = '+'
  55. * # 2 tree_del[194] start
  56. * # 2 tree_del[197]: c = '1'
  57. * # 3 tree_del[194] start
  58. * # 3 tree_del[203] end
  59. * # 2 tree_del[203] end
  60. * # 1 tree_del[197]: c = '2'
  61. * # 2 tree_del[194] start
  62. * # 2 tree_del[203] end
  63. * # 1 tree_del[203] end
  64. * $>
  65. * $> ./programme 2> /dev/null
  66. * 1 + 2
  67. * 2
  68. * +
  69. * 1
  70. * $>
  71. * $> ./programme 2> /dev/null
  72. * ( 1 + 2 ) * ( 2 + 3 )
  73. * 3
  74. * +
  75. * 2
  76. * *
  77. * 2
  78. * +
  79. * 1
  80. * $>
  81. * $> ./programme 2> /dev/null
  82. * (a v b) ^ ((x + y) / z)
  83. * z
  84. * /
  85. * y
  86. * +
  87. * x
  88. * ^
  89. * b
  90. * v
  91. * a
  92. * $>
  93. */
  94. #include <stdio.h>
  95. #include <stdlib.h>
  96. typedef struct _node_ node_t;
  97. struct _node_ {
  98. char data;
  99. node_t *left;
  100. node_t *right;
  101. };
  102. typedef node_t* tree_t;
  103. /* ОБЪЯВЛЕНИЯ ФУНКЦИЙ */
  104. /**
  105. * @fn Строит дерево выражения.
  106. */
  107. tree_t tree_build(int depth);
  108. /**
  109. * @fn Печатает дерево. Внутри запускается рекурсивная процедура печати узлов.
  110. */
  111. void tree_print(tree_t tree, int depth);
  112. /**
  113. * @fn Удаляет дерево. Используется рекурсивный спуск.
  114. */
  115. void tree_del(tree_t tree, int depth);
  116. /**
  117. * @fn Печатает поддерево. Рекурсивная процедура печати узлов.
  118. */
  119. void node_print();
  120. /**
  121. * @fn Создает новый узел дерева выражений.
  122. */
  123. tree_t node_make(char c, tree_t left, tree_t right, int depth);
  124. /**
  125. * @fn Создает и возвращает узел выражения.
  126. */
  127. tree_t node_expr(int depth);
  128. /**
  129. * @fn Создает и возвращает узел терма.
  130. */
  131. tree_t node_term(int depth);
  132. /**
  133. * @fn Считывает из стандартного ввода символы, пропуская пробелы.
  134. */
  135. char char_parse(int depth);
  136. int main() {
  137. /* построили дерево */
  138. tree_t exptree = tree_build(1);
  139. /* распечатали дерево */
  140. tree_print(exptree, 1);
  141. /* удалили дерево */
  142. tree_del(exptree, 1);
  143. return 0;
  144. }
  145. /* РЕАЛИЗАЦИЯ ФУНКЦИЙ */
  146. /**
  147. * @fn Строит дерево выражения.
  148. * Грамматика построения:
  149. * expr ::= term op term
  150. * term ::= (expr) | alnum
  151. * op ::= <любые символы>
  152. * alnum ::= [a-z]|[0-9]
  153. */
  154. tree_t tree_build(int depth) {
  155. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  156. tree_t tree = node_expr(depth + 1);
  157. fprintf(stderr, "# %d %*s %s[%d] end\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  158. return tree;
  159. }
  160. /**
  161. * @fn Возвращает узел выражения.
  162. * Выражение состоит из термов и операции и имеет вид:
  163. * <терм> <операция> <терм>.
  164. * Само выражение скобки обрабатывать не умеет.
  165. * Внутри этой функции вызываются функции конструирования термов.
  166. */
  167. tree_t node_expr(int depth) {
  168. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  169. /* левое поддерево */
  170. tree_t lhs = node_term(depth + 1);
  171. /* операция (корень этого поддерева) */
  172. char op = char_parse(depth + 1);
  173. /* правое поддерево */
  174. tree_t rhs = node_term(depth + 1);
  175. /* само текущее поддерево */
  176. tree_t hs = node_make(op, lhs, rhs, depth + 1);
  177. fprintf(stderr, "# %d %*s %s[%d] end\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  178. return hs;
  179. }
  180. /**
  181. * @fn Возвращает узел терма.
  182. * Терм имеет может представлять из себя выражение в скобках,
  183. * либо буквы или цифру.
  184. */
  185. tree_t node_term(int depth) {
  186. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  187. int c;
  188. if(!(c= char_parse(depth + 1))){
  189. fprintf(stderr, "# %d %*s %s[%d] end 1\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  190. return 0;
  191. }
  192. if ('(' == c) {
  193. tree_t tree = node_expr(depth + 1);
  194. if(!(c= char_parse(depth + 1))){
  195. fprintf(stderr, "# %d %*s %s[%d] end 2\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  196. return 0;
  197. }
  198. if (')' != c){
  199. fprintf(stderr, "# %d %*s %s[%d] end 3\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  200. return tree;
  201. }
  202. fprintf(stderr, "# %d %*s %s[%d] end 4\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  203. return tree;
  204. }
  205. else {
  206. tree_t tree = node_make(c, NULL, NULL, depth + 1);
  207. fprintf(stderr, "# %d %*s %s[%d] end 5\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  208. return tree;
  209. }
  210. }
  211. /**
  212. * @fn Считывает из стандартного ввода символы, пропуская пробелы.
  213. * Если встречаются символы \n или EOF, возвращает нуль.
  214. * В данном случае нуль нужно рассматривать как символ конца строки.
  215. *
  216. */
  217. char char_parse(int depth){
  218. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  219. char c;
  220. while(1){
  221. c = getchar();
  222. if(' ' != c)
  223. break;
  224. }
  225. if('\n' == c){
  226. fprintf(stderr, "# %d %*s %s[%d] end 1\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  227. return 0;
  228. }
  229. else if(EOF == c){
  230. fprintf(stderr, "# %d %*s %s[%d] end 2\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  231. return 0;
  232. }
  233. fprintf(stderr, "# %d %*s %s[%d] end 3\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  234. return c;
  235. }
  236. /**
  237. * @fn Создает новый узел дерева выражений.
  238. */
  239. tree_t node_make(char data, tree_t left, tree_t right, int depth) {
  240. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  241. tree_t tree = malloc(sizeof(tree_t));
  242. tree->data = data;
  243. tree->left = left;
  244. tree->right = right;
  245. fprintf(stderr, "# %d %*s %s[%d] end 3\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  246. return tree;
  247. }
  248. /**
  249. * @fn Печатает дерево.
  250. * Внутри запускается рекурсивная процедура печати узлов.
  251. */
  252. void tree_print(tree_t tree, int depth) {
  253. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  254. if (tree){
  255. fprintf(stderr, "\n");
  256. node_print(tree, 0);
  257. fprintf(stderr, "\n");
  258. }
  259. fprintf(stderr, "# %d %*s %s[%d] end\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  260. }
  261. /**
  262. * @fn Печатает поддерево. Рекурсивная процедура печати узлов.
  263. * Сначала печатает левое поддерево.
  264. * Потом печатает текущий узел с соответствующим отступом.
  265. * Потом печатает правое поддерево.
  266. */
  267. void node_print(tree_t tree, int tab) {
  268. if (tree->right){
  269. node_print(tree->right, tab + 1);
  270. }
  271. printf("%*s %c\n", tab * 4, " ", tree->data);
  272. if (tree->left){
  273. node_print(tree->left, tab + 1);
  274. }
  275. }
  276. /**
  277. * @fn Удаляет дерево. Используется рекурсивный спуск.
  278. */
  279. void tree_del(tree_t tree, int depth) {
  280. fprintf(stderr, "# %d %*s %s[%d] start\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  281. tree_t p;
  282. while (tree != NULL) {
  283. fprintf(stderr, "# %d %*s %s[%d]: c = '%c' \n", depth, depth * 4, " ", __FUNCTION__, __LINE__, tree->data);
  284. tree_del(tree->left, depth + 1);
  285. p = tree;
  286. tree = tree->right;
  287. free(p);
  288. }
  289. fprintf(stderr, "# %d %*s %s[%d] end\n", depth, depth * 4, " ", __FUNCTION__, __LINE__);
  290. }