function yat(yfile,tfile,mfile,mode,path)

%YAT   Yet Another Tree (Experimental MDS tree compiler)
%  YAT(YFILE[,TFILE,MFILE,MODE,PATH]) will create the model of a tree whose
%  structure is described in YFILE.yat, creating the TFILE.tcl file to build the
%  tree and the MFILE.m MatLab script to fill the tree. MODE instructs whether
%  these files must be e'x'ecuted (default) and 'd'ebuged. Use PATH to specify
%  the tree path.
%
%SYNTAX: (type yat to see an example)
%/* */     comment
%.name tag usage content     Node creation
%:name tag usage content     Leaf creation
%! command                   TCL command
%% command                   MatLab command
%#def NAME                   Macro definition
%#end
%#use NAME [arg0 arg1 ...]   Macro call
%(1) the identation specifies whether the node is son, brother or uncle of
%    the previous node. See ex.
%(2) tag, usage and content are optional, - if absent.
%(3) content is either a TDI expression or a MatLab expression braketed by %
%    and followed by a conversion specification (see mdsput).
%(4) in macro definition, $0..$9 or $a..$z represents the calling parameters.
%(5) > in first position marks a continuation line.

% example
if nargin == 0, type yat.yat ,return, end

% defaults and fuq
if nargin < 5, path = getenv([yfile,'_path']); end
if nargin < 4, mode = 'x'; end
if nargin < 3, mfile = [yfile '_yat']; end
if nargin < 2, tfile = [yfile '_yat']; end
tree = yfile; yfile = [yfile '.yat']; tfile  = [tfile '.tcl']; mfile = [mfile '.m'];
bld = any(mode == 'x'); dbg = any(mode == 'd');
LF = newline; HT = char(9);

yfid = fopen(yfile,'r'); if yfid < 0, error('YAT file cannot be open.'), end
txt = [LF fscanf(yfid,'%c',Inf) LF]; fclose(yfid);
tfid = fopen(tfile,'w'); if tfid < 0, error('TCL file cannot be created.'), end
mfid = fopen(mfile,'w'); if mfid < 0, error('M file cannot be created.'), end

fprintf(tfid,'! YAT compiler\n! source: %s\n! date  : %s\n\n',yfile,datestr(now));
fprintf(mfid,'%% YAT compiler\n%% source: %s\n%% date  : %s\n\n',yfile,datestr(now));

% remove comments and continuation sign
k1 = regexp(txt,'/\*'); k2 = regexp(txt,'\*/');
if (length(k1) ~= length(k2)), error('Unbalanced /* */ comments.'), end
for k = length(k1):-1:1, txt(k1(k):k2(k)+1) = ''; end
k = regexp(txt,'\n>'); txt([k k+1]) = '';

% macro extraction
kLF = find(txt == LF);
km1 = regexp(txt,'\n#def'); km2 = regexp(txt,'\n#end');
if length(km1) ~= length(km2), error('Unbalanced #def  #end macro definitions.'), end
macro = {}; macroname = {};
for k = length(km1):-1:1
 [k1,k2] = regexp(txt(km1(k)+5:end),'\w+','once');
 macroname{k} = txt(km1(k)+4+(k1:k2));
 k1 = regexp(txt(km1(k)+1:end),'\n','once');
 macro{k} = txt(km1(k)+1+k1:km2(k));
 txt(km1(k):km2(k)+4) = '';
 if dbg, disp(['Macro definition    ' macroname{k}]), end
end

