initial
This commit is contained in:
commit
29428c7a92
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
main
|
||||||
|
*.o
|
||||||
|
*.gch
|
||||||
|
*.d
|
33
Makefile
Normal file
33
Makefile
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
CXXFLAGS = -Og -g -Wall -Wextra -Werror -Wfatal-errors -Wpedantic -std=c++20
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
TARGET =
|
||||||
|
CXX = $(TARGET)g++
|
||||||
|
|
||||||
|
SRC = main.cpp
|
||||||
|
OBJ = $(patsubst %.cpp,%.o,$(SRC))
|
||||||
|
DEP = $(patsubst %.cpp,%.d,$(SRC))
|
||||||
|
|
||||||
|
all: main
|
||||||
|
|
||||||
|
-include $(DEP)
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
$(CXX) $(CXXFLAGS) -MMD -MF $(basename $<).d -c $< -o $@
|
||||||
|
|
||||||
|
main: $(OBJ)
|
||||||
|
$(CXX) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.d *.gch
|
||||||
|
|
||||||
|
.SUFFIXES:
|
||||||
|
.INTERMEDIATE:
|
||||||
|
.SECONDARY:
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
%: RCS/%,v
|
||||||
|
%: RCS/%
|
||||||
|
%: %,v
|
||||||
|
%: s.%
|
||||||
|
%: SCCS/s.%
|
99
build_radix_tree.py
Normal file
99
build_radix_tree.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
def build_radix_tree(ops: list[str]) -> dict:
|
||||||
|
root = dict()
|
||||||
|
for op in ops:
|
||||||
|
d = root
|
||||||
|
for i in range(len(op)):
|
||||||
|
if op[i] not in d:
|
||||||
|
d[op[i]] = (None,{})
|
||||||
|
if i == (len(op) - 1):
|
||||||
|
d[op[i]] = (op,d[op[i]][1])
|
||||||
|
else:
|
||||||
|
d = d[op[i]][1]
|
||||||
|
return root
|
||||||
|
|
||||||
|
def indent(i):
|
||||||
|
return " " * (2 * i)
|
||||||
|
|
||||||
|
def print_switch(d, level=0):
|
||||||
|
p = print
|
||||||
|
inden0 = indent(level+0)
|
||||||
|
inden1 = indent(level+1)
|
||||||
|
inden2 = indent(level+2)
|
||||||
|
p(inden0 + "switch (s[ix++]) {")
|
||||||
|
for key, (terminal, children) in d.items():
|
||||||
|
if key.upper() != key.lower():
|
||||||
|
p(inden0 + f"case '{key.upper()}': [[fallthrough]];")
|
||||||
|
p(inden0 + f"case '{key.lower()}':")
|
||||||
|
if terminal is not None:
|
||||||
|
p(inden1 + f"if (ix == s.length()) return {{ token::type_t::_{terminal} }};")
|
||||||
|
if children:
|
||||||
|
p(inden1 + "else {")
|
||||||
|
else:
|
||||||
|
if children:
|
||||||
|
p(inden1 + "if (ix < s.length()) {")
|
||||||
|
if children:
|
||||||
|
print_switch(children, level+2)
|
||||||
|
p(inden1 + "}")
|
||||||
|
p(inden1 + "break;")
|
||||||
|
p(inden0 + "}")
|
||||||
|
|
||||||
|
def print_keyword_func(root):
|
||||||
|
p = print
|
||||||
|
inden1 = indent(1)
|
||||||
|
p("#include <optional>")
|
||||||
|
p('#include "token.hpp"')
|
||||||
|
p()
|
||||||
|
p("namespace dsp {")
|
||||||
|
p()
|
||||||
|
p("struct keyword {")
|
||||||
|
p()
|
||||||
|
p("inline static constexpr std::optional<enum token::type_t>")
|
||||||
|
p("find(const std::string_view s)")
|
||||||
|
p("{")
|
||||||
|
p(inden1 + "if (s.length() == 0) { return {}; }")
|
||||||
|
p()
|
||||||
|
p(inden1 + "std::string_view::size_type ix = 0;")
|
||||||
|
p()
|
||||||
|
print_switch(root, level=1)
|
||||||
|
p(inden1 + "return {};")
|
||||||
|
p("}")
|
||||||
|
p()
|
||||||
|
p("};")
|
||||||
|
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",
|
||||||
|
"ad2",
|
||||||
|
"sr",
|
||||||
|
"rr",
|
||||||
|
"sl",
|
||||||
|
"rl",
|
||||||
|
"rl8",
|
||||||
|
"clr",
|
||||||
|
"mov",
|
||||||
|
"mvi",
|
||||||
|
"dma",
|
||||||
|
"dmah",
|
||||||
|
"jmp",
|
||||||
|
"btm",
|
||||||
|
"lps",
|
||||||
|
"end",
|
||||||
|
"endi",
|
||||||
|
"equ",
|
||||||
|
"org",
|
||||||
|
"ends",
|
||||||
|
])
|
||||||
|
print_keyword_func(d)
|
386
keyword.hpp
Normal file
386
keyword.hpp
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
#include <optional>
|
||||||
|
#include "token.hpp"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
struct keyword {
|
||||||
|
|
||||||
|
inline static constexpr std::optional<enum token::type_t>
|
||||||
|
find(const std::string_view s)
|
||||||
|
{
|
||||||
|
if (s.length() == 0) { return {}; }
|
||||||
|
|
||||||
|
std::string_view::size_type ix = 0;
|
||||||
|
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'A': [[fallthrough]];
|
||||||
|
case 'a':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'L': [[fallthrough]];
|
||||||
|
case 'l':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'H': [[fallthrough]];
|
||||||
|
case 'h':
|
||||||
|
if (ix == s.length()) return { token::type_t::_alh };
|
||||||
|
break;
|
||||||
|
case 'L': [[fallthrough]];
|
||||||
|
case 'l':
|
||||||
|
if (ix == s.length()) return { token::type_t::_all };
|
||||||
|
break;
|
||||||
|
case 'U': [[fallthrough]];
|
||||||
|
case 'u':
|
||||||
|
if (ix == s.length()) return { token::type_t::_alu };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N': [[fallthrough]];
|
||||||
|
case 'n':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'D': [[fallthrough]];
|
||||||
|
case 'd':
|
||||||
|
if (ix == s.length()) return { token::type_t::_and };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'D': [[fallthrough]];
|
||||||
|
case 'd':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'D': [[fallthrough]];
|
||||||
|
case 'd':
|
||||||
|
if (ix == s.length()) return { token::type_t::_add };
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if (ix == s.length()) return { token::type_t::_ad2 };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'M': [[fallthrough]];
|
||||||
|
case 'm':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case '0':
|
||||||
|
if (ix == s.length()) return { token::type_t::_m0 };
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
if (ix == s.length()) return { token::type_t::_m1 };
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if (ix == s.length()) return { token::type_t::_m2 };
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
if (ix == s.length()) return { token::type_t::_m3 };
|
||||||
|
break;
|
||||||
|
case 'C': [[fallthrough]];
|
||||||
|
case 'c':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case '0':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mc0 };
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mc1 };
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mc2 };
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mc3 };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'U': [[fallthrough]];
|
||||||
|
case 'u':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'L': [[fallthrough]];
|
||||||
|
case 'l':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mul };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'O': [[fallthrough]];
|
||||||
|
case 'o':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'V': [[fallthrough]];
|
||||||
|
case 'v':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mov };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'V': [[fallthrough]];
|
||||||
|
case 'v':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'I': [[fallthrough]];
|
||||||
|
case 'i':
|
||||||
|
if (ix == s.length()) return { token::type_t::_mvi };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N': [[fallthrough]];
|
||||||
|
case 'n':
|
||||||
|
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::type_t::_nop };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'O': [[fallthrough]];
|
||||||
|
case 'o':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'R': [[fallthrough]];
|
||||||
|
case 'r':
|
||||||
|
if (ix == s.length()) return { token::type_t::_or };
|
||||||
|
else {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'G': [[fallthrough]];
|
||||||
|
case 'g':
|
||||||
|
if (ix == s.length()) return { token::type_t::_org };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'X': [[fallthrough]];
|
||||||
|
case 'x':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'O': [[fallthrough]];
|
||||||
|
case 'o':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'R': [[fallthrough]];
|
||||||
|
case 'r':
|
||||||
|
if (ix == s.length()) return { token::type_t::_xor };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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::type_t::_sub };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'R': [[fallthrough]];
|
||||||
|
case 'r':
|
||||||
|
if (ix == s.length()) return { token::type_t::_sr };
|
||||||
|
break;
|
||||||
|
case 'L': [[fallthrough]];
|
||||||
|
case 'l':
|
||||||
|
if (ix == s.length()) return { token::type_t::_sl };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'R': [[fallthrough]];
|
||||||
|
case 'r':
|
||||||
|
if (ix < s.length()) {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'R': [[fallthrough]];
|
||||||
|
case 'r':
|
||||||
|
if (ix == s.length()) return { token::type_t::_rr };
|
||||||
|
break;
|
||||||
|
case 'L': [[fallthrough]];
|
||||||
|
case 'l':
|
||||||
|
if (ix == s.length()) return { token::type_t::_rl };
|
||||||
|
else {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case '8':
|
||||||
|
if (ix == s.length()) return { token::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::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::type_t::_dma };
|
||||||
|
else {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'H': [[fallthrough]];
|
||||||
|
case 'h':
|
||||||
|
if (ix == s.length()) return { token::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::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::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::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::type_t::_end };
|
||||||
|
else {
|
||||||
|
switch (s[ix++]) {
|
||||||
|
case 'I': [[fallthrough]];
|
||||||
|
case 'i':
|
||||||
|
if (ix == s.length()) return { token::type_t::_endi };
|
||||||
|
break;
|
||||||
|
case 'S': [[fallthrough]];
|
||||||
|
case 's':
|
||||||
|
if (ix == s.length()) return { token::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::type_t::_equ };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
218
lexer.cpp
Normal file
218
lexer.cpp
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#include <string_view>
|
||||||
|
#include <functional>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "token.hpp"
|
||||||
|
#include "num.hpp"
|
||||||
|
#include "lexer.hpp"
|
||||||
|
#include "keyword.hpp"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
constexpr static N parse_digit(const char c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
default: [[fallthrough]];
|
||||||
|
case '0': return 0;
|
||||||
|
case '1': return 1;
|
||||||
|
case '2': return 2;
|
||||||
|
case '3': return 3;
|
||||||
|
case '4': return 4;
|
||||||
|
case '5': return 5;
|
||||||
|
case '6': return 6;
|
||||||
|
case '7': return 7;
|
||||||
|
case '8': return 8;
|
||||||
|
case '9': return 9;
|
||||||
|
case 'a': return 10;
|
||||||
|
case 'b': return 11;
|
||||||
|
case 'c': return 12;
|
||||||
|
case 'd': return 13;
|
||||||
|
case 'e': return 14;
|
||||||
|
case 'f': return 15;
|
||||||
|
case 'A': return 10;
|
||||||
|
case 'B': return 11;
|
||||||
|
case 'C': return 12;
|
||||||
|
case 'D': return 13;
|
||||||
|
case 'E': return 14;
|
||||||
|
case 'F': return 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N, int base>
|
||||||
|
constexpr static N parse_number(const std::string_view s)
|
||||||
|
{
|
||||||
|
N n = 0;
|
||||||
|
for (std::string_view::size_type ix = 0; ix < s.length(); ix++) {
|
||||||
|
n *= base;
|
||||||
|
n += parse_digit<N>(s[ix]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dec_t {
|
||||||
|
constexpr static bool pred(const char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
constexpr static token_t<N> parse(const std::string_view s)
|
||||||
|
{
|
||||||
|
return parse_number<N, 10>(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hex_t {
|
||||||
|
constexpr static bool pred(const char c)
|
||||||
|
{
|
||||||
|
return dec_t::pred(c)
|
||||||
|
|| (c >= 'a' && c <= 'f')
|
||||||
|
|| (c >= 'A' && c <= 'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
constexpr static token_t<N> parse(const std::string_view s)
|
||||||
|
{
|
||||||
|
return parse_number<N, 16>(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool alpha_p(const char c)
|
||||||
|
{
|
||||||
|
return (c >= 'a' && c <= 'z')
|
||||||
|
|| (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool alpha_numeric_p(const char c)
|
||||||
|
{
|
||||||
|
return alpha_p(c) || dec_t::pred(c) || (c == '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lexer_t {
|
||||||
|
const std::string_view source;
|
||||||
|
std::string_view::size_type start_ix;
|
||||||
|
std::string_view::size_type current_ix;
|
||||||
|
token_pos_t pos;
|
||||||
|
|
||||||
|
lexer_t() = delete;
|
||||||
|
|
||||||
|
constexpr lexer_t(const std::string_view source)
|
||||||
|
: source(source), start_ix(0), pos{ .line = 1, .col = 0}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool at_end_p()
|
||||||
|
{
|
||||||
|
return current_ix >= source.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
char peek()
|
||||||
|
{
|
||||||
|
if (at_end_p()) return '\0';
|
||||||
|
return source[current_ix];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match(const char expected)
|
||||||
|
{
|
||||||
|
if (at_end_p()) return false;
|
||||||
|
else if (source[current_ix] != expected) return false;
|
||||||
|
pos.col++;
|
||||||
|
current_ix++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char advance()
|
||||||
|
{
|
||||||
|
pos.col++;
|
||||||
|
return source[current_ix++];
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string_view lexeme()
|
||||||
|
{
|
||||||
|
return source.substr(start_ix, current_ix);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
token _number()
|
||||||
|
{
|
||||||
|
while (T::pred(peek())) advance();
|
||||||
|
|
||||||
|
return {pos, token::number, lexeme(), T::parse(lexeme())};
|
||||||
|
}
|
||||||
|
|
||||||
|
token _identifier()
|
||||||
|
{
|
||||||
|
while (alpha_numeric_p(peek())) advance();
|
||||||
|
std::optional<enum token::type_t> keyword = keyword::find(lexeme());
|
||||||
|
if (keyword) return {pos, *keyword, lexeme()};
|
||||||
|
else return {pos, token::identifier, lexeme()};
|
||||||
|
}
|
||||||
|
|
||||||
|
token scan_token()
|
||||||
|
{
|
||||||
|
using enum token::type_t;
|
||||||
|
|
||||||
|
start_ix = current_ix;
|
||||||
|
|
||||||
|
const char c = advance();
|
||||||
|
switch (c) {
|
||||||
|
case '(': return {pos, left_paren, lexeme()};
|
||||||
|
case ')': return {pos, right_paren, lexeme()};
|
||||||
|
case ',': return {pos, comma, lexeme()};
|
||||||
|
case '.': return {pos, dot, lexeme()};
|
||||||
|
case '+': return {pos, plus, lexeme()};
|
||||||
|
case '-': return {pos, minus, lexeme()};
|
||||||
|
case '*': return {pos, star, lexeme()};
|
||||||
|
case '/': return {pos, slash, lexeme()};
|
||||||
|
case '%': return {pos, percent, lexeme()};
|
||||||
|
case '~': return {pos, tilde, lexeme()};
|
||||||
|
case '&': return {pos, ampersand, lexeme()};
|
||||||
|
case '|': return {pos, bar, lexeme()};
|
||||||
|
case '^': return {pos, carot, lexeme()};
|
||||||
|
case '<':
|
||||||
|
if (match('<')) return {pos, left_shift, lexeme()};
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
if (match('>')) return {pos, right_shift, lexeme()};
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
while (!at_end_p() && peek() != '\n') advance();
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
case '\r':
|
||||||
|
case '\t':
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
pos.line++;
|
||||||
|
pos.col = 0;
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
if (hex_t::pred(peek())) {
|
||||||
|
start_ix += 1;
|
||||||
|
return _number<hex_t>();
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case '0':
|
||||||
|
if (match('x')) {
|
||||||
|
if (hex_t::pred(peek())) {
|
||||||
|
start_ix += 2;
|
||||||
|
return _number<hex_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
if (dec_t::pred(c)) {
|
||||||
|
return _number<dec_t>();
|
||||||
|
} else if (alpha_p(c)) {
|
||||||
|
return _identifier();
|
||||||
|
} else {
|
||||||
|
//error(pos.line, "Unexpected character.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
64
main.cpp
Normal file
64
main.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "token.hpp"
|
||||||
|
|
||||||
|
static bool had_error = false;
|
||||||
|
|
||||||
|
static void report(int line, std::string where, std::string message)
|
||||||
|
{
|
||||||
|
std::cerr << "[line " << line << "] Error" << where << ": " << message;
|
||||||
|
had_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(int line, std::string message)
|
||||||
|
{
|
||||||
|
report(line, "", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run(std::string source)
|
||||||
|
{
|
||||||
|
std::string_view buf {source};
|
||||||
|
(void)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_prompt()
|
||||||
|
{
|
||||||
|
constexpr auto prompt = "> ";
|
||||||
|
std::string line;
|
||||||
|
std::cout << prompt << std::flush;
|
||||||
|
while (std::getline(std::cin, line)) {
|
||||||
|
run(line);
|
||||||
|
std::cout << prompt << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_file(char const * const filename)
|
||||||
|
{
|
||||||
|
std::ifstream is {filename, std::ios::binary | std::ios::ate};
|
||||||
|
if (!is.is_open()) {
|
||||||
|
std::cerr << "failed to open " << filename << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const std::streampos size = is.tellg();
|
||||||
|
std::string buf(size, '\0');
|
||||||
|
is.seekg(0);
|
||||||
|
if (!is.read(&buf[0], size)) {
|
||||||
|
std::cerr << "read failed" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
run(buf);
|
||||||
|
return had_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(const int argc, char const * const argv[])
|
||||||
|
{
|
||||||
|
switch (argc) {
|
||||||
|
case 1: run_prompt(); return had_error;
|
||||||
|
case 2: return run_file(argv[1]);
|
||||||
|
default:
|
||||||
|
std::cerr << "Usage: " << argv[0] << " [filename]" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
5
num.hpp
Normal file
5
num.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using num_t = int64_t;
|
194
token.hpp
Normal file
194
token.hpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <ostream>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include "num.hpp"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
struct object_t {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct token_pos_t {
|
||||||
|
int line;
|
||||||
|
int col;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
struct token_t {
|
||||||
|
enum type_t {
|
||||||
|
left_paren,
|
||||||
|
right_paren,
|
||||||
|
|
||||||
|
comma,
|
||||||
|
dot,
|
||||||
|
|
||||||
|
// operators
|
||||||
|
plus,
|
||||||
|
minus,
|
||||||
|
star,
|
||||||
|
slash,
|
||||||
|
percent,
|
||||||
|
tilde,
|
||||||
|
ampersand,
|
||||||
|
bar,
|
||||||
|
carot,
|
||||||
|
left_shift,
|
||||||
|
right_shift,
|
||||||
|
equal,
|
||||||
|
|
||||||
|
// literals
|
||||||
|
identifier,
|
||||||
|
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,
|
||||||
|
|
||||||
|
eof,
|
||||||
|
};
|
||||||
|
|
||||||
|
using literal_t = std::variant<std::monostate, N>;
|
||||||
|
|
||||||
|
const token_pos_t pos;
|
||||||
|
const type_t type;
|
||||||
|
const std::string_view lexeme;
|
||||||
|
const literal_t literal;
|
||||||
|
|
||||||
|
token_t() = delete;
|
||||||
|
|
||||||
|
constexpr token_t(token_pos_t pos, type_t type, const std::string_view lexeme, N number)
|
||||||
|
: pos(pos), type(type), lexeme(lexeme), literal(number)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
constexpr token_t(token_pos_t pos, type_t type, const std::string_view lexeme)
|
||||||
|
: pos(pos), type(type), lexeme(lexeme), literal()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const enum token_t<N>::type_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case left_paren : return os << "LEFT_PAREN";
|
||||||
|
case right_paren : return os << "RIGHT_PAREN";
|
||||||
|
|
||||||
|
case comma : return os << "COMMA";
|
||||||
|
case dot : return os << "DOT";
|
||||||
|
|
||||||
|
// operators
|
||||||
|
case plus : return os << "PLUS";
|
||||||
|
case minus : return os << "MINUS";
|
||||||
|
case star : return os << "STAR";
|
||||||
|
case slash : return os << "SLASH";
|
||||||
|
case percent : return os << "PERCENT";
|
||||||
|
case tilde : return os << "TILDE";
|
||||||
|
case ampersand : return os << "AMPERSAND";
|
||||||
|
case bar : return os << "BAR";
|
||||||
|
case carot : return os << "CAROT";
|
||||||
|
case left_shift : return os << "LEFT_SHIFT";
|
||||||
|
case right_shift : return os << "RIGHT_SHIFT";
|
||||||
|
case equal : return os << "EQUAL";
|
||||||
|
|
||||||
|
// literals
|
||||||
|
case identifier : return os << "IDENTIFIER";
|
||||||
|
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";
|
||||||
|
|
||||||
|
case eof : return os << "EOF";
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const token_t& token)
|
||||||
|
{
|
||||||
|
os << token.type << ' ' << token.lexeme;
|
||||||
|
|
||||||
|
if (auto* v = std::get_if<N>(&token.literal)) {
|
||||||
|
os << '/' << *v;
|
||||||
|
} else { // std::monostate
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using token = dsp::token_t<num_t>;
|
Loading…
x
Reference in New Issue
Block a user