📄 tffparse.m
字号:
% subloops MUST be zero now
if subloops ~= 0
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid LOOP nesting in TFF spec.' ...
);
end
% build special loop struct
lstruct = struct( ...
'loopvar', lvname, ...
'firstrule', slc, ...
'lastrule', elooprule, ...
'cond', rstruct.cond, ...
'dim', rstruct.dim);
% split dim from varname
ldim = 1;
fname = rstruct.varname;
[ldimmatcht{1:3}] = regexpi( ...
fname, '^[a-z][a-z_0-9]*\((\d+)\)$');
ldimmatcht = ldimmatcht{3};
if ~isempty(ldimmatcht)
ldim = str2double( ...
fname(ldimmatcht{1}(1, 1):ldimmatcht{1}(1, 1)));
fname = regexprep(fname, '\(.*\)', '');
end
% update known variables
if isfield(tffspec.Variables, fname)
tffspec.Variables.(fname) = max( ...
tffspec.Variables.(fname), ldim);
else
tffspec.Variables.(fname) = ldim;
end
% put loop struct into Loops
tffspec.Loops.(fname)(ldim) = lstruct;
% loops (end)
elseif strcmp(rstrtype, 'eloop')
% check if name matches last pushed name
if isempty(looplist) || ...
~strcmp(rstruct.varname, looplist{end})
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid LOOP (end) detected in rule %d.', ...
slc ...
);
end
% pop loop from looplist
looplist(end) = [];
% loops (exit)
elseif strcmp(rstrtype, 'xloop')
% check if name matches any current loops
if isempty(looplist) || ...
~any(strcmp(rstruct.varname, looplist))
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid XLOOP token: ''%s''.', ...
rstruct.varname ...
);
end
% don't do anything else !!! (see at bloop)
% skip N rules
elseif strcmp(rstrtype, 'skipn')
% dim MUST be a simple number
if isempty(regexp(rstruct.dim, '^\d+$', 'once'))
error( ...
'BVQXtools:BadTFFSpec', ...
'SKIPN requires a simple numeric dim.' ...
);
end
end
end
% put actrules at the end of ListOfFields
tffspec.ListOfFields(end + 1:end + length(actrules)) = actrules;
% list of magic tokens
case {'magic'}
% find "EndMagic" line
endline = llc + 1;
for slc = (lc + 2):llc
if ~isempty(regexpi(tfflines{slc}, '^endmagic'))
endline = slc - 1;
break;
end
end
blc = lc;
lc = endline + 1;
% invalid Magic
if lc > llc
error( ...
'BVQXtools:BadTFFSpec', ...
'Unclosed Magic block in specification.' ...
);
end
% list/table field separator
listsep = tffval;
% get rule headers
rhead = splittocell(tfflines{blc}, listsep, 0);
% build header struct
hstruct = struct;
for hfc = 1:length(rhead)
hfield = makelabel(rhead{hfc});
hstruct.(hfield) = hfc;
rhead{hfc} = hfield;
end
blc = blc + 1;
% bail out if invalid header given
if ~isfield(hstruct, 'name') || ...
~isfield(hstruct, 'range') || ...
~isfield(hstruct, 'type') || ...
~isfield(hstruct, 'magic')
error( ...
'BVQXtools:BadTFFSpec', ...
'Magic with bad headers.' ...
);
end
% get list of header field names (in their order)
hfields = fieldnames(hstruct);
nfields = length(hfields);
% build empty field list struct
fmagic = cell2struct(cell(0, 0, nfields), hfields, 3);
% check global fieldlist struct
tfields = fieldnames(tffspec.Magic);
% no rules yet -> OK
if isempty(tfields)
tffspec.Magic = fmagic;
% otherwise compare header fields
else
% assume no mismatch
headermismatch = false;
% check number of fields
if length(tfields) ~= length(hfields)
tfields = {};
headermismatch = true;
end
% only if still content in tfields, check names
for tfc = 1:length(tfields)
if ~strcmp(tfields{tfc}, hfields{tfc})
headermismatch = true;
break;
end
end
% if mismatch give warning and continue with next block
if headermismatch
warning( ...
'BVQXtools:BadTFFSpec', ...
'Magic blocks must match in their headers.' ...
);
continue;
end
end
% build list of magics to consider
actmagic = fmagic;
for slc = blc:endline
% split to fields
rcont = splittocell(tfflines{slc}, listsep, 0);
% reject too short arrays
if length(rcont) < length(rhead)
continue;
end
% deal into struct
rstruct = hstruct;
for hfc = 1:length(hfields)
hfield = hfields{hfc};
rstruct.(hfield) = deblank(rcont{hstruct.(hfield)});
end
% deal with empty lines
if isempty(rstruct.name) || ...
isempty(rstruct.range) || ...
isempty(rstruct.type) || ...
isempty(rstruct.magic)
continue;
end
% put non-empty type/varname rules into actrules
actmagic(end + 1) = rstruct;
end
% parse magics (using for; if any magic fails, error out!)
for slc = 1:length(actmagic)
% get rstruct from actrules
rstruct = actmagic(slc);
rstrtype = lower(rstruct.type);
% check syntax of fields, name
if ~strcmp(rstruct.name, makelabel(rstruct.name))
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid Magic.name token: ''%s''.', ...
rstruct.name ...
);
end
% ..., type
if ~any(strcmp(rstrtype, ...
{'hex', 'regexp', 'regexpi', 'strfind'}))
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid Magic.type token: ''%s''.', ...
rstruct.type ...
);
end
% ..., range
try
rrange = eval(['[' rstruct.range ']']);
if numel(rrange) ~= 2
error('INVALIDRANGE');
end
catch
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid Magic.range specification: ''%s''.', ...
rstruct.range ...
);
end
actmagic(slc).range = rrange;
% ..., magic -> parse hex codes
if strcmp(rstrtype, 'hex')
% split at comma, semicolon or spaces
rhexcodes = splittocell(rstruct.magic, ',; ', 1, 1);
% and convert
rhexvals = zeros(1, numel(rhexcodes));
for rhc = 1:numel(rhexcodes)
rhexvals(rhc) = hex2dec(rhexcodes{rhc});
end
% put back into array
actmagic(slc).magic = rhexvals(:)';
% allow hexadecimal content in other fields
else
% pack %C{xx} and 0x{xx} into hex vars
[hexpackt{1:3}] = regexpi(rstruct.magic, ...
'(\%c\{|0x\{)([0-9a-f][0-9a-f])(\})', 'once');
hexpackt = hexpackt{3};
while ~isempty(hexpackt)
rstruct.magic = strrep(rstruct.magic, ...
rstruct.magic(hexpackt(1, 1):hexpackt(3, 2)), ...
char(hex2dec( ...
rstruct.magic(hexpackt(2, 1):hexpackt(2, 2)))));
[hexpackt{1:3}] = regexpi(rstruct.magic, ...
'(\%c\{|0x\{)([0-9a-f][0-9a-f])(\})', 'once');
end
% put back into actmagic
actmagic(slc).magic = rstruct.magic;
end
end
% put actrules at the end of ListOfFields
tffspec.Magic(end + 1:end + length(actmagic)) = actmagic;
% new file code snippet
case {'newfilecode'}
% find "EndNewFileCode"
endline = llc + 1;
for slc = lc:llc
if ~isempty(regexpi(tfflines{slc}, '^endnewfilecode'))
endline = slc - 1;
break;
end
end
% check endline
if endline > llc
error( ...
'BVQXtools:BadTFFSpec', ...
'Unclosed NewFileCode section in TFF spec.' ...
);
end
% generate and check code snippet
codesnip = gluetostring(tfflines(lc:endline), char(10), 1);
tcodesnip = codesnip;
tcodesnip(tcodesnip == '@') = [];
try
eval(['if 1==0,' tcodesnip ';end']);
catch
error( ...
'BVQXtools:BadTFFSpec', ...
'Syntax error detected in AfterReadCode: ''%s''.', ...
lasterr ...
);
end
% store code
tffspec.NewFileCode = codesnip;
% paragraph arrays
case {'paragrapharrays'}
try
tffspec.ParagraphArrays = logical(eval(tffval));
catch
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid ParagraphArrays value given: ''%s''.', ...
tffval ...
);
end
% skip empty lines
case {'skipemptylines'}
try
tffspec.SkipEmptyLines = logical(eval(tffval));
catch
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid SkipEmptyLines value given: ''%s''.', ...
tffval ...
);
end
% unrecognized token, just go on
otherwise
end
end
% check longest field name
fll = 8; % minimum field name length in ANY case
for flc = 1:length(tffspec.ListOfFields)
% only for FIELD or FLIST
if any(strcmpi(tffspec.ListOfFields(flc).type, ...
{'field', 'flist'}))
fll = max(fll, length(tffspec.ListOfFields(flc).field));
end
end
tffspec.MaxFieldNameLength = fll;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -