The Wayback Machine - https://web.archive.org/web/20100423003444/http://en.literateprograms.org:80/Literate_programming_tools_(Matlab)

Literate programming tools (Matlab)

From LiteratePrograms

Jump to: navigation, search

Contents

Basic tools

Let put in this page tools in Matlab related to literate programming.

Function print4literate

Here is a function which produce an ASCII file that can by pasted into this wiki:

<<print4literate.m>>=
function str = print4literate(fnames, varargin)
% PRINT4LITERATE - produce an ASCII file ready to be pasted into
% <a href="http://www.literateprograms.org">LP.org</a>.
%
% use:
%  print4literate({file1, ..., fileN}, 'ascii-file-name' [, section-level])
% (default section level is 3)
% if you give no file name (or empty file name), nothing is writen.
%
% Original author: Charles-Albert LEHALLE

str = '';
if ~iscell(fnames)
    fnames = {fnames};
end
if nargin<3
    ext = 3;
else
    ext = varargin{2};
end
for i=1:length(fnames)
    str = sprintf('%s\n%s', str, FILE2LIT( fnames{i}, ext));
end
if nargin>1 & ~isempty(varargin{1})
    fod = fopen(varargin{1},'w');
    fprintf(fod, '%s', str);
    fclose(fod);
end

%%* Work on one function
function str = FILE2LIT(fname, ext)
str_ext = repmat('=',1,ext);
str = sprintf('%sFunction %s%s\n', str_ext, fname, str_ext);

oneliner = help(fname);
str = sprintf('%s\n%s\n<%s language=matlab>\n<<%s.m>>=', str, strtrim(oneliner), 'codeblock', fname);

fid = fopen(which(fname),'r');
while 1
    tline = fgetl(fid);
    if ~ischar(tline),   break,   end
    str = sprintf('%s\n%s',str,tline);
end
fclose(fid);

str = sprintf('%s\n</%s>\n', str, 'codeblock');

OCAMAWEB2

Ocamaweb2 is a Lite version of OCAMAWEB rewritten in Matlab dedicated to be used in conjunction with LP.org.

The content of the ocamaweb2.m section (and following) of this article has been generated by this command line:

ocamaweb2('ocamaweb2', 'c:\tmp\ocamaweb2.txt', 1)

To obtain OCamaweb2.m you have to use the download code tab of this article. You can either take the ocamaweb2.m file or the ocamaweb2_original.m file (and rename it ocamaweb2.m). The only difference is that the original file has comments in it.

Please feel free to put comments on my discussion page.


User guide

To use Ocamaweb2, simply give to this MATLAB function:

  1. the MATLAB function you want to Ocamawebize
  2. the starting level of your sections (defaut is zero)

Your MATLAB file need to include special (but quite simple) tags in its comment lines to be Ocamawebized.

At first you need to understand some basic concepts, which is that an ocamawebizable file is made of:

  • blocks of code
  • each block of code is made of
    • a title
    • a comment part
    • a code part

The title tags

The titles can be declared as:

%%** My Title
%%* My Title
%% My Title
%%*n My Title

or

%<** My Title
%<* My Title
%< My Title
%<*n My Title

where n is between 1 and 9.

The comments of a block

All the immediately following comment lines are considered to be part of the comments of the block.

%%** My Title
% Comments, 
% more comment
a=1;
% simple comment (not comment of the block)
b=2;

Code of a block

The code is all what is not comments of the block and is located inside the block.

What is inside a block?

  • For blocks which are opened by %< my title, the block is closed by the matching %>.
  • For blocks which are opened by %% my title, the block is closed the next time a %% another title is encoutered.

Simple example

For instance:

%%** My title
% this is a comment
a=1;
%<* Another section
b=1;
%>*
c=1;

Will define two blocks:

  • My title (comments: this is a comment)
a=1;
<<another section>>
c=1;
  • Another section (no comment)
b=1;

Block levels

The block level is defined by the stars just after the block tag:

  • **
    means section (i.e. == by default)
  • *
    means subsection (i.e. === )
  • nothing means paragraph (i.e. ==== )
  • *n
    means n levels below paragraph

