You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
12 KiB
418 lines
12 KiB
/***************************************************************************
|
|
* Copyright (C) 2007 Nicolas Hadacek <hadacek@kde.org> *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
#include "cdb_parser.h"
|
|
|
|
#include <ctype.h>
|
|
#include "common/global/pfile.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
const CDB::ScopeType::Data CDB::ScopeType::DATA[Nb_Types] = {
|
|
{ "G", I18N_NOOP("Global") },
|
|
{ "F", I18N_NOOP("File") },
|
|
{ "L", I18N_NOOP("Local") },
|
|
{ "S", I18N_NOOP("Structure") } // ??
|
|
};
|
|
|
|
const CDB::VarType::Data CDB::VarType::DATA[Nb_Types] = {
|
|
{ "DA", I18N_NOOP("Array") },
|
|
{ "DF", I18N_NOOP("Function") },
|
|
{ "DG", I18N_NOOP("Generic Pointer") },
|
|
{ "DC", I18N_NOOP("Code Pointer") },
|
|
{ "DX", I18N_NOOP("External RAM Pointer") },
|
|
{ "DD", I18N_NOOP("Internal RAM Pointer") },
|
|
{ "DP", I18N_NOOP("Paged Pointer") },
|
|
{ "DI", I18N_NOOP("Upper-128-byte Pointer") },
|
|
{ "SL", I18N_NOOP("Long") },
|
|
{ "SI", I18N_NOOP("Int") },
|
|
{ "SC", I18N_NOOP("Char") },
|
|
{ "SS", I18N_NOOP("Short") },
|
|
{ "SV", I18N_NOOP("Void") },
|
|
{ "SF", I18N_NOOP("Float") },
|
|
{ "ST", I18N_NOOP("Structure") },
|
|
{ "SX", I18N_NOOP("SBIT") },
|
|
{ "SB", I18N_NOOP("Bit Field") }
|
|
};
|
|
|
|
const CDB::Sign::Data CDB::Sign::DATA[Nb_Types] = {
|
|
{ "S", I18N_NOOP("Signed") },
|
|
{ "U", I18N_NOOP("Unsigned") }
|
|
};
|
|
|
|
const CDB::AddressSpaceType::Data CDB::AddressSpaceType::DATA[Nb_Types] = {
|
|
{ "A", I18N_NOOP("External Stack") },
|
|
{ "B", I18N_NOOP("Internal Stack") },
|
|
{ "C", I18N_NOOP("Code") },
|
|
{ "D", I18N_NOOP("Code / Static Segment") },
|
|
{ "E", I18N_NOOP("Lower-128-byte Internal RAM") },
|
|
{ "F", I18N_NOOP("External RAM") },
|
|
{ "G", I18N_NOOP("Internal RAM") },
|
|
{ "H", I18N_NOOP("Bit Addressable") },
|
|
{ "I", I18N_NOOP("SFR Space") },
|
|
{ "J", I18N_NOOP("SBIT Space") },
|
|
{ "R", I18N_NOOP("Register Space") },
|
|
{ "Z", I18N_NOOP("Function or Undefined Space") },
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
CDB::Object::Object(const PURL::Url &url, Log::Base &log)
|
|
: _log(log)
|
|
{
|
|
PURL::File file(url, log);
|
|
if ( !file.openForRead() ) return;
|
|
TQStringList lines = file.readLines();
|
|
|
|
for (_line=0; _line<uint(lines.count()); _line++) {
|
|
_col = 0;
|
|
_current = lines[_line];
|
|
if ( _current.isEmpty() ) continue;
|
|
char c;
|
|
if ( !readChar(c) ) return;
|
|
if ( !readAndCheckChar(':') ) return;
|
|
Record *record = 0;
|
|
bool ok;
|
|
switch (c) {
|
|
case 'M': ok = parseModuleRecord(record); break;
|
|
case 'F': ok = parseFunctionRecord(record); break;
|
|
case 'S': ok = parseSymbolRecord(record); break;
|
|
case 'T': ok = parseTypeRecord(record); break;
|
|
case 'L': ok = parseLinkerRecord(record); break;
|
|
default:
|
|
log.log(Log::LineType::Error, i18n("Unrecognized record"));
|
|
return;
|
|
}
|
|
if (!ok) {
|
|
delete record;
|
|
return;
|
|
}
|
|
_records.append(record);
|
|
}
|
|
}
|
|
|
|
CDB::Object::~Object()
|
|
{
|
|
for (uint i=0; i<uint(_records.count()); i++) delete _records[i];
|
|
}
|
|
|
|
void CDB::Object::log(Log::LineType type, const TQString &message)
|
|
{
|
|
_log.log(type, message + " " + i18n("at line #%1, column #%2").tqarg(_line+1).tqarg(_col+1));
|
|
}
|
|
|
|
void CDB::Object::logMalformed(const TQString &detail)
|
|
{
|
|
log(Log::LineType::Error, i18n("Malformed record: ") + detail);
|
|
}
|
|
|
|
bool CDB::Object::readChar(char &c)
|
|
{
|
|
if ( _col==uint(_current.length()) ) {
|
|
logMalformed(i18n("unexpected end of line"));
|
|
return false;
|
|
}
|
|
c = _current[_col].latin1();
|
|
_col++;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::readAndCheckChar(char c)
|
|
{
|
|
char r;
|
|
if ( !readChar(r) ) return false;
|
|
if ( r!=c ) {
|
|
logMalformed(i18n("was expecting '%1'").tqarg(c));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::getString(const TQString &s, TQString &r)
|
|
{
|
|
r = s;
|
|
if ( r.isEmpty() ) {
|
|
logMalformed(i18n("empty name"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::readFixedLengthString(TQString &s, uint size)
|
|
{
|
|
s = TQString();
|
|
for (uint i=0; i<size; i++) {
|
|
char c;
|
|
if ( !readChar(c) ) return false;
|
|
s += c;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::readStoppedString(TQString &name, char stop)
|
|
{
|
|
TQString s;
|
|
for (;;) {
|
|
if ( _col==uint(_current.length()) ) {
|
|
if ( stop!=0 ) {
|
|
logMalformed(i18n("unexpected end of line"));
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
if ( _current[_col]==stop ) break;
|
|
s += _current[_col];
|
|
_col++;
|
|
}
|
|
return getString(s, name);
|
|
}
|
|
|
|
bool CDB::Object::getUInt(const TQString &s, uint &v)
|
|
{
|
|
bool ok;
|
|
v = s.toUInt(&ok);
|
|
if ( !ok ) logMalformed(i18n("was expecting an uint"));
|
|
return ok;
|
|
}
|
|
|
|
bool CDB::Object::readUInt(uint &v)
|
|
{
|
|
TQString s;
|
|
for (;;) {
|
|
if ( _col==uint(_current.length()) ) break;
|
|
if ( !isdigit(_current[_col].latin1()) ) break;
|
|
s += _current[_col];
|
|
_col++;
|
|
}
|
|
return getUInt(s, v);
|
|
}
|
|
|
|
bool CDB::Object::readBool(bool &b)
|
|
{
|
|
char c;
|
|
if ( !readChar(c) ) return false;
|
|
if ( c=='0' ) b = false;
|
|
else if ( c=='1' ) b = true;
|
|
else {
|
|
logMalformed(i18n("was expecting a bool ('%1')").tqarg(c));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::readHex(uint &v)
|
|
{
|
|
TQString s;
|
|
for (;;) {
|
|
if ( _col==uint(_current.length()) ) break;
|
|
if ( !isxdigit(_current[_col].latin1()) ) break;
|
|
s += _current[_col];
|
|
_col++;
|
|
}
|
|
return getUInt(s, v);
|
|
}
|
|
|
|
bool CDB::Object::parseModuleRecord(Record * &record)
|
|
{
|
|
ModuleRecord *mr = new ModuleRecord;
|
|
record = mr;
|
|
return readStoppedString(mr->filename, 0);
|
|
}
|
|
|
|
bool CDB::Object::parse(Scope &scope, TQString &name)
|
|
{
|
|
TQString s;
|
|
if ( !readFixedLengthString(s, 1) ) return false;
|
|
scope.type = ScopeType::fromKey(s);
|
|
switch (scope.type.type()) {
|
|
case ScopeType::Structure:
|
|
case ScopeType::Global: break;
|
|
case ScopeType::File:
|
|
case ScopeType::Local:
|
|
if ( !readStoppedString(scope.name, '$') ) return false;
|
|
break;
|
|
case ScopeType::Nb_Types:
|
|
logMalformed(i18n("unknown ScopeType"));
|
|
return false;
|
|
}
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readStoppedString(name, '$') ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(scope.level) ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(scope.block) ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parse(TypeChain &typeChain)
|
|
{
|
|
uint nb;
|
|
if ( !readAndCheckChar('{') ) return false;
|
|
if ( !readUInt(nb) ) return false;
|
|
if ( !readAndCheckChar('}') ) return false;
|
|
TQString s;
|
|
if ( !readStoppedString(s, ':') ) return false;
|
|
TQStringList list = TQStringList::split(',', s, true);
|
|
for (uint i=0; i<uint(list.count()); i++) {
|
|
DCLType type;
|
|
TQString key = list[i].mid(0, 2);
|
|
type.type = VarType::fromKey(key);
|
|
switch (type.type.type()) {
|
|
case VarType::Array:
|
|
case VarType::BitField:
|
|
if ( !getUInt(list[i].mid(2), type.nb) ) return false;
|
|
break;
|
|
case VarType::Structure:
|
|
if ( !getString(list[i].mid(2), type.name) ) return false;
|
|
break;
|
|
case VarType::Nb_Types:
|
|
logMalformed(i18n("unknown DCLType"));
|
|
return false;
|
|
default: break;
|
|
}
|
|
typeChain.types.append(type);
|
|
}
|
|
if ( !readAndCheckChar(':') ) return false;
|
|
if ( !readFixedLengthString(s, 1) ) return false;
|
|
typeChain.sign = Sign::fromKey(s);
|
|
if ( typeChain.sign==Sign::Nb_Types ) {
|
|
logMalformed(i18n("unknown Sign"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parse(AddressSpace &as)
|
|
{
|
|
TQString s;
|
|
if ( !readFixedLengthString(s, 1) ) return false;
|
|
as.type = AddressSpaceType::fromKey(s);
|
|
if ( as.type==AddressSpaceType::Nb_Types ) {
|
|
logMalformed(i18n("unknown AddressSpaceType"));
|
|
return false;
|
|
}
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readBool(as.onStack) ) return false;
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readUInt(as.stackOffset) ) return false;
|
|
if ( as.type==AddressSpaceType::Register ) {
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readAndCheckChar('[') ) return false;
|
|
if ( !readStoppedString(s, ']') ) return false;
|
|
as.registers = TQStringList::split(',', s, true);
|
|
if ( as.registers.count()==0 ) {
|
|
logMalformed(i18n("no register defined"));
|
|
return false;
|
|
}
|
|
if ( !readAndCheckChar(']') ) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parse(SymbolRecord &sr)
|
|
{
|
|
if ( !parse(sr.scope, sr.name) ) return false;
|
|
if ( !readAndCheckChar('(') ) return false;
|
|
if ( !parse(sr.typeChain) ) return false;
|
|
if ( !readAndCheckChar(')') ) return false;
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !parse(sr.addressSpace) ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parse(TypeMember &tm)
|
|
{
|
|
if ( !readAndCheckChar('(') ) return false;
|
|
if ( !readAndCheckChar('{') ) return false;
|
|
if ( !readUInt(tm.offset) ) return false;
|
|
if ( !readAndCheckChar('}') ) return false;
|
|
if ( !readAndCheckChar('S') ) return false;
|
|
if ( !readAndCheckChar(':') ) return false;
|
|
if ( !parse(tm.symbol) ) return false;
|
|
if ( !readAndCheckChar(')') ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parseSymbolRecord(Record * &record)
|
|
{
|
|
SymbolRecord *sr = new SymbolRecord;
|
|
record = sr;
|
|
if ( !parse(*sr) ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parseFunctionRecord(Record * &record)
|
|
{
|
|
FunctionRecord *fr = new FunctionRecord;
|
|
record = fr;
|
|
if ( !parse(*fr) ) return false;
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readBool(fr->isInterruptHandler) ) return false;
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readUInt(fr->interruptHandler) ) return false;
|
|
if ( !readAndCheckChar(',') ) return false;
|
|
if ( !readUInt(fr->registerBank) ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parseTypeRecord(Record * &record)
|
|
{
|
|
TypeRecord *tr = new TypeRecord;
|
|
record = tr;
|
|
if ( !readAndCheckChar('F') ) return false;
|
|
if ( !readStoppedString(tr->filename, '$') ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readStoppedString(tr->name, '[') ) return false;
|
|
if ( !readAndCheckChar('[') ) return false;
|
|
for (;;) {
|
|
TypeMember tm;
|
|
if ( !parse(tm) ) return false;
|
|
tr->members.append(tm);
|
|
if ( _current[_col]==']' ) break;
|
|
}
|
|
if ( !readAndCheckChar(']') ) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CDB::Object::parseLinkerRecord(Record * &record)
|
|
{
|
|
LinkerRecord *lr = new LinkerRecord;
|
|
record = lr;
|
|
char c;
|
|
if ( !readChar(c) ) return false;
|
|
switch (c) {
|
|
case 'A':
|
|
lr->type = LinkerRecord::AsmLine;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readStoppedString(lr->filename, '$') ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(lr->line) ) return false;
|
|
break;
|
|
case 'C':
|
|
lr->type = LinkerRecord::CLine;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readStoppedString(lr->filename, '$') ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(lr->line) ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(lr->level) ) return false;
|
|
if ( !readAndCheckChar('$') ) return false;
|
|
if ( !readUInt(lr->block) ) return false;
|
|
break;
|
|
case 'X':
|
|
lr->type = LinkerRecord::EndAddress;
|
|
if ( !parse(lr->scope, lr->name) ) return false;
|
|
break;
|
|
default:
|
|
lr->type = LinkerRecord::Address;
|
|
if ( !parse(lr->scope, lr->name) ) return false;
|
|
break;
|
|
}
|
|
if ( !readAndCheckChar(':') ) return false;
|
|
if ( !readHex(lr->address) ) return false;
|
|
return true;
|
|
}
|