function [this,assign] = parse(this,p,assign)

%**************************************************************************

this.fname = p.fname;
c = p.code;

s = sprintf( ...
   'parsing file(s) <a href="matlab: edit %s">%s</a>. ', ...
   strrep(this.fname,' & ',' '),this.fname);
errorParsing = ['Error ',s];
warningParsing = ['Warning ',s];

blkKeyword = {
   '!endogenous_variables', ...
   '!exogenous_variables', ...
   '!residuals', ...
   '!parameters', ...
   '!equations', ...
   '!log_variables', ...
};   

nblk = length(blkKeyword);

endOfBlk = '(?=$';
for i = 1 : nblk
   endOfBlk = [endOfBlk,'|',blkKeyword{i}];
end
endOfBlk = [endOfBlk,')'];

blk = cell(1,nblk);
for i = 1 : nblk
   match = regexp(c,['(?<=',blkKeyword{i},').*?',endOfBlk],'match');
   blk{i} = [match{:}];
   if isempty(blk{i})
      blk{i} = '';
   end
end

%**************************************************************************
% Names.
name = cell(1,4);
namelabel = cell(1,4);
value = cell(1,4);
nametype = cell(1,4);
for i = 1 : 4
   if ~isempty(blk{i})
      % name{i} = regexp(blk{i},'\<[a-zA-Z]\w*\>','match');
      [name{i},namelabel{i},value{i}] = theparser.parsenames(blk{i});
      nametype{i} = i*ones(1,length(name{i}));
   else
      name{i} = {};
      namelabel{i} = {};
      value{i} = [];
      nametype{i} = [];
   end
end
name = [name{:}];
namelabel = [namelabel{:}];
value = [value{:}];
nametype = [nametype{:}];

name0 = name;
[name,index] = unique(name);
if length(name) ~= length(name0)
   index = setdiff(1:length(name0),index);
   utils.error('bkwmodel', ...
      'This name is declared more than once: ''%s''.', ...
      name0{index});
end
name = name0;
nname = length(name);

%**************************************************************************
% Log variables.

[allbut,blk{6}] = strfun.findremove(blk{6},'!allbut');
islog = false(size(name));
if allbut
   islog(nametype <= 2) = ~islog(nametype <= 2);
end
loglist = regexp(blk{6},'\<[a-zA-Z]\w*\>','match');
loglist = unique(loglist);
valid = true(size(loglist));
for i = 1 : length(loglist)
   index = strcmp(loglist{i},name);
   if any(index) && nametype(index) <= 2
      islog(index) = ~islog(index);
   else
      valid(i) = false;
   end
end
if any(~valid)
   utils.error('bkwmodel', ...
      ['This name found in the log-variables section ', ...
      'is not an endgenous variable: ''%s''.'], ...
      loglist{~valid});
end

%**************************************************************************
% Equations.
blk{5} = regexprep(blk{5},'\s+','');
% Do not include the semi-colon; it will be added when re-arranging LHS and
% RHS.
tokens = regexp(blk{5},'(#\(\d+\))?([^#;][^;]+);','tokens');
neqtn = length(tokens);
eqtn = cell(1,neqtn);
eqtnlabel = cell(1,neqtn);
for i = 1 : length(tokens)
   eqtn{i} = tokens{i}{2};
   if ~isempty(tokens{i}{1})
      eqtnlabel{i} = tokens{i}{1};
   else
      eqtnlabel{i} = '';
   end
end

if neqtn ~= sum(nametype == 1)
   utils.error('bkwmodel', ...
      [errorParsing, ...
      'The number of equations (%g) does not match the number ', ...
      'of endogenous variables (%g).'], ...
      neqtn,sum(nametype == 1));
end

% Parse user equations.
eqtnEval = eqtn;
% Add residuals, detect and check, identities, and remove equal signs.
identity = nan(1,neqtn);
addresiduals_();

% Remove white spaces.
eqtnEval = regexprep(eqtnEval,'\s+','');
eqtnEvalIdent = cell(1,neqtn);
eqtnEvalRes = cell(1,neqtn);
% Convert names to codes.
occurpos = [];
cannotParse = {};
leadsNotAllowed = {};
lagsNotAllowed = {};
replace = @name2code_;
isidentity = ~isnan(identity);
respoint = nan(size(eqtn));
respoint(~isidentity) = find(nametype == 3);

