diff --git a/src/shared/xdynrec.pas b/src/shared/xdynrec.pas
index fe9c5616f346c30fe79c9ba24b19193763a9e9c2..3bce723917354b4ad0e119cf92edcba521f42e2d 100644 (file)
--- a/src/shared/xdynrec.pas
+++ b/src/shared/xdynrec.pas
// TSize: pair of UShorts
// TList: actually, array of records
// TTrigData: array of mMaxDim bytes, but internally a record (mRecRef)
- // arrays of chars are pascal shortstrings (with counter in the first byte)
+ // in binary: arrays of chars are pascal shortstrings (with counter in the first byte)
private
type
TEBS = (TNone, TRec, TEnum, TBitSet);
private
- mOwner: TDynRecord;
- mPasName: AnsiString;
- mName: AnsiString;
- mType: TType;
+ mOwner: TDynRecord; // owner record
+ mName: AnsiString; // field name
+ mType: TType; // field type
mIVal: Integer; // for all integer types
mIVal2: Integer; // for point and size
mSVal: AnsiString; // string; for byte and char arrays
mAsT: Boolean; // for points and sizes, use separate fields, names starts with `t`
mDefined: Boolean;
mHasDefault: Boolean;
- mOmitDef: Boolean;
+ mWriteDef: Boolean;
mInternal: Boolean;
mNegBool: Boolean;
mBitSetUnique: Boolean; // bitset can contain only one value
mTagInt: Integer;
mTagPtr: Pointer;
+ // for pasgen
+ mAlias: AnsiString;
+
private
procedure cleanup ();
class function getTypeName (t: TType): AnsiString;
+ // build "alias name" for pascal code
+ function palias (firstUp: Boolean=false): AnsiString;
+
function definition (): AnsiString;
- function pasdef (): AnsiString;
function clone (newOwner: TDynRecord=nil; registerIn: TDynRecord=nil): TDynField;
function GetEnumerator (): TDynRecList.TEnumerator; inline;
public
- property pasname: AnsiString read mPasName;
property name: AnsiString read mName;
property baseType: TType read mType;
property negbool: Boolean read mNegBool;
private
mOwner: TDynMapDef;
mId: AnsiString;
- mPasName: AnsiString;
mName: AnsiString;
mSize: Integer;
mFields: TDynFieldList;
destructor Destroy (); override;
function definition (): AnsiString;
- function pasdef (): AnsiString;
function clone (registerIn: TDynRecord): TDynRecord;
// number of records of the given instance
function instanceCount (const typename: AnsiString): Integer;
- //procedure setUserField (const fldname: AnsiString; v: LongInt);
- //procedure setUserField (const fldname: AnsiString; v: AnsiString);
- //procedure setUserField (const fldname: AnsiString; v: Boolean);
-
function getUserVar (const aname: AnsiString): Variant;
procedure setUserVar (const aname: AnsiString; val: Variant);
public
property id: AnsiString read mId; // for map parser
- property pasname: AnsiString read mPasName;
property name: AnsiString read mName; // record name
property size: Integer read mSize; // size in bytes
//property fields: TDynFieldList read mFields;
function findTrigFor (const aname: AnsiString): TDynRecord;
function findEBSType (const aname: AnsiString): TDynEBS;
- function pasdef (): AnsiString;
function pasdefconst (): AnsiString;
// creates new header record
mAsT := false;
mHasDefault := false;
mDefined := false;
- mOmitDef := false;
+ mWriteDef := false;
mInternal := true;
mDefUnparsed := '';
mDefSVal := '';
mRecRefId := '';
mTagInt := 0;
mTagPtr := nil;
+ mAlias := '';
end;
result := TDynField.Create(mName, mType);
result.mOwner := mOwner;
if (newOwner <> nil) then result.mOwner := newOwner else result.mOwner := mOwner;
- result.mPasName := mPasName;
result.mName := mName;
result.mType := mType;
result.mIVal := mIVal;
result.mAsT := mAsT;
result.mDefined := mDefined;
result.mHasDefault := mHasDefault;
- result.mOmitDef := mOmitDef;
+ result.mWriteDef := mWriteDef;
result.mInternal := mInternal;
result.mNegBool := mNegBool;
result.mBitSetUnique := mBitSetUnique;
result.mRecRefId := mRecRefId;
result.mTagInt := mTagInt;
result.mTagPtr := mTagPtr;
+ result.mAlias := mAlias;
+end;
+
+
+function TDynField.palias (firstUp: Boolean=false): AnsiString;
+var
+ nextUp: Boolean;
+ ch: AnsiChar;
+begin
+ if (Length(mAlias) > 0) then
+ begin
+ if firstUp then result := UpCase1251(mAlias[1])+Copy(mAlias, 2, Length(mAlias)-1) else result := mAlias;
+ end
+ else
+ begin
+ result := '';
+ nextUp := firstUp;
+ for ch in mName do
+ begin
+ if (ch = '_') then begin nextUp := true; continue; end;
+ if nextUp then result += UpCase1251(ch) else result += ch;
+ nextUp := false;
+ end;
+ end;
end;
function TDynField.definition (): AnsiString;
begin
- result := mPasName+' is '+quoteStr(mName)+' type ';
+ result := quoteStr(mName)+' type ';
result += getTypeName(mType);
+ if (Length(mAlias) > 0) then result += ' alias '+mAlias;
if (mMaxDim >= 0) then result += Format('[%d]', [mMaxDim]);
if (mBinOfs >= 0) then result += Format(' offset %d', [mBinOfs]);
case mEBS of
if (mType = TType.TPoint) then begin if (mAsT) then result += ' as txy' else result += ' as xy'; end
else if (mType = TType.TSize) then begin if (mAsT) then result += ' as twh' else result += ' as wh'; end;
end;
- if mOmitDef then result += ' omitdefault';
+ if mWriteDef then result += ' writedefault';
if mInternal then result += ' internal';
end;
-function TDynField.pasdef (): AnsiString;
-begin
- result := mPasName+': ';
- case mType of
- TType.TBool: result += 'Boolean;';
- TType.TChar: if (mMaxDim > 0) then result += formatstrf('Char%d;', [mMaxDim]) else result += 'Char;';
- TType.TByte: result += 'ShortInt;';
- TType.TUByte: result += 'Byte;';
- TType.TShort: result += 'SmallInt;';
- TType.TUShort: result += 'Word;';
- TType.TInt: result += 'LongInt;';
- TType.TUInt: result += 'LongWord;';
- TType.TString: result += 'AnsiString;';
- TType.TPoint:
- if mAsT then result := 'tX, tY: Integer;'
- else if mSepPosSize then result := 'X, Y: Integer;'
- else result += 'TDFPoint;';
- TType.TSize:
- if mAsT then result := 'tWidth, tHeight: Word;'
- else if mSepPosSize then result := 'Width, Height: Word;'
- else result += 'TSize;';
- TType.TList: assert(false);
- TType.TTrigData: result += formatstrf('Byte%d;', [mMaxDim]);
- else raise Exception.Create('ketmar forgot to handle some field type');
- end;
-end;
-
-
procedure TDynField.parseDef (pr: TTextParser);
var
fldname: AnsiString;
fldtype: AnsiString;
fldofs: Integer;
fldrecname: AnsiString;
- fldpasname: AnsiString;
asxy, aswh, ast: Boolean;
ainternal: Boolean;
- omitdef: Boolean;
+ writedef: Boolean;
defstr: AnsiString;
defint, defint2: Integer;
hasdefStr: Boolean;
unique: Boolean;
asmonid: Boolean;
defech: AnsiChar;
+ xalias: AnsiString;
begin
- fldpasname := '';
fldname := '';
fldtype := '';
fldofs := -1;
aswh := false;
ast := false;
ainternal := false;
- omitdef := false;
+ writedef := false;
defstr := '';
defint := 0;
defint2 := 0;
asmonid := false;
lmaxdim := -1;
lebs := TDynField.TEBS.TNone;
+ xalias := '';
- fldpasname := pr.expectId(); // pascal field name
// field name
- pr.expectId('is');
- fldname := pr.expectStr();
- // field type
- pr.expectId('type');
- fldtype := pr.expectId();
-
- // fixed-size array?
- if pr.eatDelim('[') then
- begin
- lmaxdim := pr.expectInt();
- if (lmaxdim < 1) then raise Exception.Create(Format('invalid field ''%s'' array size', [fldname]));
- pr.expectDelim(']');
- end;
+ fldname := pr.expectStrOrId();
while (pr.tokType <> pr.TTSemi) do
begin
+ if pr.eatId('type') then
+ begin
+ if (Length(fldtype) > 0) then raise Exception.Create(Format('duplicate type definition for field ''%s''', [fldname]));
+ // field type
+ fldtype := pr.expectId();
+ // fixed-size array?
+ if pr.eatDelim('[') then
+ begin
+ lmaxdim := pr.expectInt();
+ // arbitrary limits
+ if (lmaxdim < 1) or (lmaxdim > 32768) then raise Exception.Create(Format('invalid field ''%s'' array size', [fldname]));
+ pr.expectDelim(']');
+ end;
+ continue;
+ end;
+
+ if pr.eatId('alias') then
+ begin
+ if (Length(xalias) > 0) then raise Exception.Create(Format('duplicate alias definition for field ''%s''', [fldname]));
+ xalias := pr.expectId();
+ continue;
+ end;
+
if pr.eatId('offset') then
begin
if (fldofs >= 0) then raise Exception.Create(Format('duplicate field ''%s'' offset', [fldname]));
continue;
end;
- if pr.eatId('omitdefault') then
+ if pr.eatId('writedefault') then
begin
- omitdef := true;
+ writedef := true;
continue;
end;
continue;
end;
+ // record type, no special modifiers
if (pr.tokType <> pr.TTId) then raise Exception.Create(Format('field ''%s'' has something unexpected in definition', [fldname]));
if (Length(fldrecname) <> 0) then raise Exception.Create(Format('field ''%s'' already typed as ''%s''', [fldname, fldrecname]));
else if (fldtype = 'point') then mType := TType.TPoint
else if (fldtype = 'size') then mType := TType.TSize
else if (fldtype = 'trigdata') then mType := TType.TTrigData
- else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
+ else
+ begin
+ // record types defaults to int
+ if (Length(fldrecname) > 0) then
+ begin
+ mType := TType.TInt;
+ end
+ else
+ begin
+ if (Length(fldtype) = 0) then raise Exception.Create(Format('field ''%s'' has no type', [fldname]))
+ else raise Exception.Create(Format('field ''%s'' has invalid type ''%s''', [fldname, fldtype]));
+ end;
+ end;
+ // check for valid arrays
if (lmaxdim > 0) and (mType <> TType.TChar) and (mType <> TType.TTrigData) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname, fldtype]));
+
+ // check for valid trigdata or record type
if (mType = TType.TTrigData) then
begin
- if (lmaxdim < 1) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be array', [fldname, fldtype]));
- if (Length(fldrecname) > 0) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname, fldtype]));
+ // trigdata
+ if (lmaxdim < 1) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot be non-array', [fldname, 'trigdata']));
+ if (Length(fldrecname) > 0) then raise Exception.Create(Format('field ''%s'' of type ''%s'' cannot have another type', [fldname, 'trigdata']));
lebs := TDynField.TEBS.TRec;
+ end
+ else if (Length(fldrecname) > 0) then
+ begin
+ // record
+ if not (mType in [TType.TByte, TType.TUByte, TType.TShort, TType.TUShort, TType.TInt, TType.TUInt]) then
+ begin
+ raise Exception.Create(Format('field ''%s'' of record type ''%s'' cannot have type ''%s''', [fldname, fldrecname, fldtype]));
+ end;
end;
+ // setup default value
if hasdefStr then self.mDefUnparsed := quoteStr(defstr)
else if hasdefId then self.mDefUnparsed := defstr
else if hasdefInt then
end;
self.mHasDefault := (hasdefStr or hasdefId or hasdefInt);
- self.mPasName := fldpasname;
self.mEBS := lebs;
self.mEBSTypeName := fldrecname;
self.mBitSetUnique := unique;
self.mBinOfs := fldofs;
self.mSepPosSize := (asxy or aswh);
self.mAsT := ast;
- self.mOmitDef := omitdef;
+ self.mWriteDef := writedef;
self.mInternal := ainternal;
+ self.mAlias := xalias;
end;
result := TDynRecord.Create();
result.mOwner := mOwner;
result.mId := mId;
- result.mPasName := mPasName;
result.mName := mName;
result.mSize := mSize;
result.mHeader := mHeader;
end
else
begin
- mPasName := pr.expectId(); // pascal record name
- pr.expectId('is');
- mName := pr.expectStr();
+ mName := pr.expectStrOrId();
while (pr.tokType <> pr.TTBegin) do
begin
if pr.eatId('header') then begin mHeader := true; continue; end;
end;
-function TDynRecord.pasdef (): AnsiString;
-var
- fld: TDynField;
-begin
- if isTrigData then
- begin
- assert(false);
- result := '';
- end
- else
- begin
- // record
- result := ' '+mPasName+' = packed record'#10;
- end;
- for fld in mFields do
- begin
- if fld.mInternal then continue;
- if (fld.mBinOfs < 0) then continue;
- result += ' '+fld.pasdef+#10;
- end;
- result += ' end;'#10;
-end;
-
-
function TDynRecord.definition (): AnsiString;
var
f: Integer;
else
begin
// record
- result := mPasName+' is '+quoteStr(mName);
+ result := quoteStr(mName);
if (mSize >= 0) then result += Format(' size %d bytes', [mSize]);
if mHeader then result += ' header';
end;
continue;
end;
if fld.mInternal then continue;
- if fld.mOmitDef and fld.isDefaultValue then continue;
+ if (not fld.mWriteDef) and fld.isDefaultValue then continue;
wr.putIndent();
fld.writeTo(wr);
end;
while true do
begin
if not pr.skipBlanks() then break;
- if (pr.tokType <> pr.TTId) then raise Exception.Create('identifier expected');
- if (pr.tokStr = 'enum') or (pr.tokStr = 'bitset') then
+ if (pr.tokType = pr.TTId) then
begin
- eb := TDynEBS.Create(pr);
- if (findEBSType(eb.name) <> nil) then
+ // enum or bitset
+ if (pr.tokStr = 'enum') or (pr.tokStr = 'bitset') then
begin
- eb.Free();
- raise Exception.Create(Format('duplicate enum/bitset ''%s''', [eb.name]));
+ eb := TDynEBS.Create(pr);
+ if (findEBSType(eb.name) <> nil) then
+ begin
+ eb.Free();
+ raise Exception.Create(Format('duplicate enum/bitset ''%s''', [eb.name]));
+ end;
+ eb.mOwner := self;
+ ebsTypes.append(eb);
+ //writeln(eb.definition); writeln;
+ continue;
end;
- eb.mOwner := self;
- ebsTypes.append(eb);
- //writeln(eb.definition); writeln;
- continue;
- end;
- if (pr.tokStr = 'TriggerData') then
- begin
- rec := TDynRecord.Create(pr);
- for f := 0 to High(rec.mTrigTypes) do
+ // triggerdata
+ if (pr.tokStr = 'TriggerData') then
begin
- if (findTrigFor(rec.mTrigTypes[f]) <> nil) then
+ rec := TDynRecord.Create(pr);
+ for f := 0 to High(rec.mTrigTypes) do
begin
- rec.Free();
- raise Exception.Create(Format('duplicate trigdata ''%s''', [rec.mTrigTypes[f]]));
+ if (findTrigFor(rec.mTrigTypes[f]) <> nil) then
+ begin
+ rec.Free();
+ raise Exception.Create(Format('duplicate trigdata ''%s''', [rec.mTrigTypes[f]]));
+ end;
end;
+ rec.mOwner := self;
+ trigTypes.append(rec);
+ //writeln(dr.definition); writeln;
+ continue;
end;
- rec.mOwner := self;
- trigTypes.append(rec);
- //writeln(dr.definition); writeln;
- continue;
end;
rec := TDynRecord.Create(pr);
function TDynMapDef.parseMap (pr: TTextParser): TDynRecord;
var
res: TDynRecord = nil;
- //fo: TextFile;
begin
result := nil;
try
res := nil;
finally
res.Free();
- {
- except on e: Exception do
- begin
- //TMP:segfaults!
- AssignFile(fo, 'z.log');
- Rewrite(fo);
- DumpExceptionBackTrace(fo);
- CloseFile(fo);
- res.Free();
- end;
- }
end;
end;
end;
-function TDynMapDef.pasdef (): AnsiString;
-var
- ebs: TDynEBS;
- rec: TDynRecord;
- fld: TDynField;
- needComma: Boolean;
- tn: AnsiString;
-begin
- result := '';
- result += '// ////////////////////////////////////////////////////////////////////////// //'#10;
- result += '// enums and bitsets'#10;
- for ebs in ebsTypes do result += #10+ebs.pasdef();
- result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
- result += '// records'#10'type'#10;
- for rec in recTypes do
- begin
- if (rec.mSize < 1) then continue;
- result += rec.pasdef();
- result += #10;
- end;
- result += #10#10'// ////////////////////////////////////////////////////////////////////////// //'#10;
- result += '// triggerdata'#10'type'#10;
- result += ' TTriggerData = record'#10;
- result += ' case Byte of'#10;
- result += ' 0: (Default: Byte128);'#10;
- for rec in trigTypes do
- begin
- result += ' ';
- needComma := false;
- for tn in rec.mTrigTypes do
- begin
- if needComma then result += ', ' else needComma := true;
- result += tn;
- end;
- result += ': ('#10;
- for fld in rec.mFields do
- begin
- if fld.mInternal then continue;
- if (fld.mBinOfs < 0) then continue;
- result += ' '+fld.pasdef+#10;
- end;
- result += ' );'#10;
- end;
- result += ' end;'#10;
-end;
-
-
function TDynMapDef.pasdefconst (): AnsiString;
var
ebs: TDynEBS;