Skip to content

Commit

Permalink
Implicit input. Better compatibility with Octave. Other minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
lmendo committed Dec 31, 2015
1 parent 98a2184 commit bca7a3c
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 80 deletions.
Binary file modified MATL_spec.pdf
Binary file not shown.
Binary file modified funDef.mat
Binary file not shown.
9 changes: 5 additions & 4 deletions funDef.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ Z! 1 1 1 1 1 1 true true out{1} = full(in{1}); convert sparse matrix to full mat
X" 2 inf 3 1 1 1 true true out{1} = repmat(in{:}); replicate and tile array \matlab+repmat+
Y" 2 inf 2 1 1 1 true true out{1} = repelem(in{:}); replicate elements of array \matlab+repelem+ (run-length decoding)
Z" 1 1 1 1 1 1 true true out{1} = blanks(in{1}); string of blanks \matlab+blanks+
# 1 1 1 0 0 0 false false S_OUT = STACK{end}; STACK(end) = []; output specification specify outputs for next function
#
% The numbers of inputs and outputs for # are not used by the code, but are used for generating the help file
X#
Y# 1 2 1 0 0 0 true true rng(in{:}); control the random number generator \matlab+rng+
Y#
Z# 1 3 1 0 0 0 true true data = in{1}; if ~iscell(data), data = {data}; end write to file Appends first input to file \comp{inout}, creating it if necessary. If the input is an array it is converted to char and written. If the input is a cell array input, the contents of each cell are converted to char and written, with a newline (character 10) in between. With $2$ inputs: second input specifies filename; if empty defaults to \comp{inout}. With $3$ inputs: third input specifies whether any previous contents of file should be kept.
sep = char(10);
if numel(in)>=2 && ~isempty(in{2}), file = in{2}; else file = 'inout'; end
Expand All @@ -38,7 +38,7 @@ Z# 1 3 1 0 0 0 true true data = in{1}; if ~iscell(data), data = {data}; end writ
if n<numel(data), fwrite(fid, sep); end
end
fclose(fid);
$ 1 1 1 0 0 0 false false S_IN = STACK{end}; STACK(end) = []; input specification specify inputs for next function
$
% The numbers of inputs and outputs for # are not used by the code, but are used for generating the help file
X$ 1 inf 2 0 inf 1 true true [out{:}] = feval(in{:}); execute Matlab function execute Matlab function specified by first input, using the rest of the inputs as arguments.
Y$ 1 2 1 1 1 1 true true out{1} = char(vpa(in{:})); variable precision arithmetic \matlab+char(vpa(...))+
Expand Down Expand Up @@ -309,7 +309,8 @@ YB 1 2 1 1 1 1 true true out{1} = dec2bin(in{:}); convert decimal number to bina
ZB 1 1 1 1 1 1 true true out{1} = bin2dec(in{1}); convert binary string to decimal number \matlab|bin2dec|
C
XC 1 7 2 1 3 1 true true [out{:}] = histcounts(in{:}); histogram bin counts \matlab+histcounts+
YC 2 4 2 1 1 1 true true out{1} = im2col(in{:}); rearrange array blocks into columns \matlab+im2col+
YC 2 4 2 1 1 1 true true if numel(in{2})==1, if size(in{1},1)==1, in{2} = [1 in{2}]; else in{2} = [in{2} 1]; end; end rearrange array blocks into columns \matlab+im2col+. If the second input is a scalar n, it is transformed into [1 n] if the first input is a row vector, or to [n 1] otherwise
out{1} = im2col(in{:});
ZC
D 0 inf 1 0 0 0 true true if numel(in)==1, data = in; fmt = {'%.16g '}; else data = in(1:end-1); fmt = in(end); end convert to string and display If $1$ input: \matlab+disp(num2str(..., '%.16g '))+. If several inputs: \matlab+disp(num2str(eachInput,lastInput))+, where \matlab+eachInput+ loops over all inputs but the last. In either case, (nested) cell arrays are (recursively) unboxed in linear order. \sa \matl+XD+, \matl+YD+, \matl+ZD+
kk = cellfun(@iscell, data);
Expand Down
8 changes: 8 additions & 0 deletions genHelp.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@

% Statements that are not functions. Changes done here to the comment field should be
% done in `matl_disp.m` too.
F(end+1).source = '$';
F(end).comment = 'input specification';
F(end).description = 'specify inputs for next function';

F(end+1).source = '#';
F(end).comment = 'otput specification';
F(end).description = 'specify outputs for next function';

F(end+1).source = '"';
F(end).comment = 'for';
F(end).description = '\matlab+for+ (control flow: loop). \sa \matl+]+, \matl+@+, \matl+X}+, \matl+Y}+';
Expand Down
Binary file modified help.mat
Binary file not shown.
13 changes: 7 additions & 6 deletions matl.m
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,12 @@ function matl(varargin)
end

