fork download
  1. #include <ctype.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdbool.h>
  6.  
  7. #define TAG_MAX_LEN 32 // Максимальная длина имени тега
  8.  
  9. // Виды лексем
  10. typedef enum {
  11. T_TAG_BEG, // Открывающий тег
  12. T_TAG_END, // Закрывающий тег
  13. T_TAG_SINGLE, // Одиночный тег
  14. T_DATA, // "Данные" (то есть, не тег)
  15. T_END // Конец потока лексем
  16. } token_type;
  17.  
  18. // Структура для токена
  19. struct {
  20. token_type type; // Вид лексемы
  21.  
  22. // Дополнительные данные лексемы
  23. // Выбирается одно из двух
  24. union {
  25. char tag_name[TAG_MAX_LEN]; // Имя тега
  26. size_t data_length; // Длина данных
  27. } value;
  28. } Token;
  29.  
  30. // Здесь хранится текущий символ
  31. struct {
  32. int val; // Сам символ
  33. size_t pos; // Позиция в строке
  34. size_t line; // Номер строки
  35. } Character;
  36.  
  37. size_t tabulation;
  38. const size_t TAB_STEP = 4;
  39.  
  40. static void init();
  41. static void token_next();
  42.  
  43. //----------------------------------------------------------------------------
  44.  
  45. int main() {
  46. init();
  47. for (token_next(); Token.type != T_END; token_next()) {
  48. switch (Token.type) {
  49. case T_TAG_BEG:
  50. printf("%*c%s\n", tabulation, '{', Token.value.tag_name);
  51. tabulation += TAB_STEP;
  52. break;
  53. case T_TAG_END:
  54. tabulation -= TAB_STEP;
  55. printf("%*c\n", tabulation, '}');
  56. break;
  57. case T_TAG_SINGLE:
  58. printf("%*c%s}\n", tabulation, '{', Token.value.tag_name);
  59. break;
  60. case T_DATA:
  61. printf("%*cdata length: %u]\n", tabulation, '[', Token.value.data_length);
  62. break;
  63. default:
  64. fprintf(stderr, "%s\n", "Runtime error");
  65. exit(EXIT_FAILURE);
  66. }
  67. }
  68. if (tabulation) {
  69. fprintf(stderr, "Warning! %s\n", "Unmatched tag");
  70. }
  71. }
  72.  
  73. //----------------------------------------------------------------------------
  74.  
  75. static void error(const char *message) {
  76. stderr,
  77. "Error[%u:%u] %s\n",
  78. Character.line + 1,
  79. Character.pos,
  80. message);
  81. exit(EXIT_FAILURE);
  82. }
  83.  
  84. //----------------------------------------------------------------------------
  85.  
  86. static void char_next() {
  87. Character.val = getchar();
  88. if (Character.val == '\n') {
  89. Character.pos = 0;
  90. Character.line += 1;
  91. } else {
  92. Character.pos += 1;
  93. }
  94. }
  95.  
  96. //----------------------------------------------------------------------------
  97.  
  98. static void init() {
  99. char_next();
  100. }
  101.  
  102. //----------------------------------------------------------------------------
  103.  
  104. static void scan_tag();
  105. static void scan_data();
  106.  
  107. static void token_next() {
  108. while (isspace(Character.val))
  109. char_next();
  110.  
  111. switch (Character.val) {
  112. case EOF:
  113. Token.type = T_END;
  114. break;
  115. case '<':
  116. scan_tag();
  117. break;
  118. default:
  119. scan_data();
  120. }
  121. }
  122.  
  123. //----------------------------------------------------------------------------
  124.  
  125. static void skip_comment() {
  126. char_next();
  127. if (Character.val != '-') error("Unexpected symbol");
  128. char_next();
  129. if (Character.val != '-') error("Unexpected symbol");
  130.  
  131. _Bool comment_flag = true;
  132. while ((Character.val != EOF) && comment_flag) {
  133. char_next();
  134. if (Character.val == '-') {
  135. char_next();
  136. if (Character.val == '-') {
  137. char_next();
  138. if (Character.val == '>') comment_flag = false;
  139. }
  140. }
  141. }
  142. char_next();
  143. }
  144.  
  145. //----------------------------------------------------------------------------
  146.  
  147. static void tag_get_name() {
  148. int i = 0;
  149. while ((i < (TAG_MAX_LEN - 1)) && isalnum(Character.val)) {
  150. Token.value.tag_name[i] = Character.val;
  151. char_next();
  152. i += 1;
  153. }
  154.  
  155. // Завершающий ноль для строки
  156. Token.value.tag_name[i] = 0;
  157. }
  158.  
  159. //----------------------------------------------------------------------------
  160.  
  161. static void scan_tag() {
  162. char_next();
  163.  
  164. if (Character.val == '!') {
  165. skip_comment();
  166. token_next();
  167. return;
  168. }
  169. else if (Character.val == '/') {
  170. Token.type = T_TAG_END;
  171. char_next();
  172. }
  173. else Token.type = T_TAG_BEG;
  174.  
  175. // Запоминаем имя тега
  176. tag_get_name();
  177. // Детектим пустые теги
  178. if (!strlen(Token.value.tag_name)) error("Empty tag");
  179.  
  180. // Ищем конец тега
  181. while (Character.val != '>') {
  182. if (Character.val == EOF) error("`>' expected");
  183. if (Character.val == '/') {
  184. char_next();
  185. if (Character.val == '>') {
  186. if (Token.type == T_TAG_BEG) {
  187. Token.type = T_TAG_SINGLE;
  188. break;
  189. } else error("Unexpected symbol");
  190. } else error("`/>' expected");
  191. }
  192. char_next();
  193. }
  194.  
  195. char_next();
  196. }
  197.  
  198. //----------------------------------------------------------------------------
  199.  
  200. static void scan_data() {
  201.  
  202. Token.type = T_DATA;
  203. Token.value.data_length = 0;
  204. do {
  205. Token.value.data_length += 1;
  206. char_next();
  207. } while ((Character.val != EOF) && (Character.val != '<'));
  208. }
  209.  
Success #stdin #stdout 0s 2164KB
stdin
<html>
<!-- TEST COMMENT -->
<head><title>Test input</title></head>
<body><h1>Welcome!</h1><hr/>This is a test<br/>End of data</body>
</html>
stdout
{html
   {head
       {title
           [data length: 10]
       }
   }
   {body
       {h1
           [data length: 8]
       }
       {hr}
       [data length: 14]
       {br}
       [data length: 11]
   }
}