Literate programming tools (Matlab)
From LiteratePrograms
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:
- the MATLAB function you want to Ocamawebize
- 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
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 |