% Call help, if required, and quit
useTags = isMatlab && (verNum(1)>7 || (verNum(1)==7 && verNum(2)>=13));
if any(options=='h')
if nargin>1
matl_help(H, s, verbose, isMatlab, verNum)
matl_help(H, s, verbose, useTags)
else
matl_help(H, '', verbose, isMatlab, verNum)
matl_help(H, '', verbose, useTags)
end
return
end
Expand All @@ -214,7 +215,7 @@ function matl(varargin)
if verbose
disp('Parsing program')
end
S = matl_parse(s);
S = matl_parse(s, useTags);
if verbose
if numel(S)~=1
fprintf(' %i statements parsed\n', numel(S))
Expand All @@ -239,7 +240,7 @@ function matl(varargin)
if verbose
disp('Compiling program')
end
S = matl_compile(S, F, L, pOutFile, cOutFile, verbose, isMatlab);
S = matl_compile(S, F, L, pOutFile, cOutFile, verbose, isMatlab, useTags);
%if verbose
% disp(' Done.')
%end
Expand All @@ -253,7 +254,7 @@ function matl(varargin)
%disp('--') %disp(repmat('-',size(str)))
pause
end
matl_run(S, pOutFile, cOutFileNoExt, [], isMatlab) % ...NoExt because a file name without extension is
matl_run(S, pOutFile, cOutFileNoExt, [], isMatlab, useTags) % ...NoExt because a file name without extension is
% needed in old Matlab versions
end

Expand All @@ -263,7 +264,7 @@ function matl(varargin)
disp('Press any key to run MATL program in debug mode')
pause
end
matl_run(S, pOutFile, cOutFileNoExt, [S.compileLine], isMatlab) % ...NoExt because a file name without
matl_run(S, pOutFile, cOutFileNoExt, [S.compileLine], isMatlab, useTags) % ...NoExt because a file name without
% extension is needed in old Matlab versions
end

Expand Down
96 changes: 73 additions & 23 deletions matl_compile.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function S = matl_compile(S, F, L, pOutFile, cOutFile, verbose, isMatlab)
function S = matl_compile(S, F, L, pOutFile, cOutFile, verbose, isMatlab, useTags)
%
% MATL compiler. Compiles into MATLAB code.
% Input: struct array with parsed statements.
Expand All @@ -21,21 +21,41 @@
%
% Luis Mendo

global indStepComp
global C
global indStepComp C implicitInputBlock

indStepComp = 4;

if verbose
disp(' Generating compiled code')
end

Fsource = {F.source}; % this field of `F` will be used often

% Possible improvement: preallocate for greater speed, and modify
% appendLines so that C doesn't dynamically grow
C = {}; % Cell array of strings. Each cell contains a line of compiled (MATLAB) code

if verbose
disp(' Generating compiled code')
if useTags
strongBegin = '<strong>';
strongEnd = '</strong>';
else
strongBegin = '';
strongEnd = '';
end