The third argument of the Ocamaweb2 function allow to shift the levels of all blocks.

Include Mathematics

All LaTeX code included into comments of block are translated into maths for LP.org.

ocamaweb2.m

<<ocamaweb2.m>>=
function str = ocamaweb2(fnames, varargin)
% OCAMAWEB2 - produce an ASCII file ready to be pasted into
% <a href="http://www.literateprograms.org">LP.org</a>.
%
% use:
%  ocamaweb2( matlab_file, 'ascii-file-name' [, first-section-level])
% (default section level is 0)
% if you give no file name (or empty file name), nothing is writen.
%
% You just need to copy-and-paste the obtained file to LP.org
%
% Author: Charles-Albert LEHALLE


Ocamaweb4LP.org_2

Internals_6

Ocamaweb4LP.org

To export codes to LP.org

<<Ocamaweb4LP.org_2>>=
output_style = 'LP.org';


Default values for parameters_3
if nargin>1 & ~isempty(varargin{1})
    xlog('$open$', [varargin{1} '.log']);
end

Read all files_4

Write in a file if needed_5
str = blocks;

Default values for parameters

If only one file, it's put in a cellarray.

<<Default values for parameters_3>>=
str = '';
if ~iscell(fnames)
    fnames = {fnames};
end
if nargin<3
    ext = 0;
else
    ext = varargin{2};
end

Read all files

Read all files inside a loop.

<<Read all files_4>>=
blocks = {};
for i=1:length(fnames)
    [str_tmp, blk_tmp] = FILE2LIT( fnames{i}, ext, output_style);
    blocks{end+1} = blk_tmp;
    str = sprintf('%s\n%s', str, str_tmp);
end

Write in a file if needed

<<Write in a file if needed_5>>=
if nargin>1 & ~isempty(varargin{1})
    fod = fopen(varargin{1},'w');
    for f=1:length(blocks)
        this_block = blocks{f};
        for b=1:length(this_block)
            PRETTY_PRINT( fod, this_block(b), output_style, ext);
        end
    end
    %fprintf(fod, '%s', str);
    fclose(fod);
    xlog('$close$');
end

Internals

All the internal functions are here. They are the main processing elements of this function.

<<Internals_6>>=

 Parse one matfile_7


Parse the first block line_12

Guess block level_13

Write block mark_14

Create new block_15

Final print_16

Pretty Section_17

Get section codes_18

Pretty outputs_21

Parse one matfile.

<< Parse one matfile_7>>=
function [str, blocks] = FILE2LIT(fname, ext, output_style)

str_ext = repmat('=',1,ext+3);
str = sprintf('%sFunction %s%s\n', str_ext, fname, str_ext);

oneliner = help(fname);
la  = '<';
ra  = '>';
str = sprintf('%s\n%s\n<%s language=matlab>\n%s%s%s.m%s%S=', str, strtrim(oneliner), 'codeblock', la, la, fname, ra, ra);

%blocks  = struct('name', fname, 'id', 1, 'cmt', '', 'code', '', 'state', 1);
blocks  = NEW_BLOCK( [fname '.m'], 1, '', -1, 100, 1, 0);
cb      = 1; % Current Block Id
%BLOCK STATES- 1: read code / 0: read comments

fid = fopen(which(fname),'r');
while 1
    tline = fgetl(fid);
    if ~ischar(tline)
        break
    end
    xlog('>>> %s\n', tline);
    trline = strtrim(tline);
    if length(trline)>1 & (all(trline(1:2)=='%%') | all(trline(1:2)=='%<') )

 Begin of block_8
    elseif length(trline)>1 & all(trline(1:2)=='%>')

 End of block_9
    elseif isempty(trline) | trline(1)~='%'

 End of comments_10
    else 

 Write comment or code_11
    end
    
    str = sprintf('%s\n%s',str,tline);
end
fclose(fid);

str = sprintf('%s\n</%s>\n', str, 'codeblock');

Begin of block.

