function [ts,s,err] = parseRSSS( ts, initvals )
%s = parseRSSS( ts )
%   Parse the token stream ts, returning a structure s.

% structure = token value
% value = token | { structures }

    [ts,s,err] = parseRSSSvalue( ts, true, initvals );
    if ~err
        s = s.children{1};
    end
end

function [ts,s,err] = parseRSSSvalue( ts, eof_ok, initvals )
    err = false;
    s.attribs = struct();
    s.children = {};
    while true
        [ts,t] = readtoken( ts );
        if isempty(t)
            err = ~eof_ok;
            if err
                unexpected( ts, 'empty token' );
            end
            return;
        end
        if strcmp(t,'{')
            unexpected( ts, 'token ''{''' );
            err = true;
            return;
        end
        if strcmp(t,'}')
            return;
        end
        if ~isfieldname(t)
            unexpected( ts, 'parseRSSS: bad field name "%s"', t );
            err = true;
            return;
        end
        if strcmp( t, 'include' )
            [ts,includefile] = readtoken( ts );
            if isempty(includefile)
                reportParseError( ts, '"include" must be followed by a file name.\n' );
                err = true;
                return;
            end
            ts1 = opentokenstream( includefile );
            if isempty( ts1 )
                fprintf( 1, 'Could not open file "%s".\n', includefile );
                err = true;
                return;
            else
                ts = concattokenstreams( ts1, ts );
              % [ts,s,err] = parseRSSSvalue( ts, eof_ok, initvals );
            end
        else
            [ts,t1] = readtoken( ts );
            if strcmp(t1,'}')
                unexpected( ts, 'token ''}''' );
                err = true;
                return;
            end
            if ~strcmp(t1,'{')
                s.attribs.(t) = convertAttribType( t, t1 );
            else
                [ts,s1,err] = parseRSSSvalue( ts, false, initvals );
                if err, return; end
                s1.type = t;
                s.children{end+1} = s1;
            end
        end
    end
function t2 = convertAttribType( t, t1 )
    i = regexp( t1, '\^', 'once' );
    if ~isempty(i)
        tag = t1((i+1):end);
        if isfield( initvals, tag )
            t2 = initvals.(tag);
            return;
        else
            t1 = t1(1:(i-1));
        end
    end
    switch t
        case { 'value', 'fontsize', ...
               'lines', 'rows', 'columns', 'min', 'max', ...
               'minorstep', 'majorstep' }
            t2 = parseNumbers( t1, '%f', 1 );
        case { 'margin', 'innermargin', 'outermargin' }
            t2 = parseNumbers( t1, '%f', 1, 2 );
            if length(t2)==1
                t2 = [t2 t2];
            end
        case { 'selectedchild' }
            t2 = parseNumbers( t1, '%d', 1 );
        case { 'minsize' }
            t2 = parseNumbers( t1, '%f', 1, 2 );
        case { 'color', 'backgroundcolor', 'foregroundcolor', 'textcolor' }
            t2 = parseNumbers( t1, '%f', 3 );
        case { 'multiline', 'equalwidths', 'equalheights', 'square', 'singlechild' }
            t1 = lower(t1);
            t2 = strcmp( t1, 'yes' ) || strcmp( t1, 'true' );
        otherwise
            t2 = t1;
    end
end

function n = parseNumbers( token, fmt, minnums, maxnums )
    [n,count,msg,index] = sscanf( token, fmt );
    n = n(:)';
    if (nargin >= 3) && (count < minnums)
        reportParseError( ts, '%d numbers expected but %d found.\n', minnums, count );
    end
    if nargin < 4
        maxnums = minnums;
    end
    if count > maxnums
        reportParseError( ts, '%d numbers expected but %d found.\n', maxnums, count );
    end
    if index <= length(token)
        reportParseError( ts, 'Extra found after reading %d numbers: ''%s''.\n', ...
            count, token(index:end) );
    end
end
end

function reportParseError( ts, varargin )
    if isempty( ts.name )
        fprintf( 1, 'Error on line %d: ', ts.curline );
    else
        fprintf( 1, 'Error on line %d of file "%s": ', ts.curline, ts.name );
    end
    fprintf( 1, varargin{:} );
end

            

function unexpected( ts, varargin )
    fprintf( 1, varargin{:} );
    fprintf( 1, ' at token %d of line %d of %s.\n', ts.curtok, ts.curline, ts.name );
end

function ok = isfieldname(t)
    ok = ~isempty( regexp( t, '^[A-Za-z][A-Za-z_0-9]*$', 'once' ) );
end
