keyword: add more keywords

This also concedes that a lexer that returns std::vector is probably
the simplest API for the parser, but I will still continue to consider
alternatives.
This commit is contained in:
Zack Buhman 2023-08-17 08:12:39 +00:00
parent c59a221c54
commit e48bd182dc
12 changed files with 629 additions and 319 deletions

View File

@ -7,6 +7,7 @@ CXX = $(TARGET)clang++
SRC = main.cpp
SRC += lexer.cpp
SRC += ast.cpp
SRC += parser.cpp
OBJ = $(patsubst %.cpp,%.o,$(SRC))
DEP = $(patsubst %.cpp,%.d,$(SRC))

View File

@ -4,7 +4,7 @@ namespace dsp {
void ast_printer_t::visit(const unary_t * unary) const
{
parenthesize((unary->oper).lexeme, unary->expr);
parenthesize((unary->oper).lexeme, unary->right);
}
void ast_printer_t::visit(const binary_t * binary) const

View File

@ -1,3 +1,5 @@
import sys
def build_radix_tree(ops: list[str]) -> dict:
root = dict()
for op in ops:
@ -62,38 +64,123 @@ def print_keyword_func(root):
p()
p("}")
from pprint import pprint
d = build_radix_tree([
"alh",
"all",
"alu",
"m0", "m1", "m2", "m3",
"mc0", "mc1", "mc2", "mc3",
"mul",
"nop",
"and",
"or",
"xor",
"add",
"sub",
alu_keywords = [
"ad2",
"sr",
"rr",
"sl",
"add",
"and",
"nop",
"or",
"rl",
"rl8",
"rr",
"sl",
"sr",
"sub",
"xor",
]
x_bus_keywords = [
"mov", # Mn, MCn
"x",
"p",
"mul",
]
mn = ["m0", "m1", "m2", "m3"]
mcn = ["mc0", "mc1", "mc2", "mc3"]
y_bus_keywords = [
"mov", # Mn, MCn
"clr",
"mov",
"y",
"a",
"alu",
]
d1_bus_keywords = [
"mov", # MCn
"alh",
"all",
]
reg_keywords = [
"rx",
"pl",
"ra0",
"wa0",
"lop",
"top",
]
cond_keywords = [
"z",
"nz",
"s",
"ns",
"c",
"nc",
"t0",
"nt0",
"zs",
"nzs",
]
move_immediate_keywords = [
"mvi",
]
dma_keywords = [
"dma",
"dmah",
"d0",
"prg",
]
jmp_keywords = [
"jmp",
]
loop_keywords = [
"btm",
"lps",
]
halt_keywords = [
"end",
"endi",
]
directive_keywords = [
"equ",
"org",
"ends",
])
print_keyword_func(d)
]
keywords = sorted(set([
*alu_keywords,
*x_bus_keywords,
*mn,
*mcn,
*y_bus_keywords,
*d1_bus_keywords,
*reg_keywords,
*cond_keywords,
*move_immediate_keywords,
*dma_keywords,
*jmp_keywords,
*loop_keywords,
*halt_keywords,
*directive_keywords,
]))
if sys.argv[1] == 'hpp':
d = build_radix_tree(keywords)
print_keyword_func(d)
elif sys.argv[1] == 'enum_inc':
for k in keywords:
print(f"_{k},")
elif sys.argv[1] == 'case_inc':
for k in keywords:
print(f'case _{k.ljust(4, " ")} : return os << "{k.upper()}";')
else:
assert False, sys.argv

View File

@ -1,3 +1,5 @@
#pragma once
#include <string>
#include "token.hpp"

58
keyword.case_inc Normal file
View File

@ -0,0 +1,58 @@
case _a : return os << "A";
case _ad2 : return os << "AD2";
case _add : return os << "ADD";
case _alh : return os << "ALH";
case _all : return os << "ALL";
case _alu : return os << "ALU";
case _and : return os << "AND";
case _btm : return os << "BTM";
case _c : return os << "C";
case _clr : return os << "CLR";
case _d0 : return os << "D0";
case _dma : return os << "DMA";
case _dmah : return os << "DMAH";
case _end : return os << "END";
case _endi : return os << "ENDI";
case _equ : return os << "EQU";
case _jmp : return os << "JMP";
case _lop : return os << "LOP";
case _lps : return os << "LPS";
case _m0 : return os << "M0";
case _m1 : return os << "M1";
case _m2 : return os << "M2";
case _m3 : return os << "M3";
case _mc0 : return os << "MC0";
case _mc1 : return os << "MC1";
case _mc2 : return os << "MC2";
case _mc3 : return os << "MC3";
case _mov : return os << "MOV";
case _mul : return os << "MUL";
case _mvi : return os << "MVI";
case _nc : return os << "NC";
case _nop : return os << "NOP";
case _ns : return os << "NS";
case _nt0 : return os << "NT0";
case _nz : return os << "NZ";
case _nzs : return os << "NZS";
case _or : return os << "OR";
case _org : return os << "ORG";
case _p : return os << "P";
case _pl : return os << "PL";
case _prg : return os << "PRG";
case _ra0 : return os << "RA0";
case _rl : return os << "RL";
case _rl8 : return os << "RL8";
case _rr : return os << "RR";
case _rx : return os << "RX";
case _s : return os << "S";
case _sl : return os << "SL";
case _sr : return os << "SR";
case _sub : return os << "SUB";
case _t0 : return os << "T0";
case _top : return os << "TOP";
case _wa0 : return os << "WA0";
case _x : return os << "X";
case _xor : return os << "XOR";
case _y : return os << "Y";
case _z : return os << "Z";
case _zs : return os << "ZS";

58
keyword.enum_inc Normal file
View File

@ -0,0 +1,58 @@
_a,
_ad2,
_add,
_alh,
_all,
_alu,
_and,
_btm,
_c,
_clr,
_d0,
_dma,
_dmah,
_end,
_endi,
_equ,
_jmp,
_lop,
_lps,
_m0,
_m1,
_m2,
_m3,
_mc0,
_mc1,
_mc2,
_mc3,
_mov,
_mul,
_mvi,
_nc,
_nop,
_ns,
_nt0,
_nz,
_nzs,
_or,
_org,
_p,
_pl,
_prg,
_ra0,
_rl,
_rl8,
_rr,
_rx,
_s,
_sl,
_sr,
_sub,
_t0,
_top,
_wa0,
_x,
_xor,
_y,
_z,
_zs,

View File

@ -15,8 +15,23 @@ find(const std::string_view s)
switch (s[ix++]) {
case 'A': [[fallthrough]];
case 'a':
if (ix < s.length()) {
if (ix == s.length()) return { token_t::type_t::_a };
else {
switch (s[ix++]) {
case 'D': [[fallthrough]];
case 'd':
if (ix < s.length()) {
switch (s[ix++]) {
case '2':
if (ix == s.length()) return { token_t::type_t::_ad2 };
break;
case 'D': [[fallthrough]];
case 'd':
if (ix == s.length()) return { token_t::type_t::_add };
break;
}
}
break;
case 'L': [[fallthrough]];
case 'l':
if (ix < s.length()) {
@ -47,16 +62,152 @@ find(const std::string_view s)
}
}
break;
case 'D': [[fallthrough]];
case 'd':
}
}
break;
case 'B': [[fallthrough]];
case 'b':
if (ix < s.length()) {
switch (s[ix++]) {
case 'T': [[fallthrough]];
case 't':
if (ix < s.length()) {
switch (s[ix++]) {
case 'M': [[fallthrough]];
case 'm':
if (ix == s.length()) return { token_t::type_t::_btm };
break;
}
}
break;
}
}
break;
case 'C': [[fallthrough]];
case 'c':
if (ix == s.length()) return { token_t::type_t::_c };
else {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix < s.length()) {
switch (s[ix++]) {
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_clr };
break;
}
}
break;
}
}
break;
case 'D': [[fallthrough]];
case 'd':
if (ix < s.length()) {
switch (s[ix++]) {
case '0':
if (ix == s.length()) return { token_t::type_t::_d0 };
break;
case 'M': [[fallthrough]];
case 'm':
if (ix < s.length()) {
switch (s[ix++]) {
case 'A': [[fallthrough]];
case 'a':
if (ix == s.length()) return { token_t::type_t::_dma };
else {
switch (s[ix++]) {
case 'H': [[fallthrough]];
case 'h':
if (ix == s.length()) return { token_t::type_t::_dmah };
break;
}
}
break;
}
}
break;
}
}
break;
case 'E': [[fallthrough]];
case 'e':
if (ix < s.length()) {
switch (s[ix++]) {
case 'N': [[fallthrough]];
case 'n':
if (ix < s.length()) {
switch (s[ix++]) {
case 'D': [[fallthrough]];
case 'd':
if (ix == s.length()) return { token_t::type_t::_add };
if (ix == s.length()) return { token_t::type_t::_end };
else {
switch (s[ix++]) {
case 'I': [[fallthrough]];
case 'i':
if (ix == s.length()) return { token_t::type_t::_endi };
break;
}
}
break;
case '2':
if (ix == s.length()) return { token_t::type_t::_ad2 };
}
}
break;
case 'Q': [[fallthrough]];
case 'q':
if (ix < s.length()) {
switch (s[ix++]) {
case 'U': [[fallthrough]];
case 'u':
if (ix == s.length()) return { token_t::type_t::_equ };
break;
}
}
break;
}
}
break;
case 'J': [[fallthrough]];
case 'j':
if (ix < s.length()) {
switch (s[ix++]) {
case 'M': [[fallthrough]];
case 'm':
if (ix < s.length()) {
switch (s[ix++]) {
case 'P': [[fallthrough]];
case 'p':
if (ix == s.length()) return { token_t::type_t::_jmp };
break;
}
}
break;
}
}
break;
case 'L': [[fallthrough]];
case 'l':
if (ix < s.length()) {
switch (s[ix++]) {
case 'O': [[fallthrough]];
case 'o':
if (ix < s.length()) {
switch (s[ix++]) {
case 'P': [[fallthrough]];
case 'p':
if (ix == s.length()) return { token_t::type_t::_lop };
break;
}
}
break;
case 'P': [[fallthrough]];
case 'p':
if (ix < s.length()) {
switch (s[ix++]) {
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_lps };
break;
}
}
@ -99,17 +250,6 @@ find(const std::string_view s)
}
}
break;
case 'U': [[fallthrough]];
case 'u':
if (ix < s.length()) {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_mul };
break;
}
}
break;
case 'O': [[fallthrough]];
case 'o':
if (ix < s.length()) {
@ -121,6 +261,17 @@ find(const std::string_view s)
}
}
break;
case 'U': [[fallthrough]];
case 'u':
if (ix < s.length()) {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_mul };
break;
}
}
break;
case 'V': [[fallthrough]];
case 'v':
if (ix < s.length()) {
@ -139,6 +290,10 @@ find(const std::string_view s)
case 'n':
if (ix < s.length()) {
switch (s[ix++]) {
case 'C': [[fallthrough]];
case 'c':
if (ix == s.length()) return { token_t::type_t::_nc };
break;
case 'O': [[fallthrough]];
case 'o':
if (ix < s.length()) {
@ -150,6 +305,32 @@ find(const std::string_view s)
}
}
break;
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_ns };
break;
case 'T': [[fallthrough]];
case 't':
if (ix < s.length()) {
switch (s[ix++]) {
case '0':
if (ix == s.length()) return { token_t::type_t::_nt0 };
break;
}
}
break;
case 'Z': [[fallthrough]];
case 'z':
if (ix == s.length()) return { token_t::type_t::_nz };
else {
switch (s[ix++]) {
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_nzs };
break;
}
}
break;
}
}
break;
@ -172,9 +353,134 @@ find(const std::string_view s)
}
}
break;
case 'P': [[fallthrough]];
case 'p':
if (ix == s.length()) return { token_t::type_t::_p };
else {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_pl };
break;
case 'R': [[fallthrough]];
case 'r':
if (ix < s.length()) {
switch (s[ix++]) {
case 'G': [[fallthrough]];
case 'g':
if (ix == s.length()) return { token_t::type_t::_prg };
break;
}
}
break;
}
}
break;
case 'R': [[fallthrough]];
case 'r':
if (ix < s.length()) {
switch (s[ix++]) {
case 'A': [[fallthrough]];
case 'a':
if (ix < s.length()) {
switch (s[ix++]) {
case '0':
if (ix == s.length()) return { token_t::type_t::_ra0 };
break;
}
}
break;
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_rl };
else {
switch (s[ix++]) {
case '8':
if (ix == s.length()) return { token_t::type_t::_rl8 };
break;
}
}
break;
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_rr };
break;
case 'X': [[fallthrough]];
case 'x':
if (ix == s.length()) return { token_t::type_t::_rx };
break;
}
}
break;
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_s };
else {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_sl };
break;
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_sr };
break;
case 'U': [[fallthrough]];
case 'u':
if (ix < s.length()) {
switch (s[ix++]) {
case 'B': [[fallthrough]];
case 'b':
if (ix == s.length()) return { token_t::type_t::_sub };
break;
}
}
break;
}
}
break;
case 'T': [[fallthrough]];
case 't':
if (ix < s.length()) {
switch (s[ix++]) {
case '0':
if (ix == s.length()) return { token_t::type_t::_t0 };
break;
case 'O': [[fallthrough]];
case 'o':
if (ix < s.length()) {
switch (s[ix++]) {
case 'P': [[fallthrough]];
case 'p':
if (ix == s.length()) return { token_t::type_t::_top };
break;
}
}
break;
}
}
break;
case 'W': [[fallthrough]];
case 'w':
if (ix < s.length()) {
switch (s[ix++]) {
case 'A': [[fallthrough]];
case 'a':
if (ix < s.length()) {
switch (s[ix++]) {
case '0':
if (ix == s.length()) return { token_t::type_t::_wa0 };
break;
}
}
break;
}
}
break;
case 'X': [[fallthrough]];
case 'x':
if (ix < s.length()) {
if (ix == s.length()) return { token_t::type_t::_x };
else {
switch (s[ix++]) {
case 'O': [[fallthrough]];
case 'o':
@ -190,189 +496,18 @@ find(const std::string_view s)
}
}
break;
case 'S': [[fallthrough]];
case 's':
if (ix < s.length()) {
switch (s[ix++]) {
case 'U': [[fallthrough]];
case 'u':
if (ix < s.length()) {
switch (s[ix++]) {
case 'B': [[fallthrough]];
case 'b':
if (ix == s.length()) return { token_t::type_t::_sub };
break;
}
}
break;
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_sr };
break;
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_sl };
break;
}
}
case 'Y': [[fallthrough]];
case 'y':
if (ix == s.length()) return { token_t::type_t::_y };
break;
case 'R': [[fallthrough]];
case 'r':
if (ix < s.length()) {
case 'Z': [[fallthrough]];
case 'z':
if (ix == s.length()) return { token_t::type_t::_z };
else {
switch (s[ix++]) {
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_rr };
break;
case 'L': [[fallthrough]];
case 'l':
if (ix == s.length()) return { token_t::type_t::_rl };
else {
switch (s[ix++]) {
case '8':
if (ix == s.length()) return { token_t::type_t::_rl8 };
break;
}
}
break;
}
}
break;
case 'C': [[fallthrough]];
case 'c':
if (ix < s.length()) {
switch (s[ix++]) {
case 'L': [[fallthrough]];
case 'l':
if (ix < s.length()) {
switch (s[ix++]) {
case 'R': [[fallthrough]];
case 'r':
if (ix == s.length()) return { token_t::type_t::_clr };
break;
}
}
break;
}
}
break;
case 'D': [[fallthrough]];
case 'd':
if (ix < s.length()) {
switch (s[ix++]) {
case 'M': [[fallthrough]];
case 'm':
if (ix < s.length()) {
switch (s[ix++]) {
case 'A': [[fallthrough]];
case 'a':
if (ix == s.length()) return { token_t::type_t::_dma };
else {
switch (s[ix++]) {
case 'H': [[fallthrough]];
case 'h':
if (ix == s.length()) return { token_t::type_t::_dmah };
break;
}
}
break;
}
}
break;
}
}
break;
case 'J': [[fallthrough]];
case 'j':
if (ix < s.length()) {
switch (s[ix++]) {
case 'M': [[fallthrough]];
case 'm':
if (ix < s.length()) {
switch (s[ix++]) {
case 'P': [[fallthrough]];
case 'p':
if (ix == s.length()) return { token_t::type_t::_jmp };
break;
}
}
break;
}
}
break;
case 'B': [[fallthrough]];
case 'b':
if (ix < s.length()) {
switch (s[ix++]) {
case 'T': [[fallthrough]];
case 't':
if (ix < s.length()) {
switch (s[ix++]) {
case 'M': [[fallthrough]];
case 'm':
if (ix == s.length()) return { token_t::type_t::_btm };
break;
}
}
break;
}
}
break;
case 'L': [[fallthrough]];
case 'l':
if (ix < s.length()) {
switch (s[ix++]) {
case 'P': [[fallthrough]];
case 'p':
if (ix < s.length()) {
switch (s[ix++]) {
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_lps };
break;
}
}
break;
}
}
break;
case 'E': [[fallthrough]];
case 'e':
if (ix < s.length()) {
switch (s[ix++]) {
case 'N': [[fallthrough]];
case 'n':
if (ix < s.length()) {
switch (s[ix++]) {
case 'D': [[fallthrough]];
case 'd':
if (ix == s.length()) return { token_t::type_t::_end };
else {
switch (s[ix++]) {
case 'I': [[fallthrough]];
case 'i':
if (ix == s.length()) return { token_t::type_t::_endi };
break;
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_ends };
break;
}
}
break;
}
}
break;
case 'Q': [[fallthrough]];
case 'q':
if (ix < s.length()) {
switch (s[ix++]) {
case 'U': [[fallthrough]];
case 'u':
if (ix == s.length()) return { token_t::type_t::_equ };
break;
}
}
case 'S': [[fallthrough]];
case 's':
if (ix == s.length()) return { token_t::type_t::_zs };
break;
}
}