% line by line
pdepth = 0; cpath = ''; pnode = ''; % tree top
while ~isempty(txt)
 k = regexp(txt,'\n','once'); ln = deblank(txt(1:k(1)-1)); txt(1:k(1)) = '';
 tcmd = ''; mcmd = '';

 if isempty(ln)
 elseif ln(1) == '!' % TCL inline "! command"
  tcmd = [ln(3:end) LF];
  if dbg, disp(['TCL inline          ' tcmd(1:end-1)]), end
 elseif ln(1) == '%' % MatLab inline "% command"
  mcmd = [ln(3:end) LF];
  if dbg, disp(['MatLab inline       ' mcmd(1:end-1)]), end
 elseif ln(1) == '#' % macro call "#use MACRO p1 p2 ..."
  [k1,k2] = regexp(ln,'\S+');
  % macro name
  if length(k1) < 2, error('Macro call #use without name.'), end
  mname = ln(k1(2):k2(2));
  km = strmatch(mname,macroname,'exact');
  if isempty(km), error(['Undefined macro "' mname '".']), end
  % arguments
  arg = {}; for k = 3:length(k1), arg{k-2} = ln(k1(k):k2(k)); end
  ln = macro{km};
  k1 = regexp(ln,'\$[a-z0-9]'); % $capital may be a TDI constant
  k2 = abs(ln(k1+1)) + (1 - abs('a'));
  k = find(k2 <= 0); k2(k) = k2(k) + (abs('a') - abs('0'));
  if any(k2) > length(arg)
   error(['Undefined argument in calling macro "' mname '".'])
  end
  % argument replacement
  for k = length(k1):-1:1
   ln = [ln(1:k1(k)-1) arg{k2(k)} ln(k1(k)+2:end)];
  end
  txt = [ln txt]; % for recursive #use
  if dbg, disp(['Macro expansion     ' mname]), end
 else % node declaration
  [k1,k2] = regexp(ln,'\S+');
  % node name, tag, usage, expression, current path
  if isempty(k1), error(['Missing node name in line:' LF ln]),end
  node = ln(k1(1):k2(1)); cdepth = k1(1)-1;
  if length(k1) > 1, tag   = ln(k1(2):k2(2)); else, tag   = '-'; end
  if length(k1) > 2, usage = ln(k1(3):k2(3)); else, usage = '-'; end
  if length(k1) > 3, expr  = ln(k1(4):end);   else, expr  = '-'; end
  expr = strrep(expr,HT,'');
  if cdepth > pdepth
   cpath = pnode;
  elseif cdepth < pdepth
   k = regexp(cpath,'[\.:]');
   cpath = cpath(1:k(cdepth+1)-1);
  end
  node = [cpath node];
  pnode = node; pdepth = cdepth;
  if dbg, disp(['Node creation       ' node]), end
  % TCL command
  tcmd = ['ADD NODE ' node];
  if ~strcmp(usage,'-'), tcmd = [tcmd ' /USAGE=' usage]; end
  tcmd = [tcmd LF];
  if ~strcmp(tag,'-'), tcmd = [tcmd 'ADD TAG  ' node ' ' tag LF]; end
  % expression
  if strcmp(expr,'-')
  elseif expr(1) == '%'
   if expr(2) == '%' % self write node
    mcmd = ['mdsput(''' node ''',mdsvalue(''' node '''),''' expr(3) ''')' LF];
    tcmd = [tcmd 'PUT      ' node ' ' expr(5:end) LF];
   else
    mcmd = ['mdsput(''' node ''',mdscvt(' expr(2:end-2) ',''' expr(end) '''))' LF];
   end
  else
   expr = ['"' strrep(expr,'"','''') '"'];
   tcmd = [tcmd 'PUT      ' node ' ' expr LF];
  end

 end

 % write to command file
 if ~isempty(tcmd), fwrite(tfid,tcmd,'uchar'); end
 if ~isempty(mcmd), fwrite(mfid,mcmd,'uchar'); end

end

fclose(tfid); fclose(mfid);

if bld
 if ~isempty(path)
  if isempty(mdscurrent), mdsconnect; end
  mdsdata(['setenv("' tree '_path=' path '")']);
 end
 tfid = fopen(tfile,'r');
 while 1
  ln = fgetl(tfid);
  if ~ischar(ln), fclose(tfid); break, end
  if dbg, disp(ln), end
  if ~isempty(ln)
   stat = mdstcl(ln);
   if ~rem(stat,2)
    error('TCL command "%s" failed with status=%d',ln,stat)
   end
  end
 end
 if dbg, echo(mfile(1:end-2),'on'), end
 eval(mfile(1:end-2))
end
