function [varargout]=mpp(fn,varargin)
% Simple MATLAB C-like preprocessor
%
% See mpptest.m1 for simple example. Run with:
% mpp('mpptest',param)
%
% Misha Koshelev
% February 16th, 2009
% Montague Laboratory
% definitions and temporary folder - persistent for recursion
persistent defs;
persistent temp;
recursed=~isempty(temp);
if ~recursed
temp=tempname;
mkdir(temp);
end
% special functions
if strcmpi(fn,'defs')
% pre-specify definitions
temp=[];
defs=varargin{1};
return;
elseif strcmpi(fn,'clear')
% clear everything
temp=[];
defs=struct();
return;
end
% check for file existence
if ~exist([fn,'.m1'],'file')
fprintf('File %s does not exist.\n',[fn,'.m1']);
cleanup;
return;
end
% clear function cache
clear(fn);
fi=fopen([fn,'.m1'],'r');
fo=fopen([temp,filesep,fn,'.m'],'w');
% how far into loops are we?
loop=0;
% are we writing? 0 if we are, otherwise at what loop level we stopped
writing=0;
while 1
l=fgets(fi);
if ~ischar(l)
break;
end
lt=strtrim(l);
if isempty(lt)||lt(1)~='#'
if ~writing
recurse=regexpi(l,'mpp\(.*\)','match');
if ~isempty(recurse)
recurse=recurse{1};
if length(regexp(recurse,'\('))~=1||length(regexp(recurse,'\)'))~=1
fprintf('Statement not supported:\n%s\n',l);
cleanup;
return;
end
% pull out function name
fnr=regexprep(recurse,'mpp\(''(.*)''.*','$1','ignorecase');
defss=defs;
mpp(fnr);
defs=defss;
% modify line
l=regexprep(l,'mpp\(''(.*)'',(.*)\)','$1($2)','ignorecase');
end
fwrite(fo,l);
else
% preserve line no's
fprintf(fo,'\n');
end
else
[sm,sl]=regexp(lt(2:end),'[^\w]','match','split');
len=length(sl);
if strcmpi(sl{1},'define')
% Syntax:
% #define a
% or
% #define a b
% (currently no difference)
if ~writing
if length(sl)>2
defs.(sl{2})=sl{3};
else
defs.(sl{2})=[];
end
end
elseif strcmpi(sl{1},'undef')
% Syntax:
% #undef a
if ~writing
if isfield(defs,sl{2})
defs=rmfield(defs,sl{2});
end
end
elseif strcmpi(sl{1},'ifdef')
% Syntax:
% #ifdef a
% if a defined, do ...
% or
% #ifdef a||b
% if a or b defined, do ...
% or
% #ifdef a&&b
% if a and b both defined, do ...
loop=loop+1;
if len==2
if ~writing&&~isfield(defs,sl{2})
writing=loop;
end
elseif len==4
if sm{2}=='&'&&sm{3}=='&'
if ~writing&&(~isfield(defs,sl{2})||~isfield(defs,sl{4}))
writing=loop;
end
elseif sm{2}=='|'&&sm{3}=='|'
if ~writing&&(~isfield(defs,sl{2})&&~isfield(defs,sl{4}))
writing=loop;
end
else
fprintf('Unknown preprocessor directive:\n');
fwrite(1,l);
cleanup;
return;
end
end
elseif strcmpi(sl{1},'ifndef')
% Syntax:
% #ifndef a
% if a not defined, do ...
% or
% #ifndef a||b
% if a or b not defined, do ...
% or
% #ifndef a&&b
% if a and b both not defined, do ...
loop=loop+1;
if len==2
if ~writing&&isfield(defs,sl{2})
writing=loop;
end
elseif len==4
if sm{2}=='&'&&sm{3}=='&'
if ~writing&&(isfield(defs,sl{2})||isfield(defs,sl{4}))
writing=loop;
end
elseif sm{2}=='|'&&sm{3}=='|'
if ~writing&&(isfield(defs,sl{2})&&isfield(defs,sl{4}))
writing=loop;
end
else
fprintf('Unknown preprocessor directive:\n');
fwrite(1,l);
cleanup;
return;
end
end
elseif strcmpi(sl{1},'endif')
% Syntax:
% #endif
% Must close all #ifdef and #ifndef functions
loop=loop-1;
if writing>loop
writing=0;
end
elseif strcmpi(sl{1},'else')
% Syntax:
% #else
% must follow an #ifdef or #ifndef
if writing==loop
writing=0;
elseif ~writing
writing=loop;
end
else
fprintf('Unknown preprocessor directive:\n');
fwrite(1,l);
cleanup;
return;
end
fprintf(fo,'\n');
end
end
fclose(fo);
fclose(fi);
temps=temp;
cleanup;
if ~recursed
fprintf('m file path: %s\n',temps);
ps=path;addpath(temps);
varargout={feval(str2func(fn),varargin{:})};
path(ps);
rmdir(temps,'s');
end
function cleanup
% Clean up persistent variables before exiting
%
if ~recursed
defs=struct();
temp=[];
end
end
end