View File

@ -139,7 +139,7 @@ token_t lexer_t::_identifier()
else return {pos, token_t::identifier, lexeme()};
}
std::optional<token_t> lexer_t::scan_token()
std::optional<token_t> lexer_t::lex_token()
{
using enum token_t::type_t;
@ -215,4 +215,17 @@ std::optional<token_t> lexer_t::scan_token()
__builtin_unreachable();
}
std::vector<token_t> lexer_t::lex_tokens()
{
std::vector<token_t> tokens;
while (true) {
std::optional<token_t> token_o = lex_token();
if (!token_o) continue;
tokens.push_back(*token_o);
if (token_o->type == token_t::eof) break;
}
return tokens;
}
}

View File

@ -2,6 +2,7 @@
#include <string_view>
#include <optional>
#include <vector>
#include "token.hpp"
@ -19,7 +20,8 @@ struct lexer_t {
: source(source), start_ix(0), current_ix(0), pos{ .line = 1, .col = 0 }
{ }
std::optional<token_t> scan_token();
std::optional<token_t> lex_token();
std::vector<token_t> lex_tokens();
private:
bool at_end_p();

35
lexer_iterator.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "lexer.hpp"
#include "token.hpp"
namespace dsp {
struct lexer_iterator_t
{
token_t previous_token;
token_t current_token;
lexer_t& lexer;
lexer_iterator_t(lexer_t& lexer)
: lexer(lexer), previous_token(lexer.scan_token()), current_token(previous_token)
{
}
token_t& advance()
{
previous_token = current_token;
current_token = lexer.scan_token();
return previous_token;
}
token_t& peek()
{
return current_token;
}
token_t& previous()
{
return previous_token;
}
};
}

View File

@ -5,6 +5,7 @@
#include "lexer.hpp"
#include "token.hpp"
#include "ast.hpp"
#include "parser.hpp"
namespace dsp {
@ -12,28 +13,18 @@ bool had_error = false;
}
static void print()
{
dsp::literal_t l(56);
std::string_view s("-");
dsp::token_t t({0, 0}, dsp::token_t::minus, s);
dsp::unary_t a(t, &l);
dsp::ast_printer_t p(std::cout);
p.visit(&a);
}
static void run(std::string source)
{
using namespace dsp;
std::string_view buf {source};
lexer_t lexer {buf};
while (std::optional<token_t> token_o = lexer.scan_token()) {
std::cout << *token_o << std::endl;
if (token_o->type == token_t::type_t::eof) {
break;
}
}
std::string_view buf(source);
lexer_t lexer(buf);
std::vector<token_t> tokens = lexer.lex_tokens();
parser_t parser(tokens);
expr_t * expr = parser.expression();
dsp::ast_printer_t p(std::cout);
expr->accept(&p);
std::cout << std::endl << std::flush;
}
static void run_prompt()
@ -67,7 +58,6 @@ static int run_file(char const * const filename)
int main(const int argc, char const * const argv[])
{
print();
switch (argc) {
case 1: run_prompt(); return dsp::had_error;
case 2: return run_file(argv[1]);

View File

@ -45,47 +45,12 @@ struct token_t {
string,
number,
// keywords
_alh,
_all,
_alu,
_m0,
_m1,
_m2,
_m3,
_mc0,
_mc1,
_mc2,
_mc3,
_mul,
_nop,
_and,
_or,
_xor,
_add,
_sub,
_ad2,
_sr,
_rr,
_sl,
_rl,
_rl8,
_clr,
_mov,
_mvi,
_dma,
_dmah,
_jmp,
_btm,
_lps,
_end,
_endi,
_equ,
_org,
_ends,
// ends
eof,
eol,
// keywords
#include "keyword.enum_inc"
};
using literal_t = std::variant<std::monostate, num_type>;
@ -133,44 +98,8 @@ struct token_t {
case string : return os << "STRING";
case number : return os << "NUMBER";
// keywords
case _alh : return os << "ALH";
case _all : return os << "ALL";
case _alu : return os << "ALU";
case _m0 : return os << "M0";
case _m1 : return os << "M1";
case _m2 : return os << "M2";
case _m3 : return os << "M3";
case _mc0 : return os << "MC0";
case _mc1 : return os << "MC1";
case _mc2 : return os << "MC2";
case _mc3 : return os << "MC3";
case _mul : return os << "MUL";
case _nop : return os << "NOP";
case _and : return os << "AND";
case _or : return os << "OR";
case _xor : return os << "XOR";
case _add : return os << "ADD";
case _sub : return os << "SUB";
case _ad2 : return os << "AD2";
case _sr : return os << "SR";
case _rr : return os << "RR";
case _sl : return os << "SL";
case _rl : return os << "RL";
case _rl8 : return os << "RL8";
case _clr : return os << "CLR";
case _mov : return os << "MOV";
case _mvi : return os << "MVI";
case _dma : return os << "DMA";
case _dmah : return os << "DMAH";
case _jmp : return os << "JMP";
case _btm : return os << "BTM";
case _lps : return os << "LPS";
case _end : return os << "END";
case _endi : return os << "ENDI";
case _equ : return os << "EQU";
case _org : return os << "ORG";
case _ends : return os << "ENDS";
// keywords
#include "keyword.case_inc"
case eof : return os << "EOF";
case eol : return os << "EOL";