<< Begin of block_8>>=
        if blocks(cb).state == 0
            % current block is now in code mode
            blocks(cb).state = 1;
            xlog('quit comments for <%s>\n', blocks(cb).name);
        end
        if all(trline(1:2)=='%%') & blocks(cb).preceeding > 0
            % if %% -> close preceeding block...
            % but not the root one
            xlog('Closing block <%s>\n', blocks(cb).name);
            blocks(cb).state = 1;
            cb = blocks(cb).preceeding;
            xlog('Open block <%s>\n', blocks(cb).name);
        end
        % Parsing block heading
        [b_name, b_type, b_level] = PARSE_1ST_LINE( trline);
        xlog('block <%s> seen @[%s] as a <%d:%d>\n', b_name, trline, b_type, b_level);
        % Including block reference into corrent bloc
        b_id            = length(blocks)+1;
        blocks(cb).code = sprintf('%s\n%s', blocks(cb).code, BLOCK_MARK( b_name, b_id, output_style));
        % Creating my new block
        blocks(end+1) = NEW_BLOCK( b_name, 0, '', b_type, b_level, b_id, cb);
        cb            = b_id;

End of block.

<< End of block_9>>=
        xlog('close block <%s>\n', blocks(cb).name);
        cb = blocks(cb).preceeding;
        xlog('open block <%s>\n', blocks(cb).name);

End of comments.

<< End of comments_10>>=
        xlog('end of comments for block <%s>\n', blocks(cb).name);
        blocks(cb).state = 1;
        blocks(cb).code  = sprintf('%s\n%s', blocks(cb).code, tline);

Write comment or code.

<< Write comment or code_11>>=
        if blocks(cb).state ==0
            % comment
            xlog('write into cmt\n');
            blocks(cb).cmt  = sprintf('%s\n%s', blocks(cb).cmt, strtrim(trline(2:end)));
        else
            % code
            xlog('write into code\n');
            blocks(cb).code  = sprintf('%s\n%s', blocks(cb).code, tline);
        end

Parse the first block line.

returns the block name, the block type, and the block level

<<Parse the first block line_12>>=
function [bn, bt, bl] = PARSE_1ST_LINE( str)
switch str(1:2)
    case '%%'
        bt = 0;
    case '%<'
        bt = 1;
    otherwise
        error('print4literate:P1L', 'problem with line <%s>', str);
end
str = str(3:end); % if lower error
idx_space = strfind(str, ' ');
if isempty(idx_space) | idx_space(1)==1
    bn = str;
    bl = 0;
else
    bn = str(idx_space(1)+1:end);
    bl = BLK_LEVEL(str(1:idx_space(1)-1));
end

Guess block level

<<Guess block level_13>>=
function lev = BLK_LEVEL( str)
switch str
    case '**'
        lev = 100;
    case '*'
        lev = 10;
    otherwise
        lev = str2num(str(2:end));
end

Write block mark

<<Write block mark_14>>=
function str = BLOCK_MARK(bname, bnum, bstyle)
switch bstyle
    case 'LP.org'
        la  = '<';
        ra  = '>';
        str = sprintf('\n%s%s%s_%d%s%s', la, la, bname, bnum, ra, ra);
    otherwise
        error('P4L:BM', 'no style <%s> available', bstyle);
end

Create new block

<<Create new block_15>>=
function blk = NEW_BLOCK( b_name, b_state, cmt, b_type, b_level, b_id, p_block)
blk = struct('name', b_name, 'id', b_id, 'cmt', cmt, 'code', '', 'state', b_state, 'type', b_type, 'level', b_level, 'preceeding', p_block);

Final print

<<Final print_16>>=
function PRETTY_PRINT( fod, ablock, output_style, dec)
if nargin < 4
    dec = 0;
end
% Comments
fprintf(fod, PRETTY_SECTION( output_style, ablock.level, ablock.name, ablock.cmt, ablock.id, dec));
% Code
fprintf(fod, PRETTY_CODE( output_style, ablock.level, ablock.name, ablock.code, ablock.id, dec));

Pretty Section

<<Pretty Section_17>>=
function str = PRETTY_SECTION( output_style, level, name, cmt, id, dec)
switch output_style
    case 'LP.org'
        sections = { {'==', '=='}, {'===', '==='}, {'====', '===='}, {'''''''', '.''''''<br/>'}, {'''''', '.''''<br/>'}};
    otherwise
        error('P4L:SECTION', 'style <%s> unknown', output_style);
end
str = sprintf('%s%s%s\n%s\n', GET_LEV_BEFORE(sections, level, dec), name, GET_LEV_AFTER(sections, level, dec), PRETTY_CMT(output_style, cmt));

Get section codes

<<Get section codes_18>>=

 Before section_19

 After section_20

Before section.

<< Before section_19>>=
function str = GET_LEV_BEFORE(sections, level, dec)
if level == 100
    level = -2;
elseif level == 10
    level = -1;
end
level = min(level+3+dec, length(sections));
str   = sections{level}{1};

After section.

<< After section_20>>=
function str = GET_LEV_AFTER(sections, level, dec)
if level == 100
    level = -2;
elseif level == 10
    level = -1;
end
level = min(level+3+dec, length(sections));
str   = sections{level}{2};

Pretty outputs

<<Pretty outputs_21>>=

 Pretty Cmt_22

 Pretty Code_23

 String Replace_24

Pretty Cmt.

This one is special for LaTeX expressions, for instance \alpha=\int_{x\in\Omega} f_\alpha(x)dx should be translated to a math expression for LP.org.

<< Pretty Cmt_22>>=
function str = PRETTY_CMT(output_style, cmt)
str = regexprep(cmt, '\$\$([^\$]+)\$\$', 'UNIQ628f3d29602d7d56-matha76b7dc69a4fd700000002');
str = regexprep(str, '\$([^\$]+)\$', 'UNIQ628f3d29602d7d56-matha76b7dc69a4fd700000003');
str = strrep(str, '\', '\\');

Pretty Code.

<< Pretty Code_23>>=
function str = PRETTY_CODE( output_style, level, name, code, id, dec)
switch output_style
    case 'LP.org'
        cb = 'codeblock';
        ra = '<';
        la = '>';
        sections = { ['<' cb ' language="matlab">'], [ra ra], [la la '='], ['</' cb '>'] };
    otherwise
        error('P4L:CODE', 'style <%s> unknown', output_style);
end
if id>1
    name = sprintf('%s_%d', name, id);
end
str = sprintf('%s\n%s%s%s%s\n%s\n', sections{1}, sections{2}, name, sections{3}, STR_REP(code), sections{4});

String Replace.

<< String Replace_24>>=
function str = STR_REP( str)
str = strrep(strrep(str, '%', '%%'), '\', '\\');

Original file

To allow you to compare the obtained parsed file and the original one, here is the file that has been given to Ocamweb2 (i.e. the original commented Ocamaweb2 file).

<<ocamaweb2_original.m>>=
function str = ocamaweb2(fnames, varargin)
% OCAMAWEB2 - produce an ASCII file ready to be pasted into
% <a href="http://www.literateprograms.org">LP.org</a>.
%
% use:
%  ocamaweb2( matlab_file, 'ascii-file-name' [, first-section-level])
% (default section level is 0)
% if you give no file name (or empty file name), nothing is writen.
%
% You just need to copy-and-paste the obtained file to LP.org
%
% Author: Charles-Albert LEHALLE

%%** Ocamaweb4LP.org
% To export codes to LP.org
output_style = 'LP.org';

%<* Default values for parameters
% If only one file, it's put in a cellarray.
str = '';
if ~iscell(fnames)
    fnames = {fnames};
end
if nargin<3
    ext = 0;
else
    ext = varargin{2};
end
%>*
if nargin>1 & ~isempty(varargin{1})
    xlog('$open$', [varargin{1} '.log']);
end
%<* Read all files
% Read all files
% inside a loop.
blocks = {};
for i=1:length(fnames)
    [str_tmp, blk_tmp] = FILE2LIT( fnames{i}, ext, output_style);
    blocks{end+1} = blk_tmp;
    str = sprintf('%s\n%s', str, str_tmp);
end
%>*
%<* Write in a file if needed
if nargin>1 & ~isempty(varargin{1})
    fod = fopen(varargin{1},'w');
    for f=1:length(blocks)
        this_block = blocks{f};
        for b=1:length(this_block)
            PRETTY_PRINT( fod, this_block(b), output_style, ext);
        end
    end
    %fprintf(fod, '%s', str);
    fclose(fod);
    xlog('$close$');
end
%>*
str = blocks;
%%** Internals
% All the internal functions are here.
% They are the main processing elements of this function.
%< Parse one matfile
function [str, blocks] = FILE2LIT(fname, ext, output_style)

str_ext = repmat('=',1,ext+3);
str = sprintf('%sFunction %s%s\n', str_ext, fname, str_ext);

oneliner = help(fname);
la  = '<';
ra  = '>';
str = sprintf('%s\n%s\n<%s language=matlab>\n%s%s%s.m%s%S=', str, strtrim(oneliner), 'codeblock', la, la, fname, ra, ra);

%blocks  = struct('name', fname, 'id', 1, 'cmt', '', 'code', '', 'state', 1);
blocks  = NEW_BLOCK( [fname '.m'], 1, '', -1, 100, 1, 0);
cb      = 1; % Current Block Id
%BLOCK STATES- 1: read code / 0: read comments

fid = fopen(which(fname),'r');
while 1
    tline = fgetl(fid);
    if ~ischar(tline)
        break
    end
    xlog('>>> %s\n', tline);
    trline = strtrim(tline);
    if length(trline)>1 & (all(trline(1:2)=='%%') | all(trline(1:2)=='%<') )
        %< Begin of block
        if blocks(cb).state == 0
            % current block is now in code mode
            blocks(cb).state = 1;
            xlog('quit comments for <%s>\n', blocks(cb).name);
        end
        if all(trline(1:2)=='%%') & blocks(cb).preceeding > 0
            % if %% -> close preceeding block...
            % but not the root one
            xlog('Closing block <%s>\n', blocks(cb).name);
            blocks(cb).state = 1;
            cb = blocks(cb).preceeding;
            xlog('Open block <%s>\n', blocks(cb).name);
        end
        % Parsing block heading
        [b_name, b_type, b_level] = PARSE_1ST_LINE( trline);
        xlog('block <%s> seen @[%s] as a <%d:%d>\n', b_name, trline, b_type, b_level);
        % Including block reference into corrent bloc
        b_id            = length(blocks)+1;
        blocks(cb).code = sprintf('%s\n%s', blocks(cb).code, BLOCK_MARK( b_name, b_id, output_style));
        % Creating my new block
        blocks(end+1) = NEW_BLOCK( b_name, 0, '', b_type, b_level, b_id, cb);
        cb            = b_id;
        %>
    elseif length(trline)>1 & all(trline(1:2)=='%>')
        %< End of block
        xlog('close block <%s>\n', blocks(cb).name);
        cb = blocks(cb).preceeding;
        xlog('open block <%s>\n', blocks(cb).name);
        %>
    elseif isempty(trline) | trline(1)~='%'
        %< End of comments
        xlog('end of comments for block <%s>\n', blocks(cb).name);
        blocks(cb).state = 1;
        blocks(cb).code  = sprintf('%s\n%s', blocks(cb).code, tline);
        %>
    else 
        %< Write comment or code
        if blocks(cb).state ==0
            % comment
            xlog('write into cmt\n');
            blocks(cb).cmt  = sprintf('%s\n%s', blocks(cb).cmt, strtrim(trline(2:end)));
        else
            % code
            xlog('write into code\n');
            blocks(cb).code  = sprintf('%s\n%s', blocks(cb).code, tline);
        end
        %>
    end
    
    str = sprintf('%s\n%s',str,tline);
end
fclose(fid);

str = sprintf('%s\n</%s>\n', str, 'codeblock');
%>*

%<*3 Parse the first block line
% returns the block name, the block type, and the block level
function [bn, bt, bl] = PARSE_1ST_LINE( str)
switch str(1:2)
    case '%%'
        bt = 0;
    case '%<'
        bt = 1;
    otherwise
        error('print4literate:P1L', 'problem with line <%s>', str);
end
str = str(3:end); % if lower error
idx_space = strfind(str, ' ');
if isempty(idx_space) | idx_space(1)==1
    bn = str;
    bl = 0;
else
    bn = str(idx_space(1)+1:end);
    bl = BLK_LEVEL(str(1:idx_space(1)-1));
end
%>*
%<* Guess block level
function lev = BLK_LEVEL( str)
switch str
    case '**'
        lev = 100;
    case '*'
        lev = 10;
    otherwise
        lev = str2num(str(2:end));
end
%>*
%<* Write block mark
function str = BLOCK_MARK(bname, bnum, bstyle)
switch bstyle
    case 'LP.org'
        la  = '<';
        ra  = '>';
        str = sprintf('\n%s%s%s_%d%s%s', la, la, bname, bnum, ra, ra);
    otherwise
        error('P4L:BM', 'no style <%s> available', bstyle);
end
%>*
%<* Create new block
function blk = NEW_BLOCK( b_name, b_state, cmt, b_type, b_level, b_id, p_block)
blk = struct('name', b_name, 'id', b_id, 'cmt', cmt, 'code', '', 'state', b_state, 'type', b_type, 'level', b_level, 'preceeding', p_block);
%>*
%<* Final print
function PRETTY_PRINT( fod, ablock, output_style, dec)
if nargin < 4
    dec = 0;
end
% Comments
fprintf(fod, PRETTY_SECTION( output_style, ablock.level, ablock.name, ablock.cmt, ablock.id, dec));
% Code
fprintf(fod, PRETTY_CODE( output_style, ablock.level, ablock.name, ablock.code, ablock.id, dec));
%>*
%<* Pretty Section
function str = PRETTY_SECTION( output_style, level, name, cmt, id, dec)
switch output_style
    case 'LP.org'
        sections = { {'==', '=='}, {'===', '==='}, {'====', '===='}, {'''''''', '.''''''<br/>'}, {'''''', '.''''<br/>'}};
    otherwise
        error('P4L:SECTION', 'style <%s> unknown', output_style);
end
str = sprintf('%s%s%s\n%s\n', GET_LEV_BEFORE(sections, level, dec), name, GET_LEV_AFTER(sections, level, dec), PRETTY_CMT(output_style, cmt));
%>*
%<* Get section codes
%< Before section
function str = GET_LEV_BEFORE(sections, level, dec)
if level == 100
    level = -2;
elseif level == 10
    level = -1;
end
level = min(level+3+dec, length(sections));
str   = sections{level}{1};
%>
%< After section
function str = GET_LEV_AFTER(sections, level, dec)
if level == 100
    level = -2;
elseif level == 10
    level = -1;
end
level = min(level+3+dec, length(sections));
str   = sections{level}{2};
%>
%>*
%<* Pretty outputs
%< Pretty Cmt 
% This one is special for LaTeX expressions, for instance
% $\alpha=\int_{x\in\Omega} f_\alpha(x)dx$ should be translated to a math
% expression for LP.org.
function str = PRETTY_CMT(output_style, cmt)
str = regexprep(cmt, '\$\$([^\$]+)\$\$', 'UNIQ628f3d29602d7d56-matha76b7dc69a4fd700000004');
str = regexprep(str, '\$([^\$]+)\$', 'UNIQ628f3d29602d7d56-matha76b7dc69a4fd700000005');
str = strrep(str, '\', '\\');
%>
%< Pretty Code
function str = PRETTY_CODE( output_style, level, name, code, id, dec)
switch output_style
    case 'LP.org'
        cb = 'codeblock';
        ra = '<';
        la = '>';
        sections = { ['<' cb ' language="matlab">'], [ra ra], [la la '='], ['</' cb '>'] };
    otherwise
        error('P4L:CODE', 'style <%s> unknown', output_style);
end
if id>1
    name = sprintf('%s_%d', name, id);
end
str = sprintf('%s\n%s%s%s%s\n%s\n', sections{1}, sections{2}, name, sections{3}, STR_REP(code), sections{4});
%>
%< String Replace
function str = STR_REP( str)
str = strrep(strrep(str, '%', '%%'), '\', '\\');
%>
%>*
Download code
Personal tools