#include <iostream>
#include <cctype>
#include "parser.h"
using namespace std;
namespace parser {
// Теги лексем
enum tag_t { IDENT, NUMBER, LPAREN, RPAREN, IF, LBRACE, RBRACE, SEMICOLON, EQUAL, END , EPSILON };
//___________0______1_______2_______3_______4___5_______6_______7__________8______9___,10_______
// Контекст парсера
struct context {
std::string text; // текст для анализа
coord cur; // координаты текущей позиции в тексте
coord next; // координаты следующей позиции в тексте
coord start; // координаты начала текущей лексемы
tag_t tag; // тег текущей лексемы
};
// next_char считывает следующий символ из текста и возвращает его код.
// Если достигнут конец текста, next_char возвращает -1.
int next_char(context& ctx) {
ctx.cur = ctx.next;
if (ctx.next.offs == ctx.text.length()) return -1;
char c = ctx.text[ctx.next.offs++];
if (c == '\n') {
ctx.next.line++;
ctx.next.col = 1;
} else {
ctx.next.col++;
}
return c;
}
// next_token распознаёт следующую лексему в тексте.
void next_token(context& ctx) throw(coord) {
int c;
while (isspace(c = next_char(ctx)));
ctx.start = ctx.cur;
switch (c) {
case -1: ctx.tag = END; break;
case '(': ctx.tag = LPAREN; break;
case ')': ctx.tag = RPAREN; break;
case '{': ctx.tag = LBRACE; break;
case '}': ctx.tag = RBRACE; break;
case ';': ctx.tag = SEMICOLON; break;
case '=': ctx.tag = EQUAL; break;
default:
if (isalpha(c)) {
if (c == 'i' && next_char(ctx) == 'f') {
ctx.tag = IF;
// cout << ctx.next.col << endl;
break;
}
else
while (isalnum(c = next_char(ctx)));
ctx.next.col-=1;
ctx.next.offs-=1;
ctx.tag = IDENT;
} else if (isdigit(c)) {
while (isdigit(c = next_char(ctx)));
ctx.next.col-=1;
ctx.next.offs-=1;
ctx.tag = NUMBER;
} else {
throw ctx.cur;
}
}
}
void parse_stmt(context&) throw(coord);
void parse_seq(context&) throw(coord);
void parse(std::string text) throw(coord) {
context ctx { text };
ctx.next = coord{ 0, 1, 1 };
next_token(ctx);
parse_stmt(ctx);
// next_token(ctx);
if (ctx.tag != END) throw ctx.start;
}
void parse_stmt(context& ctx) throw(coord) {
switch (ctx.tag) {
case IF:
next_token(ctx);
if (ctx.tag != LPAREN) throw ctx.start;
next_token(ctx);
if (ctx.tag != IDENT) throw ctx.start;
next_token(ctx);
if (ctx.tag != RPAREN) throw ctx.start;
else {
next_token(ctx);
parse_stmt(ctx);
break;
}
case IDENT:
next_token(ctx);
if (ctx.tag != EQUAL) throw ctx.start;
next_token(ctx);
if (ctx.tag != NUMBER) throw ctx.start;
next_token(ctx);
if (ctx.tag != SEMICOLON) throw ctx.start;
break;
case LBRACE:
next_token(ctx);
parse_seq(ctx);
// if (ctx.tag != RBRACE) throw ctx.start;
next_token(ctx);
break;
default :
throw ctx.start;
}
}
void parse_seq(context& ctx) throw(coord) {
if (ctx.tag != RBRACE) {
parse_stmt(ctx);
if (ctx.tag == RBRACE)
return;
if (ctx.tag != IDENT)
next_token(ctx);
parse_seq(ctx);
}
else return;
}
}