167 lines
4.1 KiB
C
167 lines
4.1 KiB
C
///
|
|
/// See `docs/syntax.md#indentation-levels` for an explanation of the indent level algorithm.
|
|
///
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#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;
|
|
}
|