/// /// See `docs/syntax.md#indentation-levels` for an explanation of the indent level algorithm. /// #include #include #include #include #include "../io.h" #include "indent.h" _Bool is_indent(char c) { return c == ' ' || c == '\t'; } _Bool is_newline(char c) { return c == '\r' || c == '\n'; } enum indent_type { INDENT_TABS, INDENT_SPACES, }; struct indent { uint32_t tabs; uint32_t spaces; }; static char good_indent(enum indent_type type) { switch (type) { case INDENT_TABS: return '\t'; case INDENT_SPACES: return ' '; } } static char bad_indent(enum indent_type type) { switch (type) { case INDENT_TABS: return ' '; case INDENT_SPACES: return '\t'; } } static uint32_t indent_levels = 0; static struct indent indents[MAX_INDENTS]; static uint32_t additional_line_length = 0; static _Bool tabs_allowed(void) { return indent_levels == 0 || indents[indent_levels - 1].spaces == 0; } // We only throw errors on bad indentation if the line is not empty. // This function spins to the end of the line to determine whether to throw the error. static void indent_error(enum indent_type type) { char c = peekc(); while (is_indent(c)) { nextc(); c = peekc(); if (is_newline(c)) { return; } } switch (type) { case INDENT_SPACES: fprintf(stderr, "lexical error: previous line used spaces at this indentation level; this line used tabs\n"); exit(1); case INDENT_TABS: fprintf(stderr, "lexical error: previous lines used tabs at this indentation level; this line used spaces\n"); exit(1); } } static void expect_indent(enum indent_type type, uint32_t depth) { char good = good_indent(type); char bad = bad_indent(type); char c = peekc(); for (uint32_t i = 0; i < depth; i++) { if (c == bad) { indent_error(type); return; } if (is_newline(c)) { return; } if (c != good) { fprintf(stderr, "lexical error: indentation does not match any preceding indentation level\n"); exit(1); } nextc(); c = peekc(); } } static uint32_t count_indents(enum indent_type type) { uint32_t counter = 0; char indent = good_indent(type); char c = peekc(); while (c == indent) { counter++; nextc(); c = peekc(); } return counter; } static void new_indent(void) { struct indent indent = { 0, 0 }; indent.tabs = count_indents(INDENT_TABS); indent.spaces = count_indents(INDENT_SPACES); char c = peekc(); if (c == '\t' && (indent.spaces > 0 || !tabs_allowed())) { fprintf(stderr, "lexical error: all tabs on a line must precede all spaces\n"); exit(1); } if (is_newline(c)) { return; } if (indent_levels == MAX_INDENTS) { fprintf(stderr, "lexical error: too many indentation levels! factor your code!\n"); exit(1); } indents[indent_levels] = indent; indent_levels++; } int32_t lex_indentation(void) { uint32_t indent_level = 0; char c = peekc(); while (true) { while (is_newline(c)) { nextc(); c = peekc(); } if (c == 0) { indent_levels = 0; return indent_level; } if (!is_indent(c)) { break; } indent_level = 0; while (is_indent(c) && indent_level < indent_levels) { struct indent indent = indents[indent_level]; expect_indent(INDENT_TABS, indent.tabs); expect_indent(INDENT_SPACES, indent.spaces); indent_level++; c = peekc(); } if (is_indent(c)) { new_indent(); c = peekc(); if (!is_newline(c)) { indent_levels++; return indent_levels; } } c = peekc(); } indent_levels = indent_level; return indent_levels; }