for ieq = 1 : neqtn
   eqtnEval{ieq} = regexprep(eqtnEval{ieq}, ...
      '(\<[a-zA-Z]\w*\>)(\{[^\}]+\})?(?!\()', ...
      '${replace($0,$1,$2)}');
   if ~isidentity(ieq)
      occurpos = [occurpos;ieq,respoint(ieq),0];
      eqtnEvalRes{ieq} = eqtnEval{ieq};
      eqtnEval{ieq} = [eqtnEval{ieq},'-', ...
         sprintf('x(%g,t-0)',respoint(ieq))];
   else
      occurpos = [occurpos;ieq,identity(ieq),0];
      eqtnEvalIdent{ieq} = eqtnEval{ieq};
      eqtnEval{ieq} = [sprintf('x(%g,t-0)',identity(ieq)), ...
         '-(',eqtnEval{ieq},')'];
   end
end

   function c = name2code_(thisexpression,thisname,thislag)
      c = '';
      if isempty(thislag)
         thislag = 0;
      else
         thislag = -round(sscanf(thislag,'{%g}'));
      end
      thisid = find(strcmp(thisname,name));
      if isempty(thisid) || ~isnumericscalar(thislag)
         cannotParse{end+1} = thisexpression;
         return
      end
      if thislag < 0
         leadsNotAllowed{end+1} = thisexpression;
         return
      end
      if nametype(thisid) > 2 && thislag ~= 0
         lagsNotAllowed{end+1} = thisexpression;
         return
      end
      if thislag == 0
         c = ['x(',sprintf('%g',thisid),',t-0)'];
      else
         c = ['x(',sprintf('%g',thisid),',t-',sprintf('%g',thislag),')'];
      end
      occurpos = [occurpos;ieq,thisid,thislag];
   end

if ~isempty(cannotParse)
   utils.error('bkwmodel', ...
      'Cannot parse this expression: ''%s''.',cannotParse{:});
end

if ~isempty(leadsNotAllowed)
   utils.error('bkwmodel', ...
      'Leads not allowed in bkwmodels: ''%s'',',leadsNotAllowed{:});
end

if ~isempty(lagsNotAllowed)
   utils.error('bkwmodel', ...
      'Lags only allowed for endogenous variables in bkwmodels: ''%s'',', ...
      lagsNotAllowed{:});
end

maxlag = max(occurpos(:,3));
if maxlag == 0
   maxlag = 1;
end
occur = false(neqtn,nname,maxlag);
for i = 1 : size(occurpos,1)
   occur(occurpos(i,1),occurpos(i,2),1+occurpos(i,3)) = true;
end

this.name = name;
this.nametype = nametype;
this.namelabel = labelsback(p,namelabel,'%s');
this.log = islog;
this.eqtnUsr = eqtn;
this.eqtnEval = eqtnEval;
this.eqtnEvalRes = eqtnEvalRes;
this.eqtnEvalIdent = eqtnEvalIdent;
this.eqtnlabel = labelsback(p,eqtnlabel,'%s');
this.occur = occur;
this.identity = identity;

this.Assign = nan(1,nname);
ne = sum(nametype == 3);
this.stdcorr = nan(1,ne + ne*(ne-1)/2);
this.stdcorr(1,1:ne) = 1;
this.stdcorr(1,ne+1:end) = 0;

if isempty(assign)
   assign = struct();
end

for i = 1 : nname
   if isfield(assign,name{i})
      continue
   end
   thisvalue = [];
   try
      thisvalue = eval(value{i});
   catch
      thisvalue = NaN;
   end
   if ~isnumeric(thisvalue) || isempty(thisvalue)
      thisvalue = NaN;
   end
   if ~isnan(thisvalue)
      assign.(name{i}) = thisvalue;
   end
end

