mirror of
https://github.com/ksherlock/mpw.git
synced 2025-02-07 16:30:45 +00:00
merge in debug template code
This commit is contained in:
parent
00c38721a7
commit
2adbe82704
@ -20,7 +20,7 @@ add_custom_command(
|
||||
MAIN_DEPENDENCY lexer.rl
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
add_custom_command(
|
||||
OUTPUT parser.cpp parser.h
|
||||
COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/parser.lemon" "parser.lemon"
|
||||
COMMAND lemon parser.lemon
|
||||
@ -31,6 +31,20 @@ add_custom_command(
|
||||
DEPENDS debugger.h
|
||||
)
|
||||
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT template_parser.cpp template_parser.h
|
||||
COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/template_parser.lemon" "template_parser.lemon"
|
||||
COMMAND lemon template_parser.lemon
|
||||
COMMAND cp -f template_parser.h "${CMAKE_CURRENT_SOURCE_DIR}/"
|
||||
COMMAND cp -f template_parser.out "${CMAKE_CURRENT_SOURCE_DIR}/"
|
||||
COMMAND mv -f template_parser.c template_parser.cpp
|
||||
MAIN_DEPENDENCY template_parser.lemon
|
||||
DEPENDS debugger.h
|
||||
)
|
||||
|
||||
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT loadtrap.cpp
|
||||
COMMAND ragel -p -G2 -o loadtrap.cpp "${CMAKE_CURRENT_SOURCE_DIR}/loadtrap.rl"
|
||||
@ -39,16 +53,26 @@ add_custom_command(
|
||||
)
|
||||
|
||||
|
||||
set_source_files_properties(
|
||||
loadtrap.cpp lexer.cpp
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -Wno-unused-variable"
|
||||
add_custom_command(
|
||||
OUTPUT template_loader.cpp
|
||||
COMMAND ragel -p -G2 -o template_loader.cpp "${CMAKE_CURRENT_SOURCE_DIR}/template_loader.rl"
|
||||
MAIN_DEPENDENCY template_loader.rl
|
||||
DEPENDS debugger.h template_parser.h
|
||||
)
|
||||
|
||||
|
||||
set_source_files_properties(
|
||||
loadtrap.cpp lexer.cpp template_loader.cpp
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -Wno-unused-variable"
|
||||
)
|
||||
|
||||
add_executable(mpw loader.cpp debugger.cpp debugger_internal.cpp
|
||||
address_map.cpp lexer.cpp parser.cpp loadtrap.cpp commands.cpp)
|
||||
address_map.cpp lexer.cpp parser.cpp loadtrap.cpp
|
||||
commands.cpp
|
||||
template_loader.cpp template_parser.cpp intern.cpp template.cpp)
|
||||
|
||||
|
||||
target_link_libraries(mpw CPU_LIB)
|
||||
target_link_libraries(mpw TOOLBOX_LIB)
|
||||
|
181
bin/commands.cpp
181
bin/commands.cpp
@ -1,19 +1,70 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
#include <toolbox/os.h>
|
||||
#include <macos/errors.h>
|
||||
|
||||
#include <cpu/defs.h>
|
||||
#include <cpu/CpuModule.h>
|
||||
|
||||
#include "debugger.h"
|
||||
#include "debugger_internal.h"
|
||||
#include "loader.h" // Flags
|
||||
|
||||
|
||||
namespace Debug {
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
|
||||
void hexdump(const uint8_t *data, ssize_t size, uint32_t address = 0)
|
||||
{
|
||||
const char *HexMap = "0123456789abcdef";
|
||||
|
||||
char buffer1[16 * 3 + 1 + 1];
|
||||
char buffer2[16 + 1];
|
||||
ssize_t offset = 0;
|
||||
unsigned i, j;
|
||||
|
||||
|
||||
while(size > 0)
|
||||
{
|
||||
std::memset(buffer1, ' ', sizeof(buffer1));
|
||||
std::memset(buffer2, ' ', sizeof(buffer2));
|
||||
|
||||
unsigned linelen = (unsigned)std::min(size, (ssize_t)16);
|
||||
|
||||
|
||||
for (i = 0, j = 0; i < linelen; i++)
|
||||
{
|
||||
unsigned x = data[i];
|
||||
buffer1[j++] = HexMap[x >> 4];
|
||||
buffer1[j++] = HexMap[x & 0x0f];
|
||||
j++;
|
||||
if (i == 7) j++;
|
||||
|
||||
// isascii not part of std:: and may be a macro.
|
||||
buffer2[i] = isascii(x) && std::isprint(x) ? x : '.';
|
||||
|
||||
}
|
||||
|
||||
buffer1[sizeof(buffer1)-1] = 0;
|
||||
buffer2[sizeof(buffer2)-1] = 0;
|
||||
|
||||
|
||||
std::printf("%06x: %s %s\n", address + (unsigned)offset, buffer1, buffer2);
|
||||
offset += 16;
|
||||
data += 16;
|
||||
size -= 16;
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrintError(uint32_t value)
|
||||
{
|
||||
/* expr ; error -- interpret expr as an OSErr */
|
||||
@ -66,6 +117,7 @@ namespace Debug {
|
||||
struct tm *tm;
|
||||
time_t t = OS::MacToUnix(value);
|
||||
|
||||
// localtime vs gmtime?
|
||||
tm = ::localtime(&t);
|
||||
strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M:%S %p", tm);
|
||||
puts(buffer);
|
||||
@ -76,30 +128,115 @@ namespace Debug {
|
||||
{
|
||||
|
||||
puts("");
|
||||
puts(" ## ");
|
||||
puts(" ## ## #### ");
|
||||
puts(" ## #### ## ");
|
||||
puts(" ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## #### ");
|
||||
puts("## ## ## ## ");
|
||||
puts(" ######## #### ## ## ");
|
||||
puts(" ## #################### ## ");
|
||||
puts(" ## ############## ## ");
|
||||
puts(" #### ############ ## ");
|
||||
puts(" ###### ###### ## ");
|
||||
puts(" ###### ## ");
|
||||
puts(" #### ## ");
|
||||
puts(" ## ## ");
|
||||
puts(" ## ################ ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ###### ###### ");
|
||||
puts(" ## ");
|
||||
puts(" ## ## #### ");
|
||||
puts(" ## #### ## ");
|
||||
puts(" ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## #### ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ######## #### ## ## ");
|
||||
puts(" ## #################### ## ");
|
||||
puts(" ## ############## ## ");
|
||||
puts(" #### ############ ## ");
|
||||
puts(" ###### ###### ## ");
|
||||
puts(" ###### ## ");
|
||||
puts(" #### ## ");
|
||||
puts(" ## ## ");
|
||||
puts(" ## ################ ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ## ## ## ## ");
|
||||
puts(" ###### ###### ");
|
||||
puts("");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void StackCrawl(void)
|
||||
{
|
||||
// stack crawl based on a6 frame pointers. (link/unlink)
|
||||
// start with current a6.
|
||||
|
||||
uint32_t a6 = cpuGetAReg(6);
|
||||
|
||||
/*
|
||||
* a6: previous a6
|
||||
* a6+4 return pc
|
||||
* .... parameters, locals, stack data
|
||||
* previous a6
|
||||
* return pc
|
||||
* ....
|
||||
*/
|
||||
// todo -- need a function to verify address is w/in stack range.
|
||||
|
||||
// todo print in reverse order so newest frame doesn't scroll away.
|
||||
|
||||
if (!a6) return;
|
||||
printf("a6: %08x\n", a6);
|
||||
|
||||
while(a6)
|
||||
{
|
||||
|
||||
uint32_t prevA6 = ReadLong(a6);
|
||||
if (prevA6 <= a6) break;
|
||||
|
||||
uint32_t pc = ReadLong(a6+4); //
|
||||
|
||||
|
||||
|
||||
// hexdump contents between pc and previous....
|
||||
ssize_t size = prevA6 - a6 - 8;
|
||||
hexdump(Flags.memory + a6 + 8, size);
|
||||
|
||||
|
||||
|
||||
puts("------------");
|
||||
printf("a6: %08x\n", prevA6);
|
||||
printf("pc: %08x", pc);
|
||||
|
||||
// find the routine name...
|
||||
for (const auto &kv : SymbolTable)
|
||||
{
|
||||
const auto &name = kv.first;
|
||||
auto range = kv.second;
|
||||
|
||||
// range end may be 0
|
||||
if ((pc == range.first) || (pc >= range.first && pc < range.second))
|
||||
{
|
||||
uint32_t offset = pc - range.first;
|
||||
if (offset)
|
||||
printf(" %s+$%x", name.c_str(), offset);
|
||||
else
|
||||
printf(" %s", name.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
puts("");
|
||||
|
||||
a6 = prevA6;
|
||||
}
|
||||
}
|
||||
|
||||
void Dump(uint32_t start, int size)
|
||||
{
|
||||
// TODO -- if no address, use previous address.
|
||||
// TODO -- support range?
|
||||
|
||||
|
||||
if (size <= 0) return;
|
||||
|
||||
uint32_t end = start + size;
|
||||
|
||||
if (start >= Flags.memorySize) return;
|
||||
|
||||
end = std::min(end, Flags.memorySize);
|
||||
size = end - start;
|
||||
|
||||
hexdump(Flags.memory + start, size, start);
|
||||
}
|
||||
|
||||
|
||||
}
|
114
bin/debugger.cpp
114
bin/debugger.cpp
@ -49,6 +49,7 @@
|
||||
|
||||
#include "debugger.h"
|
||||
#include "debugger_internal.h"
|
||||
#include "template.h"
|
||||
|
||||
#include <cpu/defs.h>
|
||||
#include <cpu/CpuModule.h>
|
||||
@ -62,14 +63,12 @@
|
||||
#include <toolbox/loader.h>
|
||||
#include <toolbox/mm.h>
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Debug::Internal;
|
||||
|
||||
const uint32_t kGlobalSize = 0x10000;
|
||||
const uint32_t kBackTraceSize = 20;
|
||||
const uint32_t kBackTraceSize = 50;
|
||||
|
||||
bool sigInt = false;
|
||||
bool memBreak = false;
|
||||
@ -84,6 +83,8 @@ namespace {
|
||||
ToolMap tbrkMap; // tool breaks.
|
||||
|
||||
|
||||
std::unordered_map<std::string, Debug::Template> TemplateTable;
|
||||
|
||||
struct BackTraceInfo {
|
||||
uint32_t a[8];
|
||||
uint32_t d[8];
|
||||
@ -103,48 +104,7 @@ namespace {
|
||||
|
||||
std::deque<BackTraceInfo> BackTrace;
|
||||
|
||||
void hexdump(const uint8_t *data, ssize_t size, uint32_t address = 0)
|
||||
{
|
||||
const char *HexMap = "0123456789abcdef";
|
||||
|
||||
char buffer1[16 * 3 + 1 + 1];
|
||||
char buffer2[16 + 1];
|
||||
ssize_t offset = 0;
|
||||
unsigned i, j;
|
||||
|
||||
|
||||
while(size > 0)
|
||||
{
|
||||
std::memset(buffer1, ' ', sizeof(buffer1));
|
||||
std::memset(buffer2, ' ', sizeof(buffer2));
|
||||
|
||||
unsigned linelen = (unsigned)std::min(size, (ssize_t)16);
|
||||
|
||||
|
||||
for (i = 0, j = 0; i < linelen; i++)
|
||||
{
|
||||
unsigned x = data[i];
|
||||
buffer1[j++] = HexMap[x >> 4];
|
||||
buffer1[j++] = HexMap[x & 0x0f];
|
||||
j++;
|
||||
if (i == 7) j++;
|
||||
|
||||
// isascii not part of std:: and may be a macro.
|
||||
buffer2[i] = isascii(x) && std::isprint(x) ? x : '.';
|
||||
|
||||
}
|
||||
|
||||
buffer1[sizeof(buffer1)-1] = 0;
|
||||
buffer2[sizeof(buffer2)-1] = 0;
|
||||
|
||||
|
||||
std::printf("%06x: %s %s\n", address + (unsigned)offset, buffer1, buffer2);
|
||||
offset += 16;
|
||||
data += 16;
|
||||
size -= 16;
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void printMacsbug(uint32_t pc, uint32_t opcode, uint32_t *newPC = nullptr)
|
||||
@ -479,6 +439,34 @@ namespace {
|
||||
|
||||
namespace Debug {
|
||||
|
||||
std::string ReadPString(uint32_t address)
|
||||
{
|
||||
std::string tmp;
|
||||
unsigned size = ReadByte(address++);
|
||||
|
||||
tmp.reserve(size);
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
tmp.push_back(ReadByte(address++));
|
||||
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::string ReadCString(uint32_t address)
|
||||
{
|
||||
std::string tmp;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char c = ReadByte(address++);
|
||||
if (!c) break;
|
||||
tmp.push_back(c);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ReadLong(uint32_t address)
|
||||
{
|
||||
uint32_t tmp = 0;
|
||||
@ -633,23 +621,6 @@ void Print(uint32_t data)
|
||||
}
|
||||
|
||||
|
||||
void Dump(uint32_t start, int size)
|
||||
{
|
||||
// TODO -- if no address, use previous address.
|
||||
// TODO -- support range?
|
||||
|
||||
|
||||
if (size <= 0) return;
|
||||
|
||||
uint32_t end = start + size;
|
||||
|
||||
if (start >= Flags.memorySize) return;
|
||||
|
||||
end = std::min(end, Flags.memorySize);
|
||||
size = end - start;
|
||||
|
||||
hexdump(Flags.memory + start, size, start);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -730,7 +701,7 @@ void PrintRegisters(const BackTraceInfo &i)
|
||||
i.a[4], i.a[5], i.a[6], i.a[7]
|
||||
);
|
||||
|
||||
printf("PC: %08X CSR: %04x %s\n", i.pc, i.csr, srbits);
|
||||
printf("PC: %08x CSR: %04x %s\n", i.pc, i.csr, srbits);
|
||||
|
||||
}
|
||||
|
||||
@ -1135,8 +1106,24 @@ void Info(uint32_t address)
|
||||
cp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ApplyTemplate(int32_t address, const std::string &name)
|
||||
{
|
||||
// find the template..
|
||||
|
||||
auto iter = TemplateTable.find(name);
|
||||
if (iter == TemplateTable.end()) {
|
||||
fprintf(stderr, "Unknown template: %s\n", name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
FieldEntry *e = iter->second;
|
||||
ApplyTemplate(address, e);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
@ -1255,6 +1242,9 @@ void Shell()
|
||||
LoadTrapFile(MPW::RootDirPathForFile("Globals.text"), GlobalTable);
|
||||
LoadTrapFile(MPW::RootDirPathForFile("Traps.text"), TrapTable);
|
||||
|
||||
LoadTemplateFile(MPW::RootDirPathForFile("Templates.text"), TemplateTable);
|
||||
|
||||
|
||||
|
||||
// load the error code to error mnemonic
|
||||
ErrorTableInvert.reserve(ErrorTable.size());
|
||||
|
@ -110,6 +110,9 @@ uint32_t ReadLong(uint32_t);
|
||||
uint16_t ReadWord(uint32_t);
|
||||
uint8_t ReadByte(uint32_t);
|
||||
|
||||
std::string ReadPString(uint32_t);
|
||||
std::string ReadCString(uint32_t);
|
||||
|
||||
void Print(uint32_t value);
|
||||
void PrintRegisters();
|
||||
|
||||
@ -144,6 +147,10 @@ void ReadWriteBreak(int32_t address);
|
||||
void PrintError(uint32_t value);
|
||||
void PrintDate(uint32_t value);
|
||||
|
||||
void StackCrawl(void);
|
||||
|
||||
void ApplyTemplate(int32_t address, const std::string &name);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
101
bin/intern.cpp
Normal file
101
bin/intern.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "intern.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace {
|
||||
|
||||
std::unordered_multimap<unsigned, std::string *> InternTable;
|
||||
|
||||
std::string EmptyString;
|
||||
|
||||
unsigned int DJBHash(const char* begin, size_t length)
|
||||
{
|
||||
unsigned int hash = 5381;
|
||||
|
||||
for(size_t i = 0; i < length; ++i)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + (begin[i]);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Intern {
|
||||
|
||||
const std::string *String(std::string &&str)
|
||||
{
|
||||
size_t size = str.size();
|
||||
if (!size) return &EmptyString;
|
||||
|
||||
unsigned hash = DJBHash(str.data(), size);
|
||||
|
||||
auto range = InternTable.equal_range(hash);
|
||||
auto iter = range.first;
|
||||
auto endit = range.second;
|
||||
|
||||
for( ; iter != endit; ++iter)
|
||||
{
|
||||
// hash matches, make sure the string does.
|
||||
const std::string *s = iter->second;
|
||||
|
||||
if (s->size() == size && std::memcmp(s->data(), str.data(), size) == 0)
|
||||
return s;
|
||||
}
|
||||
|
||||
// insert it. I suppose this could throw, in which case a string would leak.
|
||||
std::string *s = new std::string(std::move(str));
|
||||
InternTable.emplace(std::make_pair(hash, s));
|
||||
return s;
|
||||
}
|
||||
|
||||
const std::string *String(const char *begin, size_t size)
|
||||
{
|
||||
if (!size) return &EmptyString;
|
||||
|
||||
unsigned hash = DJBHash(begin, size);
|
||||
|
||||
auto range = InternTable.equal_range(hash);
|
||||
auto iter = range.first;
|
||||
auto endit = range.second;
|
||||
|
||||
for( ; iter != endit; ++iter)
|
||||
{
|
||||
// hash matches, make sure the string does.
|
||||
const std::string *s = iter->second;
|
||||
|
||||
if (s->size() == size && std::memcmp(s->data(), begin, size) == 0)
|
||||
return s;
|
||||
}
|
||||
|
||||
// insert it. I suppose this could throw, in which case a string would leak.
|
||||
std::string *s = new std::string(begin, size);
|
||||
InternTable.emplace(std::make_pair(hash, s));
|
||||
return s;
|
||||
}
|
||||
|
||||
const std::string *String(const char *begin, const char *end)
|
||||
{
|
||||
return String(begin, end - begin);
|
||||
}
|
||||
|
||||
|
||||
const std::string *String(const char *cp)
|
||||
{
|
||||
if (!cp || !*cp) return &EmptyString;
|
||||
|
||||
return String(cp, strlen(cp));
|
||||
}
|
||||
|
||||
const std::string *String(const std::string &s)
|
||||
{
|
||||
return String(s.data(), s.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
14
bin/intern.h
Normal file
14
bin/intern.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __intern_h__
|
||||
#define __intern_h__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Intern {
|
||||
const std::string *String(const std::string &s);
|
||||
const std::string *String(std::string &&s);
|
||||
const std::string *String(const char *);
|
||||
const std::string *String(const char *, size_t size);
|
||||
const std::string *String(const char *, const char *);
|
||||
};
|
||||
|
||||
#endif
|
83
bin/lexer.rl
83
bin/lexer.rl
@ -105,6 +105,21 @@ namespace {
|
||||
# this exits with cs == lexer_en_error.
|
||||
error := any* ${ fbreak; };
|
||||
|
||||
# identifiers.
|
||||
ident := |*
|
||||
|
||||
[ \t\r\n]+;
|
||||
|
||||
[_A-Za-z][_A-Za-z0-9]* {
|
||||
|
||||
std::unique_ptr<std::string> sp(new std::string(ts, te));
|
||||
|
||||
Parse(parser, tkIDENTIFIER, Token::Make(sp.get(), 0), command);
|
||||
Strings.push_back(std::move(sp));
|
||||
};
|
||||
|
||||
*|;
|
||||
|
||||
# semi-colon commands.
|
||||
semi := |*
|
||||
|
||||
@ -130,6 +145,10 @@ namespace {
|
||||
Parse(parser, tkSEMIERROR, 0, command);
|
||||
};
|
||||
|
||||
't'i {
|
||||
Parse(parser, tkSEMIT, 0, command);
|
||||
fgoto ident;
|
||||
};
|
||||
|
||||
|
||||
*|;
|
||||
@ -196,6 +215,7 @@ namespace {
|
||||
Parse(parser, tkINTEGER, value, command);
|
||||
};
|
||||
|
||||
# todo -- (\['\]|[^'\]){1,4} ?
|
||||
['] [^']{1,4} ['] {
|
||||
// 4 cc code
|
||||
|
||||
@ -250,6 +270,12 @@ namespace {
|
||||
Parse(parser, tkDUMP, 0, command);
|
||||
};
|
||||
|
||||
|
||||
'sc'i | 'stackcrawl'i {
|
||||
Parse(parser, tkSTACKCRAWL, 0, command);
|
||||
};
|
||||
|
||||
|
||||
'h'i | 'help'i {
|
||||
Parse(parser, tkHELP, 0, command);
|
||||
};
|
||||
@ -350,28 +376,49 @@ bool ParseLine(const char *iter, Command *command)
|
||||
const char *te;
|
||||
int cs, act;
|
||||
|
||||
for(;;)
|
||||
%% write init;
|
||||
|
||||
|
||||
%% write exec;
|
||||
|
||||
if (cs == lexer_error || cs < lexer_first_final)
|
||||
{
|
||||
if (p == eof)
|
||||
{
|
||||
fprintf(stderr, "Unexpected end of line\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0, l = 2 + (p - iter); i < l; ++i)
|
||||
fputc(' ', stderr);
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
fprintf(stderr, "^\nunexpected character: `%c'\n", *p);
|
||||
|
||||
if (cs == lexer_error)
|
||||
{
|
||||
fprintf(stderr, "illegal character: `%c'\n", *p);
|
||||
ParseFree(parser, free);
|
||||
return false;
|
||||
}
|
||||
if (cs == lexer_en_error)
|
||||
{
|
||||
ParseFree(parser, free);
|
||||
return false;
|
||||
}
|
||||
if (p == pe)
|
||||
{
|
||||
Parse(parser, tkEOL, 0, command);
|
||||
break;
|
||||
}
|
||||
|
||||
ParseFree(parser, free);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (cs == lexer_en_error)
|
||||
{
|
||||
ParseFree(parser, free);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cs < lexer_first_final)
|
||||
{
|
||||
fprintf(stderr, "Incomplete command\n");
|
||||
ParseFree(parser, free);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
if (p == pe)
|
||||
{
|
||||
// always true?
|
||||
Parse(parser, tkEOL, 0, command);
|
||||
}
|
||||
|
||||
Parse(parser, 0, 0, command);
|
||||
|
@ -69,28 +69,6 @@ const uint32_t kGlobalSize = 0x10000;
|
||||
uint8_t *Memory = nullptr;
|
||||
uint32_t MemorySize = 0;
|
||||
|
||||
#if 0
|
||||
uint32_t EmulatedNewPtr(uint32_t size)
|
||||
{
|
||||
if (size & 0x01) size++;
|
||||
|
||||
if (HighWater + size > MemorySize)
|
||||
{
|
||||
fprintf(stderr, "Insufficient Memory!\n");
|
||||
exit(EX_CONFIG);
|
||||
}
|
||||
|
||||
|
||||
uint32_t address = HighWater;
|
||||
|
||||
|
||||
HighWater += size;
|
||||
|
||||
std::memset(Memory + HighWater, 0, size);
|
||||
|
||||
return address;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t ReadByte(const void *data, uint32_t offset)
|
||||
{
|
||||
|
@ -162,6 +162,10 @@ stmt ::= LIST expr(a) EOL.
|
||||
Debug::List(a.intValue);
|
||||
}
|
||||
|
||||
stmt ::= STACKCRAWL EOL.
|
||||
{
|
||||
Debug::StackCrawl();
|
||||
}
|
||||
|
||||
stmt ::= expr(a) SEMI SEMIH EOL.
|
||||
{
|
||||
@ -200,19 +204,30 @@ stmt ::= expr(a) COLON expr(b) SEMI SEMIL EOL.
|
||||
Debug::List(a.intValue, b.intValue);
|
||||
}
|
||||
|
||||
|
||||
stmt ::= expr(a) SEMI SEMIDATE EOL.
|
||||
{
|
||||
Debug::PrintDate(a.intValue);
|
||||
}
|
||||
|
||||
|
||||
stmt ::= expr(a) SEMI SEMIERROR EOL.
|
||||
{
|
||||
Debug::PrintError(a.intValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
stmt ::= expr(a) SEMI SEMIT IDENTIFIER(b) EOL.
|
||||
{
|
||||
Debug::ApplyTemplate(a.intValue, *b.stringValue);
|
||||
}
|
||||
|
||||
|
||||
stmt ::= expr SEMI SEMIT error EOL.
|
||||
{
|
||||
fprintf(stderr, "usage: expression ; t TemplateName\n");
|
||||
}
|
||||
|
||||
|
||||
stmt ::= DREGISTER(a) EQ expr(b) EOL.
|
||||
{
|
||||
Debug::SetDRegister(a.intValue, b.intValue);
|
||||
|
270
bin/template.cpp
Normal file
270
bin/template.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
#include "template.h"
|
||||
#include "debugger.h"
|
||||
#include "debugger_internal.h"
|
||||
#include "loader.h" // Flags.
|
||||
|
||||
#include <toolbox/toolbox.h>
|
||||
|
||||
|
||||
namespace Debug {
|
||||
|
||||
using namespace Debug::Internal;
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
unsigned CalcOneSize(FieldEntry *e)
|
||||
{
|
||||
unsigned s = (e->type & 0x0f00) >> 8;
|
||||
|
||||
if (!s) {
|
||||
// struct or pointer...
|
||||
if (e->type & 0x8000) s = 4;
|
||||
else if (e->tmpl) s = e->tmpl->struct_size;
|
||||
}
|
||||
|
||||
if (e->count) s *= e->count;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned CalcSize(FieldEntry *e)
|
||||
{
|
||||
unsigned size = 0;
|
||||
|
||||
while (e)
|
||||
{
|
||||
size += CalcOneSize(e);
|
||||
e = e->next;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
FieldEntry *Reverse(FieldEntry *e)
|
||||
{
|
||||
if (!e) return e;
|
||||
|
||||
// reverse the order...
|
||||
FieldEntry *prev;
|
||||
FieldEntry *next;
|
||||
|
||||
prev = nullptr;
|
||||
for(;;)
|
||||
{
|
||||
next = e->next;
|
||||
e->next = prev;
|
||||
|
||||
prev = e;
|
||||
e = next;
|
||||
if (!e) return prev;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool ValidPointer(uint32_t value)
|
||||
{
|
||||
return value && value < Flags.memorySize;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CreateTypedef(const std::string *name, int type, TemplateParseInfo *info)
|
||||
{
|
||||
// check if it's an existing typedef...
|
||||
|
||||
auto &Templates = *info->templates;
|
||||
auto &Types = *info->types;
|
||||
|
||||
|
||||
auto iter = Types.find(*name);
|
||||
if (iter != Types.end())
|
||||
{
|
||||
if (iter->second == type) return; // ok, just a duplicate.
|
||||
fprintf(stderr, "Template Error: line %d - redefining %s\n",
|
||||
info->LineNumber, name->c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Templates.find(*name) != Templates.end())
|
||||
{
|
||||
fprintf(stderr, "Template Error: line %d - redefining %s\n",
|
||||
info->LineNumber, name->c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Types.emplace(std::make_pair(*name, type));
|
||||
}
|
||||
|
||||
|
||||
void CreateTemplate(const std::string *name, FieldEntry *firstField, TemplateParseInfo *info)
|
||||
{
|
||||
auto &Templates = *info->templates;
|
||||
auto &Types = *info->types;
|
||||
|
||||
// check if it exists...
|
||||
|
||||
if (Templates.find(*name) != Templates.end())
|
||||
{
|
||||
fprintf(stderr, "Template Error: line %d - redefining %s\n",
|
||||
info->LineNumber, name->c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Types.find(*name) != Types.end())
|
||||
{
|
||||
fprintf(stderr, "Template Error: line %d - redefining %s\n",
|
||||
info->LineNumber, name->c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
firstField = Reverse(firstField);
|
||||
firstField->struct_size = CalcSize(firstField);
|
||||
|
||||
Templates.emplace(std::make_pair(*name, firstField));
|
||||
}
|
||||
|
||||
void CleanupString(std::string &s)
|
||||
{
|
||||
// replace non-printables.
|
||||
std::transform(s.begin(), s.end(), s.begin(), [](char c){
|
||||
return isprint(c) ? c : '.';
|
||||
});
|
||||
|
||||
if (s.size() > 40) {
|
||||
s.resize(37);
|
||||
s.append("...");
|
||||
}
|
||||
}
|
||||
void PrettyPrint(uint32_t value, unsigned type)
|
||||
{
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case kOSType:
|
||||
// print 4-cc code
|
||||
fputc(' ', stdout);
|
||||
fputs(ToolBox::TypeToString(value).c_str(), stdout);
|
||||
return;
|
||||
|
||||
case kOSErr:
|
||||
// print value + short name
|
||||
{
|
||||
printf(" %-6d", (int16_t)value);
|
||||
auto iter = ErrorTableInvert.find(value);
|
||||
if (iter != ErrorTableInvert.end()) printf(" %s", iter->second.c_str());
|
||||
}
|
||||
return;
|
||||
|
||||
case kPStringPtr:
|
||||
// read the string...
|
||||
if (ValidPointer(value))
|
||||
{
|
||||
std::string tmp = ReadPString(value);
|
||||
CleanupString(tmp);
|
||||
printf(" '%s'", tmp.c_str());
|
||||
}
|
||||
return;
|
||||
|
||||
case kCStringPtr:
|
||||
// read the string...
|
||||
if (ValidPointer(value))
|
||||
{
|
||||
std::string tmp = ReadCString(value);
|
||||
CleanupString(tmp);
|
||||
printf(" '%s'", tmp.c_str());
|
||||
}
|
||||
return;
|
||||
return;
|
||||
|
||||
case kBoolean:
|
||||
fputc(' ', stdout);
|
||||
fputs(value ? "true" : "false", stdout);
|
||||
return;
|
||||
|
||||
case kHandle:
|
||||
// print address, size, locked stats.
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// int/signed int - print base 10.
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ApplyTemplate(uint32_t address, FieldEntry *e, unsigned indent)
|
||||
{
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!e) return;
|
||||
|
||||
for( ; e ; e = e->next)
|
||||
{
|
||||
|
||||
bool nonl = false;
|
||||
unsigned count = e->count;
|
||||
unsigned type = e->type;
|
||||
unsigned s = (type & 0x0f00) >> 8;
|
||||
|
||||
for (unsigned i = 0; i < indent; ++i) fputc('>',stdout);
|
||||
fputs(e->name->c_str(),stdout);
|
||||
for(unsigned i = indent + e->name->length(); i < 40; ++i) fputc(' ',stdout);
|
||||
|
||||
// todo -- support arrays
|
||||
// todo -- pretty print values (boolean, oserr, ostype, etc.)
|
||||
switch(s)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
uint8_t value = ReadByte(address + offset);
|
||||
printf(" %02x", value);
|
||||
PrettyPrint(value, type);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
uint16_t value = ReadWord(address + offset);
|
||||
printf(" %04x", value);
|
||||
PrettyPrint(value, type);
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
uint32_t value = ReadLong(address + offset);
|
||||
printf("%08x", value);
|
||||
PrettyPrint(value, type);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0:
|
||||
// either a pointer or a struct
|
||||
if (type & 0x8000) {
|
||||
// pointer.
|
||||
uint32_t value = ReadLong(address + offset);
|
||||
printf("%08x", value);
|
||||
PrettyPrint(value, type);
|
||||
break;
|
||||
}
|
||||
if (type == 0) {
|
||||
// struct ... recurse.
|
||||
fputc('\n', stdout);
|
||||
nonl = true;
|
||||
ApplyTemplate(address + offset, e->tmpl, indent + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nonl) fputc('\n', stdout);
|
||||
offset += CalcOneSize(e);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
88
bin/template.h
Normal file
88
bin/template.h
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef __debug_template_h__
|
||||
#define __debug_template_h__
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace Debug {
|
||||
|
||||
|
||||
enum {
|
||||
|
||||
// 0x8000 = pointer
|
||||
// 0x1000 = unsigned
|
||||
// 0x0f00 = size
|
||||
// 0-255 - type
|
||||
|
||||
kSInt64 = 0x0800,
|
||||
kUInt64 = 0x1800,
|
||||
|
||||
kSInt32 = 0x0400,
|
||||
kUInt32 = 0x1400,
|
||||
|
||||
kSInt16 = 0x0200,
|
||||
kUInt16 = 0x1200,
|
||||
|
||||
kSInt8 = 0x0100,
|
||||
kUInt8 = 0x1100,
|
||||
|
||||
kStruct = 0x0000,
|
||||
kStructPtr = 0x8000,
|
||||
|
||||
// ptrs are a special case where size = 0
|
||||
//kVoid = 0x0001,
|
||||
kVoidPtr = 0x8001,
|
||||
|
||||
// these exist for display purposes.
|
||||
kCStringPtr = 0x8002,
|
||||
kPStringPtr = 0x8003,
|
||||
|
||||
kOSType = 0x00404,
|
||||
kBoolean = 0x0105,
|
||||
kOSErr = 0x0206,
|
||||
kHandle = 0x04007,
|
||||
|
||||
};
|
||||
|
||||
inline unsigned MakePtr(unsigned type) { return type | 0x8000; }
|
||||
inline unsigned MakeType(unsigned size, bool sign, bool ptr) {
|
||||
unsigned rv = size << 8;
|
||||
if (!sign) rv |= 0x0100;
|
||||
if (ptr) rv |= 0x8000;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct FieldEntry;
|
||||
typedef FieldEntry *Template;
|
||||
|
||||
struct FieldEntry {
|
||||
std::string *name;
|
||||
unsigned type;
|
||||
unsigned count;
|
||||
Template tmpl;
|
||||
FieldEntry *next;
|
||||
unsigned struct_size; // only populated for head entry.
|
||||
};
|
||||
|
||||
struct TemplateParseInfo {
|
||||
std::unordered_map<std::string, Template> *templates;
|
||||
std::unordered_map<std::string, unsigned> *types;
|
||||
int LineNumber;
|
||||
};
|
||||
|
||||
|
||||
void CreateTypedef(const std::string *name, int type, TemplateParseInfo *);
|
||||
void CreateTemplate(const std::string *name, FieldEntry *firstField, TemplateParseInfo *);
|
||||
|
||||
|
||||
bool LoadTemplateFile(const std::string &filename, std::unordered_map<std::string, Template> &);
|
||||
|
||||
|
||||
void ApplyTemplate(uint32_t address, FieldEntry *e, unsigned indent = 0);
|
||||
|
||||
|
||||
|
||||
}
|
||||
#endif
|
254
bin/template_loader.rl
Normal file
254
bin/template_loader.rl
Normal file
@ -0,0 +1,254 @@
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "template_parser.h"
|
||||
#include "template.h"
|
||||
#include "intern.h"
|
||||
|
||||
namespace {
|
||||
|
||||
int tox(char c)
|
||||
{
|
||||
c |= 0x20; // lowercase it.
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
return 0;
|
||||
}
|
||||
uint32_t scan10(const char *begin, const char *end)
|
||||
{
|
||||
return std::accumulate(begin, end, 0,
|
||||
[](uint32_t value, char c){
|
||||
return value * 10 + c - '0';
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t scan16(const char *begin, const char *end)
|
||||
{
|
||||
return std::accumulate(begin, end, 0,
|
||||
[](uint32_t value, char c){
|
||||
return (value << 4) + tox(c);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void *TemplateParseAlloc(void *(*mallocProc)(size_t));
|
||||
void TemplateParseFree(void *p, void (*freeProc)(void*));
|
||||
|
||||
void TemplateParse(void *yyp, int yymajor, void *yyminor, Debug::TemplateParseInfo *);
|
||||
|
||||
|
||||
void TemplateParse(void *yyp, int yymajor, int yyminor, Debug::TemplateParseInfo *info)
|
||||
{
|
||||
TemplateParse(yyp, yymajor, (void *)(ptrdiff_t)yyminor, info);
|
||||
}
|
||||
|
||||
void TemplateParse(void *yyp, int yymajor, const std::string *yyminor, Debug::TemplateParseInfo *info)
|
||||
{
|
||||
TemplateParse(yyp, yymajor, (void *)yyminor, info);
|
||||
}
|
||||
|
||||
#define TemplateParse(a,b,c) TemplateParse(a,b,c, &info)
|
||||
|
||||
%%{
|
||||
machine lexer;
|
||||
|
||||
# this exits with cs == lexer_en_error.
|
||||
error := any* ${ fbreak; };
|
||||
|
||||
block_comment := |*
|
||||
[\n\r] { info.LineNumber++; };
|
||||
'*/' { fgoto main; };
|
||||
any ;
|
||||
*|;
|
||||
|
||||
main := |*
|
||||
|
||||
[\n\r] { info.LineNumber++; };
|
||||
[ \t]+;
|
||||
|
||||
'//' [^\r\n]* ;
|
||||
'/*' { fgoto block_comment; };
|
||||
|
||||
';' { TemplateParse(parser, tkSEMI, 0); };
|
||||
'{' { TemplateParse(parser, tkLBRACE, 0); };
|
||||
'}' { TemplateParse(parser, tkRBRACE, 0); };
|
||||
'[' { TemplateParse(parser, tkLBRACKET, 0); };
|
||||
']' { TemplateParse(parser, tkRBRACKET, 0); };
|
||||
'*' { TemplateParse(parser, tkSTAR, 0); };
|
||||
|
||||
'struct' { TemplateParse(parser, tkSTRUCT, 0); };
|
||||
'typedef' { TemplateParse(parser, tkTYPEDEF, 0); };
|
||||
|
||||
|
||||
'int' { TemplateParse(parser, tkINT, 0); };
|
||||
'long' { TemplateParse(parser, tkLONG, 0); };
|
||||
'short' { TemplateParse(parser, tkSHORT, 0); };
|
||||
'volatile' { TemplateParse(parser, tkVOLATILE, 0); };
|
||||
#'const' { TemplateParse(parser, tkCONST, 0); };
|
||||
'char' { TemplateParse(parser, tkCHAR, 0); };
|
||||
#'bool' { TemplateParse(parser, tkBOOL, 0); };
|
||||
'void' { TemplateParse(parser, tkVOID, 0); };
|
||||
|
||||
'signed' { TemplateParse(parser, tkSIGNED, 0); };
|
||||
'unsigned' { TemplateParse(parser, tkUNSIGNED, 0); };
|
||||
|
||||
'int64_t' { TemplateParse(parser, tkTYPECODE, kSInt64); };
|
||||
'uint64_t' { TemplateParse(parser, tkTYPECODE, kUInt64); };
|
||||
|
||||
'int32_t' { TemplateParse(parser, tkTYPECODE, kSInt32); };
|
||||
'uint32_t' { TemplateParse(parser, tkTYPECODE, kUInt32); };
|
||||
|
||||
'int16_t' { TemplateParse(parser, tkTYPECODE, kSInt16); };
|
||||
'uint16_t' { TemplateParse(parser, tkTYPECODE, kUInt16); };
|
||||
|
||||
'int8_t' { TemplateParse(parser, tkTYPECODE, kSInt8); };
|
||||
'uint8_t' { TemplateParse(parser, tkTYPECODE, kUInt8); };
|
||||
|
||||
|
||||
'StringPtr' { TemplateParse(parser, tkTYPECODE, kPStringPtr); };
|
||||
'CStringPtr' { TemplateParse(parser, tkTYPECODE, kCStringPtr); };
|
||||
'Ptr' { TemplateParse(parser, tkTYPECODE, kVoidPtr); };
|
||||
'OSType' { TemplateParse(parser, tkTYPECODE, kOSType); };
|
||||
'OSErr' { TemplateParse(parser, tkTYPECODE, kOSErr); };
|
||||
'Boolean' { TemplateParse(parser, tkTYPECODE, kBoolean); };
|
||||
'Handle' { TemplateParse(parser, tkTYPECODE, kHandle); };
|
||||
|
||||
|
||||
# numbers. negative numbers are not allowed.
|
||||
|
||||
'0x'i xdigit+ {
|
||||
// hexadecimal
|
||||
uint32_t value = scan16(ts + 2, te);
|
||||
TemplateParse(parser, tkINTEGER, value);
|
||||
};
|
||||
|
||||
digit+ {
|
||||
uint32_t value = scan10(ts, te);
|
||||
TemplateParse(parser, tkINTEGER, value);
|
||||
};
|
||||
|
||||
# identifier ... but also need to check if it's a type.
|
||||
[A-Za-z_][A-Za-z0-9_]* {
|
||||
|
||||
// intern the string.
|
||||
|
||||
const std::string *name = Intern::String(ts, te);
|
||||
bool ok = false;
|
||||
|
||||
if (!ok) {
|
||||
auto iter = Types.find(*name);
|
||||
if (iter != Types.end())
|
||||
{
|
||||
TemplateParse(parser, tkTYPECODE, iter->second);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
auto iter = Templates.find(*name);
|
||||
if (iter != Templates.end())
|
||||
{
|
||||
TemplateParse(parser, tkTEMPLATE, iter->second);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
TemplateParse(parser, tkIDENTIFIER, name);
|
||||
}
|
||||
};
|
||||
|
||||
*|;
|
||||
|
||||
}%%
|
||||
|
||||
namespace Debug {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool LoadTemplateFile(const std::string &filename, std::unordered_map<std::string, Template> &Templates)
|
||||
{
|
||||
%% write data;
|
||||
|
||||
void *parser;
|
||||
|
||||
int fd;
|
||||
struct stat st;
|
||||
char *buffer;
|
||||
|
||||
std::unordered_map<std::string, unsigned> Types;
|
||||
TemplateParseInfo info;
|
||||
|
||||
info.LineNumber = 1;
|
||||
info.templates = &Templates;
|
||||
info.types = &Types;
|
||||
|
||||
// simple types are handled via the lexer so no need to populate them here.
|
||||
|
||||
|
||||
|
||||
if (stat(filename.c_str(), &st) < 0) return false;
|
||||
if (st.st_size == 0) return false;
|
||||
|
||||
fd = open(filename.c_str(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Error opening template file: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = (char *)mmap(nullptr, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
|
||||
if (buffer == MAP_FAILED) {
|
||||
perror("Error mapping template file: ");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
|
||||
|
||||
|
||||
parser = TemplateParseAlloc(malloc);
|
||||
|
||||
const char *p = buffer;
|
||||
const char *pe = buffer + st.st_size;
|
||||
const char *eof = pe;
|
||||
const char *ts;
|
||||
const char *te;
|
||||
int cs, act;
|
||||
|
||||
%% write init;
|
||||
%% write exec;
|
||||
|
||||
if (cs == lexer_error || cs < lexer_first_final)
|
||||
{
|
||||
if (p == eof)
|
||||
fprintf(stderr, "Template error: line %d - unexpected EOF\n", info.LineNumber);
|
||||
else
|
||||
fprintf(stderr, "Template error: line %d - illegal character: `%c'\n", info.LineNumber, *p);
|
||||
TemplateParseFree(parser, free);
|
||||
munmap(buffer, st.st_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TemplateParse(parser, 0, 0);
|
||||
TemplateParseFree(parser, free);
|
||||
|
||||
munmap(buffer, st.st_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
127
bin/template_parser.lemon
Normal file
127
bin/template_parser.lemon
Normal file
@ -0,0 +1,127 @@
|
||||
%token_prefix tk
|
||||
%name TemplateParse
|
||||
%extra_argument { Debug::TemplateParseInfo *info }
|
||||
|
||||
|
||||
%include {
|
||||
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "template.h"
|
||||
|
||||
using namespace Debug;
|
||||
}
|
||||
|
||||
%type struct_fields { FieldEntry * }
|
||||
%type struct_field { FieldEntry * }
|
||||
|
||||
start ::= templates.
|
||||
|
||||
templates ::= templates struct.
|
||||
templates ::= templates typedef.
|
||||
templates ::= .
|
||||
|
||||
// typedeffing arrays or pointers is not allowed.
|
||||
|
||||
typedef ::= TYPEDEF type(a) IDENTIFIER(b). {
|
||||
CreateTypedef((std::string *)b, a, info);
|
||||
}
|
||||
|
||||
struct ::= STRUCT IDENTIFIER(a) LBRACE struct_fields(b) RBRACE SEMI.
|
||||
{
|
||||
CreateTemplate((std::string *)a, b, info);
|
||||
}
|
||||
|
||||
struct_fields(rhs) ::= struct_fields(a) struct_field(b). {
|
||||
// reverse order?
|
||||
b->next = a;
|
||||
rhs = b;
|
||||
}
|
||||
|
||||
struct_fields(rhs) ::= struct_field(a). {
|
||||
rhs = a;
|
||||
}
|
||||
|
||||
struct_field(rhs) ::= type(a) IDENTIFIER(b) array_count(c) SEMI.
|
||||
{
|
||||
FieldEntry *e = (FieldEntry *)calloc(sizeof(FieldEntry), 1);
|
||||
|
||||
e->name = (std::string *)b;
|
||||
e->type = a;
|
||||
e->count = c;
|
||||
rhs = e;
|
||||
}
|
||||
|
||||
struct_field(rhs) ::= opt_volatile TEMPLATE(a) opt_star(star) IDENTIFIER(b) array_count(c) SEMI. {
|
||||
FieldEntry *e = (FieldEntry *)calloc(sizeof(FieldEntry), 1);
|
||||
|
||||
e->name = (std::string *)b;
|
||||
e->type = star ? kStructPtr : kStruct;
|
||||
e->tmpl = (Template)a;
|
||||
e->count = c;
|
||||
|
||||
rhs = e;
|
||||
}
|
||||
|
||||
%type array_count { int }
|
||||
array_count(rhs) ::= . { rhs = 0; }
|
||||
array_count(rhs) ::= LBRACKET INTEGER(a) RBRACKET. {
|
||||
int i = (int)(ptrdiff_t)a;
|
||||
if (i == 0) {
|
||||
fprintf(stderr, "Template error: line %u: 0-sized arrays are not allowed.\n",
|
||||
info->LineNumber);
|
||||
i = 1;
|
||||
}
|
||||
rhs = i;
|
||||
}
|
||||
|
||||
%type type { int }
|
||||
type(rhs) ::= opt_volatile typecode(a). { rhs = a; }
|
||||
|
||||
// this is an expected error...
|
||||
type(rhs) ::= opt_volatile IDENTIFIER(xxx). {
|
||||
|
||||
// ugh, Lemon will blindly replace text within a string.
|
||||
fprintf(stderr, "Template error: line %u: %s is not a known type.\n",
|
||||
info->LineNumber, ((std::string *)xxx)->c_str());
|
||||
|
||||
rhs = 'i';
|
||||
}
|
||||
|
||||
|
||||
opt_volatile ::= .
|
||||
opt_volatile ::= VOLATILE.
|
||||
|
||||
|
||||
%type typecode { int }
|
||||
typecode(rhs) ::= SIGNED. { rhs = kSInt32; }
|
||||
typecode(rhs) ::= UNSIGNED. {rhs = kUInt32; }
|
||||
|
||||
typecode(rhs) ::= opt_signed CHAR. { rhs = kSInt8; }
|
||||
typecode(rhs) ::= UNSIGNED CHAR. { rhs = kUInt8; }
|
||||
|
||||
typecode(rhs) ::= opt_signed SHORT. { rhs = kSInt16; }
|
||||
typecode(rhs) ::= UNSIGNED SHORT. { rhs = kUInt16; }
|
||||
|
||||
typecode(rhs) ::= opt_signed LONG opt_int. { rhs = kSInt32; }
|
||||
typecode(rhs) ::= UNSIGNED LONG opt_int. { rhs = kUInt32; }
|
||||
|
||||
typecode(rhs) ::= opt_signed LONG LONG. { rhs = kSInt64; }
|
||||
typecode(rhs) ::= UNSIGNED LONG LONG. { rhs = kUInt64; }
|
||||
|
||||
typecode(rhs) ::= TYPECODE(a). { rhs = (int)(ptrdiff_t)a; }
|
||||
|
||||
/* pointers are not fully supported yet */
|
||||
typecode(rhs) ::= VOID STAR. { rhs = kVoidPtr; }
|
||||
|
||||
opt_signed ::= .
|
||||
opt_signed ::= SIGNED.
|
||||
|
||||
opt_int ::= .
|
||||
opt_int ::= INT.
|
||||
|
||||
%type opt_star { int }
|
||||
opt_star(rhs) ::= . { rhs = 0; }
|
||||
opt_star(rhs) ::= STAR. { rhs = 1; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user