--- /dev/null
+
+# header
+
+This parser is primarily for testing indent and linebreak handling.
+It reads a very simple code fragment with if/else statements and
+simple assignments with expressions, and then prints out the same
+with complete bracketing and indenting.
+
+ ./parsergen -o itest --LALR indent_test.cgm
+ cc -o itest itest.c lib*.o -licuuc -lgmp
+ ./itest itest.code
+
+ struct expression {
+ struct text op;
+ struct expression *left, *right;
+ };
+ struct statement {
+ struct statement *next;
+ struct expression *expr;
+ struct statement *thenpart;
+ struct statement *elsepart;
+ };
+
+ struct expr {
+ struct expression *e;
+ };
+ struct stmt {
+ struct statement *s;
+ };
+
+# code
+ #include <stdlib.h>
+ #include <fcntl.h>
+ #include <sys/mman.h>
+ #include <stdio.h>
+ #include "mdcode.h"
+ #include "scanner.h"
+ #include "parser.h"
+ #include "itest.h"
+
+ static void free_expression(struct expression *e)
+ {
+ if (!e)
+ return;
+ free_expression(e->left);
+ free_expression(e->right);
+ free(e);
+ }
+ static void free_expr(struct expr *e)
+ {
+ free_expression(e->e);
+ free(e);
+ }
+ static void free_statement(struct statement *s)
+ {
+ if (!s)
+ return;
+ free_statement(s->next);
+ free_statement(s->thenpart);
+ free_statement(s->elsepart);
+ free(s);
+ }
+
+ static void free_stmt(struct stmt *s)
+ {
+ free_statement(s->s);
+ free(s);
+ }
+
+ static void print_expression(struct expression *e)
+ {
+ if (e->left && e->right) printf("(");
+ if (e->left)
+ print_expression(e->left);
+ if (e->op.len)
+ printf("%.*s", e->op.len, e->op.txt);
+ if (e->right)
+ print_expression(e->right);
+ if (e->left && e->right) printf(")");
+ }
+
+ static void print_statement(struct statement *s, int depth)
+ {
+ if (!s)
+ return;
+ if (!s->thenpart) {
+ printf("%*.s", depth, "");
+ print_expression(s->expr);
+ printf(";\n");
+ } else {
+ printf("%*.sif ", depth, "");
+ print_expression(s->expr);
+ printf(":\n");
+ print_statement(s->thenpart, depth+4);
+ if (s->elsepart) {
+ printf("%*.selse:\n", depth, "");
+ print_statement(s->elsepart, depth+4);
+ }
+ }
+ print_statement(s->next, depth);
+ }
+
+ int main(int argc, char *argv[])
+ {
+ int fd = open(argv[1], O_RDONLY);
+ int len = lseek(fd, 0, 2);
+ char *file = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ struct section *s = code_extract(file, file+len, NULL);
+ struct token_config config = {
+ .ignored = (1 << TK_line_comment)
+ | (1 << TK_block_comment),
+ .number_chars = ".,_+-",
+ .word_start = "",
+ .word_cont = "",
+ };
+ parse_itest(s->code, &config, argc > 2 ? stderr : NULL);
+ exit(0);
+ }
+
+
+# grammar
+
+~~~~~~
+
+Program -> Statementlist ${ print_statement($1.s, 0); }$
+
+OptNL -> NEWLINE
+ |
+OptSemi -> NEWLINE
+ | ;
+
+$stmt
+ Statementlist -> Statements ${ $0.s = $1.s; $1.s = NULL; }$
+ Statements -> Statements Statement ${
+ {
+ struct statement **s;
+ $0 = $1;
+ s = &($0.s);
+ while (*s)
+ s = &(*s)->next;
+ if (body[2])
+ *s = $2.s;
+ if (body[0])
+ $1.s = NULL;
+ if (body[2])
+ $2.s = NULL;
+ }
+ }$
+ | Statements OptSemi ${ $0.s = $1.s; $1.s = NULL; }$
+ | ${ $0.s = NULL; }$
+ | ERROR ${ printf("statement ERROR\n"); $0.s = NULL; }$
+
+ Block -> OptNL { Statementlist } OptNL ${ $0 = $3; $3.s = NULL; }$
+ | : Statementlist ${ $0 = $2; $2.s = NULL; }$
+
+ Statement -> Factor = Expression OptSemi ${
+ $0.s = calloc(1, sizeof(struct statement));
+ $0.s->expr = calloc(1, sizeof(struct expression));
+ $0.s->expr->left = $1.e; $1.e = NULL;
+ $0.s->expr->op = $2.txt;
+ $0.s->expr->right = $3.e; $3.e = NULL;
+ }$
+ | if Expression Block OptNL ${
+ $0.s = calloc(1, sizeof(struct statement));
+ $0.s->expr = $2.e; $2.e = NULL;
+ $0.s->thenpart = $3.s;
+ $3.s = NULL;
+ }$
+ | if Expression Statement ${
+ $0.s = calloc(1, sizeof(struct statement));
+ $0.s->expr = $2.e; $2.e = NULL;
+ $0.s->thenpart = $3.s;
+ $3.s = NULL;
+ }$
+ | if Expression Block OptNL else Block ${
+ $0.s = calloc(1, sizeof(struct statement));
+ $0.s->expr = $2.e; $2.e = NULL;
+ $0.s->thenpart = $3.s;
+ $3.s = NULL;
+ $0.s->elsepart = $6.s;
+ $6.s = NULL;
+ }$
+ | if Expression Block OptNL else Statement ${
+ $0.s = calloc(1, sizeof(struct statement));
+ $0.s->expr = $2.e; $2.e = NULL;
+ $0.s->thenpart = $3.s;
+ $3.s = NULL;
+ $0.s->elsepart = $6.s;
+ $6.s = NULL;
+ }$
+
+$expr
+ Expression -> Expression + Term ${
+ $0.e = calloc(1, sizeof(struct expression));
+ $0.e->op = $2.txt;
+ $0.e->left = $1.e; $1.e = NULL;
+ $0.e->right = $3.e; $3.e= NULL;
+ }$
+ | Expression - Term ${
+ $0.e = calloc(1, sizeof(struct expression));
+ $0.e->op = $2.txt;
+ $0.e->left = $1.e; $1.e = NULL;
+ $0.e->right = $3.e; $3.e= NULL;
+ }$
+ | Term ${ $0.e = $1.e; $1.e = NULL; }$
+ Term -> Term * Factor ${
+ $0.e = calloc(1, sizeof(struct expression));
+ $0.e->op = $2.txt;
+ $0.e->left = $1.e; $1.e = NULL;
+ $0.e->right = $3.e; $3.e= NULL;
+ }$
+ | Term / Factor ${
+ $0.e = calloc(1, sizeof(struct expression));
+ $0.e->op = $2.txt;
+ $0.e->left = $1.e; $1.e = NULL;
+ $0.e->right = $3.e; $3.e= NULL;
+ }$
+ | Factor ${ $0.e = $1.e; $1.e = NULL; }$
+ Factor -> IDENTIFIER ${
+ $0.e = calloc(1, sizeof(struct expression));
+ $0.e->op = $1.txt;
+ }$
+