% Nested functions follow.

   %***********************************************************************
   % Nested function addresiduals_().
   function addresiduals_()
      multiple = false(1,neqtn);
      invalidName = false(1,neqtn);
      invalididentity = false(1,neqtn);
      resoccur = false(neqtn,nname);
      for iEq = 1 : neqtn
         % Position of the equal sign in the equation. This is needed to
         % determine if the residual is on the RHS or LHS, and assign it
         % the correct sign. If there is no equal sign in the equation, the
         % whole equation is considered a RHS, and the equal sign is
         % implicitly at the end (equalsignpos = Inf).
         equalsignpos = strfind(eqtnEval{iEq},'=');
         if isempty(equalsignpos)
            equalsignpos = Inf;
         else
            equalsignpos = equalsignpos(1);
         end
         [start,resname] = ...
            regexp(eqtnEval{iEq},'(?<=&\s*)[a-zA-Z]\w*','start','match');
         if length(resname) > 1
            % More than one residuals added to this equation.
            multiple(iEq) = true;
            continue
         end
         if ~isempty(start)
            if start > equalsignpos
               whereisres = 'rhs';
            else
               whereisres = 'lhs';
            end
            resname = resname{1};
            eqtnEval{iEq} = regexprep(eqtnEval{iEq},['&\s*',resname],'');
            % Reset `equalsignpos` because we removed the residual string
            % and everything has shifted in the equation text
            equalsignpos = strfind(eqtnEval{iEq},'=');
            if isempty(equalsignpos)
               equalsignpos = Inf;
            else
               equalsignpos = equalsignpos(1);
            end
         end
         if ~isinf(equalsignpos)
            % Convert LHS=RHS to LHS-(RHS).
            LHS = eqtnEval{iEq}(1:equalsignpos-1);
            RHS = eqtnEval{iEq}(equalsignpos+1:end);
         else
            LHS = eqtnEval{iEq}(1:equalsignpos-1);
            RHS = '0';
         end
         if isempty(LHS)
            LHS = '0';
         end
         if isempty(RHS)
            RHS = '0';
         end
         if ~isempty(start)
            % This is an equation with a residual.
            pos = nametype == 3 & strcmp(name,resname);
            if ~any(pos)
               invalidName(iEq) = true;
               continue
            end
            % Residual names will be subtracted later on. We need equations
            % without residuals to create eqtnEvalAllRes.
            if isequal(whereisres,'rhs')
               % Residual placed after the equal sign.
               eqtnEval{iEq} = [LHS,'-(',RHS,')'];
            else
               % Residual placed before the equal sign.
               eqtnEval{iEq} = [RHS,'-(',LHS,')'];
            end
            resoccur(iEq,pos) = true;
         else
            % This is an identity. An identity must have an equal sign and
            % a single variable on the LHS.
            if isinf(equalsignpos)
               invalididentity(iEq) = true;
               continue
            end
            identname = strtrim(LHS);
            % Name position of the LHS variables.
            pos = nametype == 1 & strcmp(name,identname);
            if ~any(pos)
               invalididentity(iEq) = true;
               continue
            end
            identity(iEq) = find(pos);
            % Identity variable names will be added later on. We need
            % equations without identity variable names to create
            % eqtnEvalIdent.
            eqtnEval{iEq} = RHS;
         end
      end
      % Check that all the LHS identity variables are unique.
      chkuniqueident_(identity);
      if any(multiple)
         utils.error('bkwmodel', ...
            'This equation has multiple residual addition commands: ''%s''.', ...
         eqtn{multiple});
      end
      if any(invalidName)
         utils.error('bkwmodel', ...
            'Invalid name in a residual addition command in this equation: ''%s''.', ...
         eqtn{invalidName});
      end      
      if any(invalididentity)
         utils.error('bkwmodel', ...
            'Invalid syntax: ''%s''.', ...
         eqtn{invalididentity});
      end      
      index = sum(double(resoccur),1);
      if any(index > 1)
         utils.error('bkwmodel', ...
            'This residual is used mutliple times: ''%s''.', ...
         name{index > 1});
      end
      % Re-order residual names and exclude those that don't occur in the
      % equations.
      [eqpos,respos] = find(resoccur);
      [eqpos,index] = sort(eqpos);
      % Re-order those residuals that stay in the model.
      respos = respos(index);
      resname = name(respos);
      resnamelabel = namelabel(respos);
      resvalue = value(respos);
      % Positions of the residuals removed.
      excludepos = setdiff(find(nametype == 3),respos);
      excludename = name(excludepos);
      % Remove needless residuals.
      name(excludepos) = [];
      namelabel(excludepos) = [];
      value(excludepos) = [];
      nametype(excludepos) = [];
      nname = length(name);
      % Rewrite the residuals in the proper order.
      name(nametype == 3) = resname;
      namelabel(nametype == 3) = resnamelabel;
      value(nametype == 3) = resvalue;
      if ~isempty(excludename)
         utils.warning('bkwmodel', ...
            'This residual is needless and has been removed from the model: ''%s''.', ...
            excludename{:});
      end
   end
   % End of nested function addresiduals_().

   %***********************************************************************
   % Nested function. 
   function chkuniqueident_(identity)
      [temp,tempindex] = unique(identity);
      if length(temp) ~= length(identity)
         temp = identity;
         temp(tempindex) = [];
         temp = unique(temp);
         utils.error('bkwmodel', ...
            'This name occurs on the LHS of more than one identity: ''%s''.', ...
            name{temp});
      end
   end
   % End of nested function chkuniqueident_().

end