% Define blocks of code that will be reused
implicitInputBlock = {...
'if ~isempty(nin) && (numel(STACK)+nin(1)<1)' ...
'implInput = {};' ...
'for k = 1:1-numel(STACK)-nin(1)' ...
'implInput{k} = input(implicitInputPrompt,''s'');' ...
'assert(isempty(regexp(implInput{k}, ''^[^'''']*(''''[^'''']*''''[^'''']*)*[a-zA-Z]{2}'', ''once'')), ''MATL:runtime'', ''MATL run-time error: input not allowed'')' ...
'if isempty(implInput{k}), implInput{end} = []; else implInput{k} = eval(implInput{k}); end' ...
'end' ...
'STACK = [implInput STACK];' ...
'clear implInput k' ...
'end'};

% Include function header
[~, name] = fileparts(cOutFile);
appendLines(['function ' name], 0)
Expand All @@ -61,6 +81,7 @@
appendLines('P = pi; Y = inf; N = NaN; M = -1; G = -1j;', 0)
% Constants to be used by function code
appendLines('defaultInputPrompt = ''> '';', 0);
appendLines('implicitInputPrompt = ''> '';', 0);
% Predefine literals for functions
if ~isempty(S)
plf = cellstr(char(bsxfun(@plus, 'X0', [floor(0:.1:2.9).' repmat((0:9).',3,1)]))).'; % {'X0'...'Z9'}
Expand Down Expand Up @@ -101,8 +122,18 @@
case 'literal.logicalRowArray'
lit = strrep(strrep(S(n).source,'T','true,'),'F','false,');
lit = ['[' lit(1:end-1) ']'];
appendLines(['STACK{end+1} = ' lit ';'], S(n).nesting)
appendLines(['STACK{end+1} = ' lit ';'], S(n).nesting)
case 'metaFunction.inSpec'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
appendLines('S_IN = STACK{end}; STACK(end) = [];', S(n).nesting)
case 'metaFunction.outSpec'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
appendLines('S_OUT = STACK{end}; STACK(end) = [];', S(n).nesting)
case 'controlFlow.for'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
newLines{1} = sprintf('in = STACK{end}; STACK(end) = []; indFor%i = 0;', S(n).nesting);
newLines{2} = sprintf('for varFor%i = in', S(n).nesting);
appendLines(newLines, S(n).nesting)
Expand All @@ -117,6 +148,8 @@
newLines = sprintf('indDoWhile%i = indDoWhile%i+1;', S(n).nesting, S(n).nesting);
appendLines(newLines, S(n).nesting+1)
case 'controlFlow.while' % 'X`'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
newLines = { sprintf('indWhile%i = 0;', S(n).nesting) ...
sprintf('condWhile%i = STACK{end};', S(n).nesting) ...
'STACK(end) = [];' ...
Expand All @@ -125,19 +158,25 @@
newLines = sprintf('indWhile%i = indWhile%i+1;', S(n).nesting, S(n).nesting);
appendLines(newLines, S(n).nesting+1)
case 'controlFlow.if' % '?'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
newLines = { 'in = STACK{end}; STACK(end) = [];' ...
'if in' };
appendLines(newLines, S(n).nesting);
case 'controlFlow.else' % '}'
appendLines('else', S(n).nesting)
case 'controlFlow.end' % ']'
if strcmp(S(S(n).from).type, 'controlFlow.doWhile')
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
newLines = { sprintf('condDoWhile%i = STACK{end};', S(n).nesting) ...
'STACK(end) = [];' ...
'end' ...
sprintf('clear indDoWhile%i', S(n).nesting)};
appendLines(newLines, S(n).nesting)
elseif strcmp(S(S(n).from).type, 'controlFlow.while')
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
newLines = { sprintf('condWhile%i = STACK{end};', S(n).nesting) ...
'STACK(end) = [];' ...
'end' ...
Expand All @@ -151,11 +190,15 @@
newLines = 'end';
appendLines(newLines, S(n).nesting);
else
error('MATL:compiler:internal', 'MATL internal error while compiling statement <strong>%s</strong>', S(n).source)
error('MATL:compiler:internal', 'MATL internal error while compiling statement %s%s%s', strongBegin, S(n).source, strongEnd)
end
case 'controlFlow.conditionalBreak'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
appendLines('in = STACK{end}; STACK(end) = []; if in, break, end', S(n).nesting)
case 'controlFlow.conditionalContinue'
appendLines('nin = 0;', S(n).nesting);
appendLines(implicitInputBlock, S(n).nesting); % code block for implicit input
appendLines('in = STACK{end}; STACK(end) = []; if in, continue, end', S(n).nesting)
case 'controlFlow.forValue'
k = S(S(n).from).nesting;
Expand All @@ -169,13 +212,17 @@
case 'function'
k = find(strcmp(S(n).source, Fsource), 1); % Fsource is guaranteed to contain unique entries.
if isempty(k)
error('MATL:compiler', 'MATL error while compiling: function <strong>%s</strong> in <a href="matlab: opentoline(''%s'', %i)">statement number %i</a> not defined in MATL', S(n).source, pOutFile, n, n)
if useTags
error('MATL:compiler', 'MATL error while compiling: function %s%s%s in <a href="matlab: opentoline(''%s'', %i)">statement number %i</a> not defined in MATL', strongBegin, S(n).source, strongEnd, pOutFile, n, n)
else
error('MATL:compiler', 'MATL error while compiling: function %s%s%s in statement number %i not defined in MATL', strongBegin, S(n).source, strongEnd, n)
end
end
appendLines(funWrap(F(k).minIn, F(k).maxIn, F(k).defIn, F(k).minOut, F(k).maxOut, F(k).defOut, ...
F(k).consumeInputs, F(k).wrap, F(k).body), S(n).nesting)
C = [C strcat(blanks(indStepComp*S(n).nesting), newLines)];
otherwise
error('MATL:compiler:internal', 'MATL internal error while compiling statement <strong>%s</strong>: unrecognized statement type', S(n).source)
error('MATL:compiler:internal', 'MATL internal error while compiling statement %s%s%s: unrecognized statement type', strongBegin, S(n).source, strongEnd)
end
end

Expand All @@ -192,22 +239,22 @@
appendLines('% Define subfunctions', 0)
appendLines('', 0)
appendLines({...
'function y = num2str(varargin)';
'x = varargin{1}; x = reshape(x, size(x,1),[]);';
'if nargin==1 || ischar(varargin{1}) || isnumeric(varargin{2})'; % normal `num2str` function
'y = builtin(''num2str'', varargin{:});';
'else'; % interception
'fmt = varargin{2}; y = sprintf([fmt ''\n''], x.''); y = regexp(y, ''\n'', ''split''); y = y(1:end-1).'';';
'y = cellfun(@fliplr, y, ''uniformoutput'', false); y = char(y); y = fliplr(y);';
'y = reshape(y.'',[],size(x,1)).''; y = strtrim(y);';
'end';
'function y = num2str(varargin)' ...
'x = varargin{1}; x = reshape(x, size(x,1),[]);' ...
'if nargin==1 || ischar(varargin{1}) || isnumeric(varargin{2}) || any(imag(x(:)))' ... % normal `num2str` function
'y = builtin(''num2str'', varargin{:});' ...
'else' ... % interception
'fmt = varargin{2}; y = sprintf([fmt ''\n''], x.''); y = regexp(y, ''\n'', ''split''); y = y(1:end-1).'';' ...
'y = cellfun(@fliplr, y, ''uniformoutput'', false); y = char(y); y = fliplr(y);' ...
'y = reshape(y.'',[],size(x,1)).''; y = strtrim(y);' ...
'end' ...
'end'}, 0)
% im2col
appendLines('', 0)
appendLines({...
'function y = im2col(varargin)';
'argin1 = varargin{1}; argin1 = reshape(argin1, size(argin1,1), []);';
'y = builtin(''im2col'', argin1, varargin{2:end});';
'function y = im2col(varargin)' ...
'argin1 = varargin{1}; argin1 = reshape(argin1, size(argin1,1), []);' ...
'y = builtin(''im2col'', argin1, varargin{2:end});' ...
'end'}, 0)
end

Expand Down Expand Up @@ -239,19 +286,22 @@
% Code generated at the beginning of functions: check S_IN and S_OUT,
% get inputs, prepare outputs, consume inputs if applicable.
% `consume` indicates if inputs should be removed from the stack
global implicitInputBlock
newLines = { ...
sprintf('if isempty(S_IN), S_IN = %s; end', defIn) ...
sprintf('if isnumeric(S_IN) && numel(S_IN) == 1, if S_IN < %s || S_IN > %s, error(''MATL:runner'', ''MATL run-time error: incorrect input specification''), end', minIn, maxIn) ...
sprintf('elseif islogical(S_IN), if nnz(S_IN) < %s || nnz(S_IN) > %s, error(''MATL:runner'', ''MATL run-time error: incorrect input specification''), end', minIn, maxIn) ...
'else error(''MATL:runner'', ''MATL run-time error: input specification not recognized''), end' ...
'if isnumeric(S_IN), nin = -S_IN+1:0; else nin = find(S_IN)-numel(S_IN); end' ...
'if isnumeric(S_IN), nin = -S_IN+1:0; else nin = find(S_IN)-numel(S_IN); end'};
newLines = [newLines implicitInputBlock]; % code block for implicit input
newLines = [newLines, {...
'in = STACK(end+nin);' ...
sprintf('if isempty(S_OUT), S_OUT = %s; end', defOut) ...
sprintf('if isnumeric(S_OUT) && numel(S_OUT) == 1, if S_OUT < %s || S_OUT > %s, error(''MATL:runner'', ''MATL run-time error: incorrect output specification''), end', minOut, maxOut) ...
sprintf('elseif islogical(S_OUT), if numel(S_OUT) < %s || numel(S_OUT) > %s, error(''MATL:runner'', ''MATL run-time error: incorrect output specification''), end', minOut, maxOut) ...
'else error(''MATL:runner'', ''MATL run-time error: output specification not recognized''), end' ...
'if isnumeric(S_OUT), nout = S_OUT; else nout = numel(S_OUT); end' ...
'out = cell(1,nout);' };
'out = cell(1,nout);' }];
% For logical S_IN we use nnz (the inputs are picked from the stack), but
% for logical S_OUT we use numel (the function is called with that many outputs)
if consume
Expand Down
2 changes: 2 additions & 0 deletions matl_disp.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ function matl_disp(S, F, indentBase, indentStep, indentCommentSymbol, indentComm
texts(ismember(Stype, {'literal.logicalRowArray'})) = {'logical row array literal'};
texts(ismember(Stype, {'literal.cellArray'})) = {'cell array literal'};
texts(ismember(Stype, {'literal.string'})) = {'string literal'};
texts(strcmp(Stype, 'metaFunction.inSpec')) = {'input specification'};
texts(strcmp(Stype, 'metaFunction.outSpec')) = {'output specification'};
texts(strcmp(Stype, 'controlFlow.for')) = {'for'};
texts(strcmp(Stype, 'controlFlow.doWhile')) = {'do...while'};
texts(strcmp(Stype, 'controlFlow.while')) = {'while'};
Expand Down
Loading

0 comments on commit bca7a3c

Please sign in to comment.