--- /dev/null
+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.
+
+# File: indent_test.mk
+ myCFLAGS := -Wall -g -fplan9-extensions
+ CFLAGS := $(filter-out $(myCFLAGS),$(CFLAGS)) $(myCFLAGS)
+ LDLIBS:= libparser.o libscanner.o libmdcode.o -licuuc
+
+ all :: itest
+ itest.c itest.h : indent_test.mdc parsergen libparser.o libscanner.o libmdcode.o
+ ./parsergen -o itest --LALR --tag indent indent_test.mdc
+ indent_test.mk: indent_test.mdc md2c
+ ./md2c indent_test.mdc
+ itest: itest.c
+
+ tests:: itest itest.code
+ ./itest itest.code
+
+# indent: header
+
+ ./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;
+ };
+
+# indent: code
+ #include <unistd.h>
+ #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_statement(struct statement *s)
+ {
+ if (!s)
+ return;
+ free_statement(s->next);
+ free_expression(s->expr);
+ free_statement(s->thenpart);
+ free_statement(s->elsepart);
+ 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);
+ while (s) {
+ struct section *t = s->next;
+ code_free(s->code);
+ free(s);
+ s = t;
+ }
+ exit(0);
+ }
+
+
+# indent: grammar
+
+~~~~~~
+
+Program -> Statementlist ${ print_statement($1, 0); }$
+
+OptNL -> NEWLINE
+ |
+
+$*statement
+ Statementlist -> Statements ${ $0 = $<1; }$
+
+ Statements -> Statements Statement ${
+ {
+ struct statement **s;
+ $0 = $<1;
+ s = &$0;
+ while (*s)
+ s = &(*s)->next;
+ *s = $<2;
+ }
+ }$
+ | Statement ${ $0 = $<1; }$
+ | ERROR ${ printf("statement ERROR\n"); $0 = NULL; }$
+
+ Open -> {
+ | NEWLINE {
+ Close -> }
+ | NEWLINE }
+ Block -> Open Statementlist Close ${ $0 = $<2; }$
+ | Open SimpleStatements } ${ $0 = $<2; }$
+ | : Statementlist ${ $0 = $2; $2 = NULL; }$
+
+ SimpleStatements -> SimpleStatements ; SimpleStatement ${
+ {
+ struct statement **s;
+ $0 = $<1;
+ s = &$0;
+ while (*s)
+ s = &(*s)->next;
+ *s = $<3;
+ }
+ }$
+ | SimpleStatement ${ $0 = $<1; }$
+ | SimpleStatements ; ${ $0 = $<1; }$
+
+ SimpleStatement -> Factor = Expression ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = calloc(1, sizeof(struct expression));
+ $0->expr->left = $<1;
+ $0->expr->op = $2.txt;
+ $0->expr->right = $<3;
+ }$
+ Statement -> SimpleStatements NEWLINE ${
+ $0 = $<1;
+ }$
+ | IfStatement ${ $0 = $<1; }$
+ | Statement NEWLINE ${ $0 = $<1; }$
+
+ IfStatement -> if Expression Block OptNL ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = $<2;
+ $0->thenpart = $<3;
+ }$
+ | if Expression : SimpleStatements ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = $<2;
+ $0->thenpart = $<4;
+ }$
+ | if Expression Block OptNL else Block ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = $<2;
+ $0->thenpart = $<3;
+ $0->elsepart = $<6;
+ }$
+ | if Expression Block OptNL else : SimpleStatements ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = $<2;
+ $0->thenpart = $<3;
+ $0->elsepart = $<7;
+ }$
+ | if Expression Block OptNL else IfStatement ${
+ $0 = calloc(1, sizeof(struct statement));
+ $0->expr = $<2;
+ $0->thenpart = $<3;
+ $0->elsepart = $<6;
+ }$
+
+$*expression
+ Expression -> Expression + Term ${
+ $0 = calloc(1, sizeof(struct expression));
+ $0->op = $2.txt;
+ $0->left = $<1;
+ $0->right = $<3;
+ }$
+ | Expression - Term ${
+ $0 = calloc(1, sizeof(struct expression));
+ $0->op = $2.txt;
+ $0->left = $<1;
+ $0->right = $<3;
+ }$
+ | Term ${ $0 = $1; $1 = NULL; }$
+ Term -> Term * Factor ${
+ $0 = calloc(1, sizeof(struct expression));
+ $0->op = $2.txt;
+ $0->left = $<1;
+ $0->right = $<3;
+ }$
+ | Term / Factor ${
+ $0 = calloc(1, sizeof(struct expression));
+ $0->op = $2.txt;
+ $0->left = $<1;
+ $0->right = $<3;
+ }$
+ | Factor ${ $0 = $1; $1 = NULL; }$
+ Factor -> IDENTIFIER ${
+ $0 = calloc(1, sizeof(struct expression));
+ $0->op = $1.txt;
+ }$
+~~~~~~
+
+# File: itest.code
+
+ # test code
+ ~~~~~~
+ hello = yes; mister = no;
+ there = x;
+ all = y;
+ if cond + cond2 :
+ hello = x;
+ hello2 = x;
+
+ sum = val +
+ val;
+
+ if condX:
+ foo = x *
+ x +
+ y
+ / two;
+ else if cond2:
+ there1 =x
+ there1a=x
+ there2=x
+ there3=x;
+ all = y;
+ if true {yes=x;} else : no=x
+ if true: yes = no; no = yes;
+
+ if false {
+ print = OK
+ } else {
+ print = not_OK
+ }
+
+ if a:
+ if b:
+ c= d
+ x = y
+