From cddd389977c51924a698c6af583c022f2841dbb6 Mon Sep 17 00:00:00 2001 From: Victor Lei Date: Thu, 9 Jun 2016 11:49:57 +0300 Subject: [PATCH 1/7] Recovered SMOP.rst --- smop/SMOP.rst | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 smop/SMOP.rst diff --git a/smop/SMOP.rst b/smop/SMOP.rst new file mode 100644 index 00000000..cba267f1 --- /dev/null +++ b/smop/SMOP.rst @@ -0,0 +1,232 @@ + +Class matlabarray +================= + +Matlab arrays differ from numpy arrays in many ways, and class +matlabarray captures the following differences: + +A. Base-one indexing + Following Fortran tradition, matlab starts array indexing with + one, not with zero. Correspondingly, the last element of a + N-element array is N, not N-1. + +B. C_CONTIGUOUS and F_CONTIGUOUS data layout + Matlab matrix elements are ordered in columns-first, aka + F_CONTIGUOUS order. By default, numpy arrays are C_CONTIGUOUS. + Instances of matlabarray are F_CONTIGUOUS, except if created + empty, in which case they are C_CONTIGUOUS. + + +-----------------------+--------------------------------------+ + | matlab | numpy | + +=======================+======================================+ + |:: |:: | + | | | + | > reshape(1:4,[2 2]) | >>> a=matlabarray([1,2,3,4]) | + | 1 3 | >>> a.reshape(2,2,order="F") | + | 2 4 | 1 3 | + | | 2 4 | + | | | + | | >>> a.reshape(2,2,order="C") | + | | 1 2 | + | | 3 4 | + +-----------------------+--------------------------------------+ + + >>> a=matlabarray([1,2,3,4]) + >>> a.flags.f_contiguous + True + >>> a.flags.c_contiguous + False + + >>> a=matlabarray() + >>> a.flags.c_contiguous + True + >>> a.flags.f_contiguous + False + +C. Auto-expanding arrays + Arrays are auto-expanded on out-of-bound assignment. Deprecated, + this feature is widely used in legacy code. In smop, out-of-bound + assignment is fully supported for row and column vectors, and for + their generalizations having shape + + [1 1 ... N ... 1 1 1] + + These arrays may be resized along their only non-singular + dimension. For other arrays, new columns can be added to + F_CONTIGUOUS arrays, and new rows can be added to C_CONTIGUOUS + arrays. + + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + |:: |:: | + | | | + | > a=[] | >>> a=matlabarray() | + | > a(1)=123 | >>> a[1]=123 | + | > a | >>> a | + | 123 | 123 | + | | | + +----------------------------+----------------------------------+ + +D. Create by update + In matlab, arrays can be created by updating a non-existent array, + as in the following example: + + >>> clear a + >>> a(17) = 42 + + This unique feature is not yet supported by smop, but can be + worked around by inserting assignments into the original matlab + code: + + >>> a = [] + >>> a(17) = 42 + +E. Assignment as copy + Array data is not shared by copying or slice indexing. Instead + there is copy-on-write. + +F. Everything is a matrix + There are no zero or one-dimensional arrays. Scalars are + two-dimensional rather than zero-dimensional as in numpy. + +G. Single subscript implies ravel. + TBD + +H. Broadcasting + Boadcasting rules are different + +I. Boolean indexing + TBD + +---------------------------------------------------------------------- + +Data structures +=============== + +#. Empty vector [], empty string "", and empty cellarray {} + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + | :: | :: | + | | | + | > size([]) | >>> matlabarray().shape | + | 0 0 | (0, 0) | + | | | + | > size('') | >>> char().shape | + | 0 0 | (0, 0) | + | | | + | > size({}) | >>> cellarray().shape | + | 0 0 | (0, 0) | + +----------------------------+----------------------------------+ + + +#. Scalars are 1x1 matrices + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + | :: | :: | + | | | + | > a=17 | >>> a=matlabarray(17) | + | > size(a) | >>> a.shape | + | 1 1 | 1 1 | + | | | + +----------------------------+----------------------------------+ + +#. Rectangular char arrays + Class char inherits from class matlabarray the usual matlab array + behaviour -- base-1 indexing, Fortran data order, auto-expand on + out-of-bound assignment, etc. + + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + | :: | :: | + | | | + | > s='helloworld' | >>> s=char('helloworld') | + | > size(s) | >>> print size_(s) | + | 1 10 | (1,10) | + | > s(1:5)='HELLO' | >>> s[1:5]='HELLO' | + | > s | >>> print s | + | HELLOworld | HELLOworld | + | > resize(s,[2 5]) | >>> print resize_(s,[2,5]) | + | HELLO | HELLO | + | world | world | + +----------------------------+----------------------------------+ + +#. Row vector + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + | :: | :: | + | | | + | > s=[1 2 3] | >>> s=matlabarray([1,2,3]) | + | | | + +----------------------------+----------------------------------+ + + +#. Column vector + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + |:: |:: | + | | | + | > a=[1;2;3] | >>> a=matlabarray([[1], | + | | [2], | + | | [2]]) | + | > size(a) | >>> a.shape | + | 3 1 | (3, 1) | + +----------------------------+----------------------------------+ + + +#. Cell arrays + Cell arrays subclass matlabarray and inherit the usual matlab + array behaviour -- base-1 indexing, Fortran data order, expand on + out-of-bound assignment, etc. Unlike matlabarray, each element of + cellarray holds a python object. + + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + |:: |:: | + | | | + | > a = { 'abc', 123 } | >>> a=cellarray(['abc',123]) | + | > a{1} | >>> a[1] | + | abc | abc | + +----------------------------+----------------------------------+ + +#. Cell arrays of strings + In matlab, cellstrings are cell arrays, where each cell contains a + char object. In numpy, class cellstring derives from matlabarray, + and each cell contains a native python string (not a char + instance). + + +----------------------------+----------------------------------+ + | matlab | numpy | + +============================+==================================+ + |:: |:: | + | | | + | > a = { 'abc', 'hello' } | >>> a=cellstring(['abc', | + | | 'hello']) | + | > a{1} | >>> a[1] | + | abc | abc | + +----------------------------+----------------------------------+ + +---------------------------------------------------------------------- + +Data structures + All matlab data structures subclass from matlabarray + +Structs + TBD + +Function pointers + Handles @ + +String concatenation + Array concatenation not implemented + + >>> ['hello' 'world'] + helloworld + +.. vim: tw=70:sw=2 From 99d82d5b69f3db7d6fbb4c476e6be6712c8ff16d Mon Sep 17 00:00:00 2001 From: Victor Lei Date: Thu, 9 Jun 2016 12:38:35 +0300 Subject: [PATCH 2/7] m --- smop/benchmark5/benchmark5.m | 201 ---------------- smop/mybench.m | 450 ----------------------------------- smop/mybench.py | 248 ------------------- 3 files changed, 899 deletions(-) delete mode 100644 smop/benchmark5/benchmark5.m delete mode 100644 smop/mybench.m delete mode 100644 smop/mybench.py diff --git a/smop/benchmark5/benchmark5.m b/smop/benchmark5/benchmark5.m deleted file mode 100644 index 0f46b15b..00000000 --- a/smop/benchmark5/benchmark5.m +++ /dev/null @@ -1,201 +0,0 @@ -%*************************************************************************** -%* Matlab Benchmark program version 5.0 * -%* Author : Stefan Steinhaus * -%* EMAIL : stefan@steinhaus-net.de * -%* This program is public domain. Feel free to copy it freely. * -%*************************************************************************** - -clc -disp('The following benchmark program will print the average timings') -disp('to calculate the functions by 3 runs.') -disp(' ') -disp(' ') -disp('!!! MATLAB - Benchmarkprogram !!!') -disp('=================================') -disp(' ') - -%* Misc. operation * - -result=0; runs=3; -for i=1:runs; - tic; - datmat=importdata('./Currency2.txt',' '); - Limits_Area=[1,261,522,784,1045,1305,1565,1827,2088,2349,2610,2871,3131,3158]; - for k=1:2000; - for j=1:13; - Year_Data=datmat.data(Limits_Area(j):Limits_Area(j+1)-1,4:37); - Min_Year=min(Year_Data); - Max_Year=max(Year_Data); - Mean_Year=mean(Year_Data); - GainLoss_Year=100-(Year_Data(1,:)+0.00000001)./(Year_Data(end,:)+0.00000001)*100; - end; - end; - result=result+toc; -end; -result=result/runs; -disp(['IO test & descriptive statistics_____________________ : ' num2str(result) ' sec.']) - -result=0; a=1; -for i=1:runs - tic; - for x=1:15000; - for y=1:15000; - a=a+x+y; - end; - end; - result=result+toc; -end; -result=result/runs; -disp(['Loop testing_________________________________________ : ' num2str(result) ' sec.']) -result=0; -for i=1:runs - for j=1:200 - b=1+(rand(2000,2000)/100); - tic; - a=b.^1000; - result=result+toc; - end -end -result=result/runs; -disp(['2000x2000 normal distributed random matrix^1000______ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1000000,1); - tic; - b=sort(a); - result=result+toc; - end -end -result=result/runs; -disp(['1000000 values sorted ascending______________________ : ' num2str(result) ' sec.']) - -%* Analysis * - -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1048576,1); - tic; - b=fft(a); - result=result+toc; - end -end -result=result/runs; -disp(['FFT over 1048576 values (2^20)_______________________ : ' num2str(result) ' sec.']) - -%* Algebra * - -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - tic; - b=det(a); - result=result+toc; - end -end -result=result/runs; -disp(['Determinant of a 1500x1500 random matrix_____________ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - tic; - b=inv(a); - result=result+toc; - end -end -result=result/runs; -disp(['Inverse of a 1500x1500 uniform distr. random matrix__ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - a=rand(1200,1200); - tic; - c=eig(a); - result=result+toc; -end -result=result/runs; -disp(['Eigenval. of a normal distr. 1200x1200 randommatrix___ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - a=a'*a; - tic; - b=chol(a); - result=result+toc; - end -end -result=result/runs; -disp(['Cholesky decomposition of a 1500x1500-matrix_________ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - tic; - b=a'*a; - result=result+toc; - end -end -result=result/runs; -disp(['1500x1500 cross-product matrix_______________________ : ' num2str(result) ' sec.']) - -%* Number theory * - -clear a; clear b; -phi = 1.6180339887498949; result=0; -for i=1:runs; - a=floor(1000*rand(10000000,1)); - tic; - b=(phi.^a-(-phi).^(-a))/sqrt(5); - result=result+toc; -end -result=result/runs; -disp(['Calculation of 10000000 fibonacci numbers____________ : ' num2str(result) ' sec.']) - -%* Stochastic-statistic * - -result=0; a=0; b=0; -for i=1:runs; - a=rand(10000,1000); - tic; - b=princomp(a); - result=result+toc; -end; -result=result/runs; -disp(['Calc. of the principal components of a 10000x1000 matrix : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - tic; - b=gamma(a); - result=result+toc; - end -end -result=result/runs; -disp(['Gamma function over a 1500x1500 matrix_______________ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=rand(1500,1500); - tic; - b=erf(a); - result=result+toc; - end -end -result=result/runs; -disp(['Gaussian error function over a 1500x1500 matrix______ : ' num2str(result) ' sec.']) -clear a; clear b; result=0; -for i=1:runs - for j=1:100 - a=randn(1000,1000); - b=1:1000; - tic; - b=a\b'; - result=result+toc; - end -end -result=result/runs; -disp(['Linear regression over a 1000x1000 matrix____________ : ' num2str(result) ' sec.']) diff --git a/smop/mybench.m b/smop/mybench.m deleted file mode 100644 index 735df9dd..00000000 --- a/smop/mybench.m +++ /dev/null @@ -1,450 +0,0 @@ -function res = mybench(varargin) -% res = mybench('noOfRepeats', 3,... -% 'normalize',true,... -% 'onlyTests',[],... -% 'imagep',true) -% -% Benchmark script for MATLAB and Octave. -% Tested on Matlab 2009a and Octave 3.2.2 for Windows. -% -% Execution time of basic matrix manipulation function is tested along with -% integration, solving nonlinear equation, image processing functions ( if -% avaliable), saveing/loading matrices to a file system, for loop, -% binary operation,etc. In total 27 -% tests are performed (less if image processing functions not avaliable). -% -% All results are normilized against the results obtained -% using MATLAB 7.4.0.287 (R2007a) on Intel Mac OS X 10.4.11 (Intel Core Duo -% 2GHz, 2GB RAM) -% -% At the end, arithmetic and geometric means of the times obtained are -% calculated. All results obtained are stored in a txt file named -% results_.txt. -% -% INPUT -% noOfRepeats - int - number of times each test is executed (default 3) -% normalize - boolean - normalize results (default true). -% onlyTests - int vector - do only tests given (default [], i.e. do all tests) -% -% OUTPUT -% res - struct - normalized geometric mean . -% -% EXAMPLES -% res = mybench(); %perform all tests with default settings. -% res = mybench('noOfRepeats',10); %perform all tests 10 times. -% res = mybench('onlyTests',[1,5,8]); %perform only tests 1,5 and 8. -% res = mybench('noOfRepeats', 1,'normalize',false); % repeat 1 time -% %each tests and do not -% %normalize results. -% -% KNOWN_ISSUES -% Solving nonlinear equation produces info about termination - don't worry. -% -% Site: http:\\shortrecipes.blogspot.com -% Date: Nov 2009 -% - global IS_OCTAVE IS_IMAGE_PROCESSING - - %DEFAULT INPUT PARAMETERS. - conf = struct(... - 'noOfRepeats', 3,... - 'normalize',true,... - 'imagep',true,... - 'onlyTests',[]... - ); - - conf = getargs(conf, varargin); - - IS_OCTAVE = exist('OCTAVE_VERSION','builtin') > 0; - IS_IMAGE_PROCESSING = false; - NO_REPETITIONS = conf.noOfRepeats; - NORMALIZE_TIMES = conf.normalize; - - if exist('imrotate','file') > 0 && exist('imresize','file') > 0 ... - && exist('imerode','file') > 0 && conf.imagep == true - disp('Image processing toolbox found'); - IS_IMAGE_PROCESSING = true; - end - - if conf.noOfRepeats < 1 - conf.noOfRepeats = 1; - end - - clc; - - mytests=getBenchmarkTests(); - noOftests = length(mytests); - - %create output file - moVersio = strrep(version(),' ','_'); - outFileName = "foo.txt" %['results_',moVersio,'.txt']; - fid = fopen(outFileName,'w'); - - if NORMALIZE_TIMES - fprintf(fid,'%s\t%s\t%s\n',['Name_',moVersio],'Time','Norm_time'); - else - fprintf(fid,'%s\t%s\n',['Name_',moVersio],'Time_[s]'); - end - - avarage_time = 0; - - times_vector =[]; - times_vector1 =[]; % not normalized - - if isempty(conf.onlyTests) - doTheseTests = 1:noOftests; - else - doTheseTests = conf.onlyTests; - noOftests = length(conf.onlyTests); - end - - %loop over tests - for i=doTheseTests - - %display some info - fprintf(1,'Execute test %d/%d - %s\n',i,noOftests,... - mytests{i}.name); - if IS_OCTAVE, fflush(stdout); end - - try - %get input for a give test - x = mytests{i}.input(); - - %execute test and measure time - cumulative_time = 0; - cumulative_time1 = 0; - goldResult = 1; - for ii=1:NO_REPETITIONS - - fprintf(1,'%d ',ii); - if IS_OCTAVE, fflush(stdout); end - - t0=tic(); - mytests{i}.test(x); - t1=toc(t0); - - if isfield(mytests{i}, 'goldResult') && NORMALIZE_TIMES == true - goldResult = mytests{i}.goldResult; - end - - cumulative_time=cumulative_time+t1/goldResult; - cumulative_time1=cumulative_time1+t1; - - end - avarage_time = cumulative_time/NO_REPETITIONS; - avarage_time1 = cumulative_time1/NO_REPETITIONS; - times_vector(end+1) = avarage_time; - times_vector1(end+1) = avarage_time1; % not normalized - - catch - le = lasterror; - disp(le.message); - fprintf(1,'\n\n \t ... Skip to the next test ...\n\n'); - if IS_OCTAVE, fflush(stdout); end - continue - end - - - %some postprocessing if defined - if isfield(mytests{i}, 'post') - mytests{i}.post(); - end - - - %display some info - - fprintf(1,'\n\tTime %.2f [s]\n',avarage_time1); - if NORMALIZE_TIMES == true - fprintf(1,'\tNormalized time %.2f \n',avarage_time); - end - fprintf(1,'\n'); - if IS_OCTAVE, fflush(stdout); end - - if NORMALIZE_TIMES - fprintf(fid,'%s\t%f\t%f\n',mytests{i}.name,... - avarage_time1,avarage_time); - else - fprintf(fid,'%s\t%f\n',mytests{i}.name,... - avarage_time1); - end - end - - times_product = prod(times_vector); - times_mean = mean(times_vector); - times_geometric_mean = times_product^(1/length(times_vector) ); - - times_product1 = prod(times_vector1); % not normalized - times_mean1 = mean(times_vector1); - times_geometric_mean1 = times_product1^(1/length(times_vector1) ); - - res.norm_geometric_mean = times_geometric_mean; - res.norm_arithmetic_mean = times_mean; - res.norm_min = min(times_vector); - res.norm_max = max(times_vector); - - fprintf(1,'\n\t --- SUMMARY ---\n'); - - %display some info - - fprintf(1,'\n\tMean: geometric %.3f [s], arithmetic %.3f [s]',... - times_geometric_mean1,times_mean1); - fprintf(1,'\n\tMin %.3f [s], Max %.3f [s]\n\n',... - min(times_vector1),max(times_vector1)); - - - if NORMALIZE_TIMES == true - fprintf(1,'\tNormalized Mean: geometric %.3f, arithmetic %.3f',... - times_geometric_mean,times_mean); - fprintf(1,'\n\tNormalized Min %.3f [s], Max %.3f [s]\n\n',... - min(times_vector),max(times_vector)); - end - if IS_OCTAVE, fflush(stdout); end - - if NORMALIZE_TIMES - fprintf(fid,'%s\t%f\t%f\n','Geom_mean',times_geometric_mean1,... - times_geometric_mean); - else - fprintf(fid,'%s\t%f\t%f\n','Geom_mean',times_geometric_mean1); - end - - fclose(fid); - - disp(''); - disp(['End of test. File ',outFileName,' was created.']) - - %do some clean up - if exist('out_gray.png'), delete 'out_gray.png'; end - if exist('out_1.png'), delete 'out_1.png'; end - if exist('out_mtx'), delete 'out_mtx'; end - if exist('out_mtx.mat'), delete 'out_mtx.mat'; end - if exist('dlm.txt'), delete 'dlm.txt'; end - - clear IS_OCTAVE IS_IMAGE_PROCESSING; - -%%%%%%%%%%%%%%%%%%%%%%%%%% FUNCTIONS ********************** - -function s = getBenchmarkTests() -%the cell with tests name, test functions and input params. -%Each tests has the form of a structure with filelds 'name', 'test', -%'input', and optional 'post' nad 'goldResult' fields. -%'name' is a name that you want to give to your test. -%'test' is an anonymous function or a function handler. The execution time -% of this function is measured. -%'input' anonymous function that provides input data to a test. The time -% of this function is not measured. -% 'post' anonymous function that can do some postprocessing, e.g. cleanup. -% the time of 'post' funciton is not measured -% 'goldResult' is a result in seconds obtaiend on my computer. This times -% is used for the normalization of time scores. -% -% - global IS_OCTAVE IS_IMAGE_PROCESSING - - s={}; - - s{end+1}=struct('name','interp2','test', @(x) interp2(x,2,'spline'),... - 'input', @()rand(600),'goldResult',4.20); - - - s{end+1}=struct(... - 'name','rand',... - 'test', @(x) rand(x),... - 'input', @()4000,... - 'goldResult',1.12); - - s{end+1}=struct(... - 'name','randn',... - 'test', @(x) randn(x),... - 'input', @()4000,... - 'goldResult',0.58); - - s{end+1}=struct('name','primes','test', @(x) primes(x), 'input', @() 1e7,... - 'goldResult',0.74); - - s{end+1}=struct('name','fft2','test', @(x) fft2(x), 'input', @()rand(3000),... - 'goldResult',2.28); - - % s{end+1}=struct('name','ifft2','test', @(x) ifft2(x), 'input', @()rand(3000),... - % 'goldResult',2.979296); - - s{end+1}=struct('name','square','test', @(x) x^2, 'input', @()rand(1000),... - 'goldResult',1.35); - - s{end+1}=struct('name','inv','test', @(x) inv(x), 'input', @()rand(1000),... - 'goldResult',0.87); - - s{end+1}=struct('name','eig','test', @(x) eig(x), 'input', @()rand(1000),... - 'goldResult',9.45); - - s{end+1}=struct('name','qr','test', @(x) qr(x), 'input', @()rand(1000),... - 'goldResult',0.79); - - s{end+1}=struct('name','schur','test', @(x) schur(x), 'input', @()rand(600),... - 'goldResult',2.67); - - s{end+1}=struct('name','roots','test', @(x) roots(x), 'input', ... - @()rand(600,1),'goldResult',2.08); - - s{end+1}=struct('name','binary',... - 'test', @(x) eye(x)<1,... - 'input', @() 5000 ,... - 'goldResult',0.51); - - s{end+1}=struct('name','forLoop',... - 'test', @(x)forLoop(x),... - 'input', @() 200 ,... - 'goldResult',0.06); - - s{end+1}=struct('name','makeSparse',... - 'test', @(x) sparse(x),... - 'input', @() eye(5000) ,... - 'goldResult',0.49); - - s{end+1}=struct('name','multiplySparse',... - 'test', @(x) sparse(x)*sparse(x),... - 'input', @() eye(5000)*rand(1) ,... - 'goldResult',0.98); - - s{end+1}=struct('name','sLinearEq',... - 'test', @(x) magic(x)/rand(1,x),... - 'input', @() 2000 ,... - 'goldResult',1.94); - - % s{end+1}=struct('name','sNonLinearEq','test',... - % @(x) solveNonLinearEq(),'input', @() NaN ,'goldResult',0.07); - - s{end+1}=struct('name','saveLoadMtx','test',... - @(x) saveLoadMtx(x),'input', @() rand(1000),'goldResult',0.93); - - s{end+1}=struct('name','dlmwriteRead','test',... - @(x) dlmwriteRead(x),'input', @() rand(500),'goldResult',5.03); - - s{end+1}=struct('name','median','test', @(x) median(x(:)),... - 'input', @() rand(4000), 'goldResult',3.32); - - s{end+1}=struct('name','std','test', @(x) std(x(:)),... - 'input', @() rand(4000),'goldResult',0.84); - - % s{end+1}=struct('name','quadl','test',... - % @() quadl (@(x) x .* sin (1 ./ x) .* sqrt (abs (1 - x)), 0, 3),... - % 'input', @() NaN,'goldResult',0.038028); - - - if IS_IMAGE_PROCESSING - - s{end+1}=struct('name','doImgAndSaveAsPNG','test',... - @(x) doImgAndSaveAsPNG(x),'input',... - @() rand(1500,1500,3) ,'post', @() pause(2),... - 'goldResult',2.00 ); - - s{end+1}=struct('name','imageToGray','test', @(I) imageToGray(I),... - 'input', ... - @() imread('out_1.png'),'goldResult',0.56 ); - - s{end+1}=struct('name','imageRotate','test', @(I) imageRotate(I),... - 'input',... - @() imread('out_gray.png'), 'goldResult',2.94 ); - - s{end+1}=struct('name','imresize','test', @(I) imresize(I,1.2),... - 'input',... - @() imread('out_gray.png'), 'goldResult',1.24); - - s{end+1}=struct('name','imageFilter','test', @(I) imageFilter(I),... - 'input',... - @() imread('out_gray.png'), 'goldResult',0.20); - - s{end+1}=struct('name','imageErode','test', @(I) imageErode(I),... - 'input',... - @() imread('out_gray.png'), 'goldResult',0.46 ); - - s{end+1}=struct('name','medfilt2','test', @(I) medfilt2(I),... - 'input',... - @() magic(2000), 'goldResult',1.03 ); - - end - - % ADDITIONAL TEST FUNCTIONS -function saveLoadMtx(out_mtx) - save out_mtx; - clear out_mtx; - load out_mtx; - -function dlmwriteRead(x) - dlmwrite('dlm.txt', x); - dlmread('dlm.txt'); - - -function forLoop(x) - for i=1:x - for j=1:x - for k=1:x - i+j+k; - end - end - end - -function doImgAndSaveAsPNG(x) - %plot a surf and save it image png with 300DPI - %f=figure; - %set(f,'Visible','off'); - %surf(x); - %print(['-f',int2str(f)],'-dpng','-r200',['out_1.png']); - %close(f) - - imwrite(x,'out_1.png'); - - -function solveNonLinearEq() - [x, fval, info] = fsolve (@equationsToSolve, [1; 1;1;1]); - -function y = equationsToSolve (x) - y(1) = -2*x(1)^2 + 3*x(1)*x(2) + 4*sin(x(2)) + log(x(3)) - 6; - y(2) = 3*x(1)^2 - 2*x(2)*x(2)^2 + 3*cos(x(1)) + 4; - y(3) = 1*x(1)^2 - 2*x(1)*x(2)*x(4)^2 + 3*cos(x(1)) + 4; - y(4) = 1*x(1)^2 - 2*x(1)*x(2)*x(3)^2 + 3*cos(x(4)) + 4; - - -function imageToGray(I) - Igray = rgb2gray(I); - imwrite(Igray,'out_gray.png'); - -function imageRotate(I) - I2=imrotate(I,2); - -function imageFilter(I) - h=fspecial('sobel'); - filteredI = imfilter(I, h); - -function imageErode(I) - SE=eye(5); - erodedI = imerode(I, SE); - - - - -% Get input argumetns -function defs = getargs(defs, varglist) - l=length(varglist); - if l==0, return, end - if mod(l,2) ~=0, - disp(' !!! Odd number of parameters !!!'); - defs={}; - return - end - varnames={varglist{1:2:l}}; - varvalues={varglist{2:2:l}}; - given_vars=zeros(1,l/2); - for i=1:1:l/2 - existss=isfield(defs,varnames{i}); - given_vars(i)=existss; - end - - if min(given_vars)==0, - disp('!!! No such parameter(s):'); - disp(varnames(~given_vars)); - defs={}; - return - end - for i=1:1:l/2 - defs.(varnames{i}) = varvalues{i}; - end diff --git a/smop/mybench.py b/smop/mybench.py deleted file mode 100644 index ebd1a3e5..00000000 --- a/smop/mybench.py +++ /dev/null @@ -1,248 +0,0 @@ -# Autogenerated with SMOP version 0.26.3-15-gcf44923 -# /home/lei/.local/bin/smop -r core -r octave -o mybench.py mybench.m -from __future__ import division -from core import * -from octave import * -def mybench(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - global IS_OCTAVE,IS_IMAGE_PROCESSING - conf=struct_(char('noOfRepeats'),3,char('normalize'),true,char('imagep'),true,char('onlyTests'),[]) - conf=getargs(conf,varargin) - IS_OCTAVE=exist(char('OCTAVE_VERSION'),char('builtin')) > 0 - IS_IMAGE_PROCESSING=copy(false) - NO_REPETITIONS=conf.noOfRepeats - NORMALIZE_TIMES=conf.normalize - if exist(char('imrotate'),char('file')) > 0 and exist(char('imresize'),char('file')) > 0 and exist(char('imerode'),char('file')) > 0 and conf.imagep == true: - disp(char('Image processing toolbox found')) - IS_IMAGE_PROCESSING=copy(true) - if conf.noOfRepeats < 1: - conf.noOfRepeats = copy(1) - clc - mytests=getBenchmarkTests() - noOftests=length(mytests) - moVersio=strrep(version(),char(' '),char('_')) - outFileName=char('foo.txt') - fid=fopen(outFileName,char('w')) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%s\t%s\n'),[char('Name_'),moVersio],char('Time'),char('Norm_time')) - else: - fprintf(fid,char('%s\t%s\n'),[char('Name_'),moVersio],char('Time_[s]')) - avarage_time=0 - times_vector=matlabarray([]) - times_vector1=matlabarray([]) - if isempty(conf.onlyTests): - doTheseTests=arange(1,noOftests) - else: - doTheseTests=conf.onlyTests - noOftests=length(conf.onlyTests) - for i in doTheseTests.reshape(-1): - fprintf(1,char('Execute test %d/%d - %s\n'),i,noOftests,mytests[i].name) - if IS_OCTAVE: - fflush(stdout) - try: - x=mytests[i].input() - cumulative_time=0 - cumulative_time1=0 - goldResult=1 - for ii in arange(1,NO_REPETITIONS).reshape(-1): - fprintf(1,char('%d '),ii) - if IS_OCTAVE: - fflush(stdout) - t0=tic() - mytests[i].test(x) - t1=toc(t0) - if isfield(mytests[i],char('goldResult')) and NORMALIZE_TIMES == true: - goldResult=mytests[i].goldResult - cumulative_time=cumulative_time + t1 / goldResult - cumulative_time1=cumulative_time1 + t1 - avarage_time=cumulative_time / NO_REPETITIONS - avarage_time1=cumulative_time1 / NO_REPETITIONS - times_vector[end() + 1]=avarage_time - times_vector1[end() + 1]=avarage_time1 - finally: - pass - if isfield(mytests[i],char('post')): - mytests[i].post() - fprintf(1,char('\n\tTime %.2f [s]\n'),avarage_time1) - if NORMALIZE_TIMES == true: - fprintf(1,char('\tNormalized time %.2f \n'),avarage_time) - fprintf(1,char('\n')) - if IS_OCTAVE: - fflush(stdout) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%f\t%f\n'),mytests[i].name,avarage_time1,avarage_time) - else: - fprintf(fid,char('%s\t%f\n'),mytests[i].name,avarage_time1) - times_product=prod(times_vector) - times_mean=mean(times_vector) - times_geometric_mean=times_product ** (1 / length(times_vector)) - times_product1=prod(times_vector1) - times_mean1=mean(times_vector1) - times_geometric_mean1=times_product1 ** (1 / length(times_vector1)) - res.norm_geometric_mean = copy(times_geometric_mean) - res.norm_arithmetic_mean = copy(times_mean) - res.norm_min = copy(min(times_vector)) - res.norm_max = copy(max(times_vector)) - fprintf(1,char('\n\t --- SUMMARY ---\n')) - fprintf(1,char('\n\tMean: geometric %.3f [s], arithmetic %.3f [s]'),times_geometric_mean1,times_mean1) - fprintf(1,char('\n\tMin %.3f [s], Max %.3f [s]\n\n'),min(times_vector1),max(times_vector1)) - if NORMALIZE_TIMES == true: - fprintf(1,char('\tNormalized Mean: geometric %.3f, arithmetic %.3f'),times_geometric_mean,times_mean) - fprintf(1,char('\n\tNormalized Min %.3f [s], Max %.3f [s]\n\n'),min(times_vector),max(times_vector)) - if IS_OCTAVE: - fflush(stdout) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%f\t%f\n'),char('Geom_mean'),times_geometric_mean1,times_geometric_mean) - else: - fprintf(fid,char('%s\t%f\t%f\n'),char('Geom_mean'),times_geometric_mean1) - fclose(fid) - disp(char('')) - disp([char('End of test. File '),outFileName,char(' was created.')]) - if exist(char('out_gray.png')): - delete(char('out_gray.png')) - if exist(char('out_1.png')): - delete(char('out_1.png')) - if exist(char('out_mtx')): - delete(char('out_mtx')) - if exist(char('out_mtx.mat')): - delete(char('out_mtx.mat')) - if exist(char('dlm.txt')): - delete(char('dlm.txt')) - clear(char('IS_OCTAVE'),char('IS_IMAGE_PROCESSING')) - return res -def getBenchmarkTests(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - global IS_OCTAVE,IS_IMAGE_PROCESSING - s=cellarray([]) - s[end() + 1]=struct_(char('name'),char('rand'),char('test'),lambda x: rand(x),char('input'),lambda : 4000,char('goldResult'),1.12) - s[end() + 1]=struct_(char('name'),char('randn'),char('test'),lambda x: randn(x),char('input'),lambda : 4000,char('goldResult'),0.58) - s[end() + 1]=struct_(char('name'),char('primes'),char('test'),lambda x: primes(x),char('input'),lambda : 10000000.0,char('goldResult'),0.74) - s[end() + 1]=struct_(char('name'),char('fft2'),char('test'),lambda x: fft2(x),char('input'),lambda : rand(3000),char('goldResult'),2.28) - s[end() + 1]=struct_(char('name'),char('square'),char('test'),lambda x: x ** 2,char('input'),lambda : rand(1000),char('goldResult'),1.35) - s[end() + 1]=struct_(char('name'),char('inv'),char('test'),lambda x: inv(x),char('input'),lambda : rand(1000),char('goldResult'),0.87) - s[end() + 1]=struct_(char('name'),char('eig'),char('test'),lambda x: eig(x),char('input'),lambda : rand(1000),char('goldResult'),9.45) - s[end() + 1]=struct_(char('name'),char('qr'),char('test'),lambda x: qr(x),char('input'),lambda : rand(1000),char('goldResult'),0.79) - s[end() + 1]=struct_(char('name'),char('schur'),char('test'),lambda x: schur(x),char('input'),lambda : rand(600),char('goldResult'),2.67) - s[end() + 1]=struct_(char('name'),char('roots'),char('test'),lambda x: roots(x),char('input'),lambda : rand(600,1),char('goldResult'),2.08) - s[end() + 1]=struct_(char('name'),char('interp2'),char('test'),lambda x: interp2(x,2,char('spline')),char('input'),lambda : rand(600),char('goldResult'),4.2) - s[end() + 1]=struct_(char('name'),char('binary'),char('test'),lambda x: eye(x) < 1,char('input'),lambda : 5000,char('goldResult'),0.51) - s[end() + 1]=struct_(char('name'),char('forLoop'),char('test'),lambda x: forLoop(x),char('input'),lambda : 200,char('goldResult'),0.06) - s[end() + 1]=struct_(char('name'),char('makeSparse'),char('test'),lambda x: sparse(x),char('input'),lambda : eye(5000),char('goldResult'),0.49) - s[end() + 1]=struct_(char('name'),char('multiplySparse'),char('test'),lambda x: sparse(x) * sparse(x),char('input'),lambda : eye(5000) * rand(1),char('goldResult'),0.98) - s[end() + 1]=struct_(char('name'),char('sLinearEq'),char('test'),lambda x: magic(x) / rand(1,x),char('input'),lambda : 2000,char('goldResult'),1.94) - s[end() + 1]=struct_(char('name'),char('saveLoadMtx'),char('test'),lambda x: saveLoadMtx(x),char('input'),lambda : rand(1000),char('goldResult'),0.93) - s[end() + 1]=struct_(char('name'),char('dlmwriteRead'),char('test'),lambda x: dlmwriteRead(x),char('input'),lambda : rand(500),char('goldResult'),5.03) - s[end() + 1]=struct_(char('name'),char('median'),char('test'),lambda x: median(x[:]),char('input'),lambda : rand(4000),char('goldResult'),3.32) - s[end() + 1]=struct_(char('name'),char('std'),char('test'),lambda x: std(x[:]),char('input'),lambda : rand(4000),char('goldResult'),0.84) - if IS_IMAGE_PROCESSING: - s[end() + 1]=struct_(char('name'),char('doImgAndSaveAsPNG'),char('test'),lambda x: doImgAndSaveAsPNG(x),char('input'),lambda : rand(1500,1500,3),char('post'),lambda : pause(2),char('goldResult'),2.0) - s[end() + 1]=struct_(char('name'),char('imageToGray'),char('test'),lambda I: imageToGray(I),char('input'),lambda : imread(char('out_1.png')),char('goldResult'),0.56) - s[end() + 1]=struct_(char('name'),char('imageRotate'),char('test'),lambda I: imageRotate(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),2.94) - s[end() + 1]=struct_(char('name'),char('imresize'),char('test'),lambda I: imresize(I,1.2),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),1.24) - s[end() + 1]=struct_(char('name'),char('imageFilter'),char('test'),lambda I: imageFilter(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),0.2) - s[end() + 1]=struct_(char('name'),char('imageErode'),char('test'),lambda I: imageErode(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),0.46) - s[end() + 1]=struct_(char('name'),char('medfilt2'),char('test'),lambda I: medfilt2(I),char('input'),lambda : magic(2000),char('goldResult'),1.03) - return s -def saveLoadMtx(out_mtx=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[out_mtx].count(None)+len(args) - - save(char('out_mtx')) - clear(char('out_mtx')) - load(char('out_mtx')) - return -def dlmwriteRead(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - dlmwrite(char('dlm.txt'),x) - dlmread(char('dlm.txt')) - return -def forLoop(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - for i in arange(1,x).reshape(-1): - for j in arange(1,x).reshape(-1): - for k in arange(1,x).reshape(-1): - i + j + k - return -def doImgAndSaveAsPNG(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - imwrite(x,char('out_1.png')) - return -def solveNonLinearEq(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - x,fval,info=fsolve(equationsToSolve,[[1],[1],[1],[1]],nargout=3) - return -def equationsToSolve(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - y[1]=- 2 * x[1] ** 2 + 3 * x[1] * x[2] + 4 * sin(x[2]) + log(x[3]) - 6 - y[2]=3 * x[1] ** 2 - 2 * x[2] * x[2] ** 2 + 3 * cos(x[1]) + 4 - y[3]=1 * x[1] ** 2 - 2 * x[1] * x[2] * x[4] ** 2 + 3 * cos(x[1]) + 4 - y[4]=1 * x[1] ** 2 - 2 * x[1] * x[2] * x[3] ** 2 + 3 * cos(x[4]) + 4 - return y -def imageToGray(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - Igray=rgb2gray(I) - imwrite(Igray,char('out_gray.png')) - return -def imageRotate(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - I2=imrotate(I,2) - return -def imageFilter(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - h=fspecial(char('sobel')) - filteredI=imfilter(I,h) - return -def imageErode(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - SE=eye(5) - erodedI=imerode(I,SE) - return -def getargs(defs=None,varglist=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 2-[defs,varglist].count(None)+len(args) - - l=length(varglist) - if l == 0: - return defs - if mod(l,2) != 0: - disp(char(' !!! Odd number of parameters !!!')) - defs=cellarray([]) - return defs - varnames=cellarray([varglist[1:2:l]]) - varvalues=cellarray([varglist[2:2:l]]) - given_vars=zeros(1,l / 2) - for i in arange(1,l / 2,1).reshape(-1): - existss=isfield(defs,varnames[i]) - given_vars[i]=existss - if min(given_vars) == 0: - disp(char('!!! No such parameter(s):')) - disp(varnames[not given_vars]) - defs=cellarray([]) - return defs - for i in arange(1,l / 2,1).reshape(-1): - setattr(defs,varnames[i],varvalues[i]) - return defs - -mybench() From 59bee37981d92c1aa1986f0433efeb8165571c34 Mon Sep 17 00:00:00 2001 From: Victor Lei Date: Thu, 9 Jun 2016 12:40:39 +0300 Subject: [PATCH 3/7] m --- smop/Makefile | 60 --------------------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 smop/Makefile diff --git a/smop/Makefile b/smop/Makefile deleted file mode 100644 index 567c6855..00000000 --- a/smop/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -# for py3: make PYTHON=python3 CYTHON="cython -3" V=3.4 - -VPATH = $(SCRIPTS)/general:$(SCRIPTS)/specfun:$(SCRIPTS)/plot/util -OCTAVE = /home/lei/octave-3.8.2 -SCRIPTS = $(OCTAVE)/scripts - -CYTHON = cython -PYTHON = python -#FLAGS = -S stft.m,datenum.m -X orderfields.m,legend.m,pack.m,unpack.m,__unimplemented__.m,assert.m,optimset.m -#FLAGS = -X interp1.m,interpn.m,nargchk.m,nargoutchk.m,profexplore.m,profshow.m,quadgk.m,quadl.m -FLAGS = -Xquadgk,quadl,lcm,adaptlobstp,spline,ppval,ellipke -V = 2.7 -ALL = \*.m - -all: mybench.py - $(PYTHON) -c "import mybench ; mybench.mybench()" - -octave.py: primes.m interp2.m meshgrid.m repmat.m - $(PYTHON) main.py $^ -r core -o $@ - -#octave.py: -# find $(SCRIPTS)/general $(SCRIPTS)/specfun -name \*.m | xargs smop -v -o $@ -r core $(FLAGS) - - -go_so: solver.so - $(PYTHON) go.py - -go_py: solver.py - $(PYTHON) go.py - -go_m: solver.m - octave -q go.m - -clean: - rm -f a.* *.pyc solver.so solver.py octave.so - -solver.py: solver.m r8_random.m - $(PYTHON) main.py $^ -o $@ - -check: - $(PYTHON) octave.py - $(PYTHON) test_core.py - $(PYTHON) test_matlabarray.py - $(PYTHON) test_sparsearray.py - -$(PYTHON) test_lexer.py - $(PYTHON) main.py $(FLAGS) solver.m r8_random.m -o solver.py && python go.py - -test: - find $(SCRIPTS) -name $(ALL) | xargs python main.py $(FLAGS) - -%.c: %.py - $(CYTHON) $^ - -%.so: %.c - gcc -Wno-cpp -I /usr/include/python$V -O2 -shared -o $@ $^ - -%.py: %.m - #smop $(FLAGS) -o $@ $^ - $(PYTHON) main.py -o $@ $^ -r core -r octave - $(PYTHON) $@ From 3132b8a2f0a0028802f360bf021d31a42525e8c6 Mon Sep 17 00:00:00 2001 From: Victor Leikehman Date: Wed, 27 Jul 2016 14:50:41 +0300 Subject: [PATCH 4/7] WIP --- smop/Makefile | 2 +- smop/benchmark5/benchmark5.m | 18 ++++++++---------- smop/core.py | 3 ++- smop/mybench.m | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/smop/Makefile b/smop/Makefile index 567c6855..0d63c03a 100644 --- a/smop/Makefile +++ b/smop/Makefile @@ -35,7 +35,7 @@ clean: rm -f a.* *.pyc solver.so solver.py octave.so solver.py: solver.m r8_random.m - $(PYTHON) main.py $^ -o $@ + $(PYTHON) main.py $^ -o $@ -r core check: $(PYTHON) octave.py diff --git a/smop/benchmark5/benchmark5.m b/smop/benchmark5/benchmark5.m index 0f46b15b..2038bd3c 100644 --- a/smop/benchmark5/benchmark5.m +++ b/smop/benchmark5/benchmark5.m @@ -4,8 +4,6 @@ %* EMAIL : stefan@steinhaus-net.de * %* This program is public domain. Feel free to copy it freely. * %*************************************************************************** - -clc disp('The following benchmark program will print the average timings') disp('to calculate the functions by 3 runs.') disp(' ') @@ -38,8 +36,8 @@ result=0; a=1; for i=1:runs tic; - for x=1:15000; - for y=1:15000; + for x=1:1500; + for y=1:1500; a=a+x+y; end; end; @@ -49,7 +47,7 @@ disp(['Loop testing_________________________________________ : ' num2str(result) ' sec.']) result=0; for i=1:runs - for j=1:200 + for j=1:2 b=1+(rand(2000,2000)/100); tic; a=b.^1000; @@ -60,7 +58,7 @@ disp(['2000x2000 normal distributed random matrix^1000______ : ' num2str(result) ' sec.']) clear a; clear b; result=0; for i=1:runs - for j=1:100 + for j=1:1 a=rand(1000000,1); tic; b=sort(a); @@ -88,7 +86,7 @@ clear a; clear b; result=0; for i=1:runs - for j=1:100 + for j=1:1 a=rand(1500,1500); tic; b=det(a); @@ -99,7 +97,7 @@ disp(['Determinant of a 1500x1500 random matrix_____________ : ' num2str(result) ' sec.']) clear a; clear b; result=0; for i=1:runs - for j=1:100 + for j=1:1 a=rand(1500,1500); tic; b=inv(a); @@ -119,7 +117,7 @@ disp(['Eigenval. of a normal distr. 1200x1200 randommatrix___ : ' num2str(result) ' sec.']) clear a; clear b; result=0; for i=1:runs - for j=1:100 + for j=1:1 a=rand(1500,1500); a=a'*a; tic; @@ -131,7 +129,7 @@ disp(['Cholesky decomposition of a 1500x1500-matrix_________ : ' num2str(result) ' sec.']) clear a; clear b; result=0; for i=1:runs - for j=1:100 + for j=1:1 a=rand(1500,1500); tic; b=a'*a; diff --git a/smop/core.py b/smop/core.py index 29c4acc9..0cbbaf4b 100644 --- a/smop/core.py +++ b/smop/core.py @@ -6,7 +6,8 @@ import __builtin__ import numpy -from numpy import sqrt +from sparsearray import sparsearray as sparse +from numpy import sqrt,eye from numpy.fft import fft2 from numpy.linalg import inv from numpy.linalg import qr as _qr diff --git a/smop/mybench.m b/smop/mybench.m index 735df9dd..581b061c 100644 --- a/smop/mybench.m +++ b/smop/mybench.m @@ -242,8 +242,8 @@ s={}; - s{end+1}=struct('name','interp2','test', @(x) interp2(x,2,'spline'),... - 'input', @()rand(600),'goldResult',4.20); + %s{end+1}=struct('name','interp2','test', @(x) interp2(x,2,'spline'),... + % 'input', @()rand(600),'goldResult',4.20); s{end+1}=struct(... From 18cc37f80854501fb40efedc7141762821930953 Mon Sep 17 00:00:00 2001 From: Victor Leikehman Date: Wed, 27 Jul 2016 14:55:32 +0300 Subject: [PATCH 5/7] wip --- HACKING.rst | 91 +++++++++++++++++++++ README | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 HACKING.rst create mode 100644 README diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 00000000..07b6863d --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,91 @@ +========= +HACKING +========= + ++-----------------------------------------+-------+-------+-------+ +| |matlab |fortran|python | ++=========================================+=======+=======+=======+ +| | | | | +| A. Base-one indexing | yes | yes | no | ++-----------------------------------------+-------+-------+-------+ +| | | | | +| B. Columns-first data layout | yes | yes | no | ++-----------------------------------------+-------+-------+-------+ +| C. Auto-expanding arrays | yes | no * | yes | ++-----------------------------------------+-------+-------+-------+ +| D. Update to create | yes | no * | yes | ++-----------------------------------------+-------+-------+-------+ +| E. Assignment as copy | yes | yes | no | ++-----------------------------------------+-------+-------+-------+ + + ++-----------------------------------------+-------+-------+-------+ +| |matlab |fortran|python | ++=========================================+=======+=======+=======+ +| F. Matrices everywhere | yes | no | no | ++-----------------------------------------+-------+-------+-------+ +| G. Single subscript implies ravel | yes | | | ++-----------------------------------------+-------+-------+-------+ +| H. Broadcast | | | | ++-----------------------------------------+-------+-------+-------+ +| I. Boolean indexing | | | | ++-----------------------------------------+-------+-------+-------+ +| J. Type and rank must be known | no | yes | no | +| in compile time | | | | ++-----------------------------------------+-------+-------+-------+ + ++-----------------------------------------+-------+-------+-------+ +| |matlab |fortran|python | ++=========================================+=======+=======+=======+ +| K. Garbage collection | yes | no * | yes | ++-----------------------------------------+-------+-------+-------+ +| L. All uppercase | no | yes | no | ++-----------------------------------------+-------+-------+-------+ +| M. Structs | | | | ++-----------------------------------------+-------+-------+-------+ +| N. Interpreted | yes | no | yes | ++-----------------------------------------+-------+-------+-------+ +| P. Strings are arrays of chars | yes | no | yes | ++-----------------------------------------+-------+-------+-------+ + +C. Auto-expanding arrays + Matlab arrays are auto-magically resized on out-of-bounds + update. Deprecated, this feature is widely used in legacy code. + Supporting this feature is hard both in python and in fortran. + + In fortran, the pattern should be somehow (how exactly?) detected in + compile-time. In python ``__setitem__`` hides ``try-catch``, with + ``resize`` called inside ``catch``. Is try-catch in fortran? + + In numpy out-of-bounds assignment is an error. In smop, + out-of-bounds assignment is supported for row and column matrices + and their generalizations having shape + + [1 1 ... N ... 1] + + These arrays may be resized along their only non-singular dimension. + For other matrices, new columns can be added to F_CONTIGUOUS arrays, + and new rows can be added to C_CONTIGUOUS arrays. + + Finally, scalar array of any dimension, having shape + + [1 1 ... 1] + + can be resized along any dimension. + +D. Update to create + In matlab, arrays may be created by updating a non existent array, + as in the example:: + + >>> clear a + >>> a(17)=42 + + This unique feature is not supported by smop, but can be worked + around by inserting assignments into the original matlab code:: + + >>> a=[] + >>> a(17_=42 + +------------------------------------- + +.. vim:tw=70 diff --git a/README b/README new file mode 100644 index 00000000..2febcaf0 --- /dev/null +++ b/README @@ -0,0 +1,228 @@ +Summary +------- + +SMOP stands for Small Matlab/Octave to Python compiler. It is +supposed to help those who attempt migration from Matlab to Python. +Despite the similarities between the two languages, there are enough +differences to make manual translation too time consuming. + +SMOP is not a polished product, nor a replacement to Octave and +Matlab. Taking into account its size (less than 3000 lines), this is +not surprizing. There are no toolboxes. Small everyday functions +(max, length, etc.) are recognized and supported, but that's all. + +SMOP is written in Python, using PLY -- Python Lex/Yacc for lexical +analysis and parsing, and numpy for runtime environment. SMOP is +platform-independent, but is tested only on Linux. It is a +command-line utility. + +Example +------- + +It is possible to run an example without installing smop. Just unzip +it somewhere, and `cd` there. In your current directory you will find +a bunch of .py files and a file named fastsolver.m. It is taken from +the winning submission to Matlab programming competition in 2004 +(Moving Furniture +http://www.mathworks.cn/matlabcentral/contest/contests/12/submissions/29989). + +Now type `python main.py fastsolver.m -o fastsolver.py`. If you don't +specify the output file with -o option, it is written to `a.py`. +Each time a function is translated, its name is written. + +.. code:: sh + + lei@fuji:~/smop/smop$ python main.py fastsolver.m + fastsolver.m + solver + cbest + mainsolver + imoves + easysolver + localfiddler + findoverlaps + dijkstra + improve + TLL79 + solverA + solver1 + movefrompos + onemove + solver2 + SearchPath + Faster10IntReps2 + matrixsolver + outoftheway + ismember1 + ismember2 + setdiff + unique + sub2ind + randperm + perms + itTakesAThief + movefurniture + findshortestpath + dealWall1 + lei@fuji:~/smop/smop$ + +The entire submission contains 2093 lines, and it is automatically +translated to Python by smop. These are the good news. The bad news +are that generating the code is not enough to run the program, so +there are no performance numbers yet. + +#. While the submission itself --- the solver program --- does not use + graphics, the envelope code that is responsible to run the + submission, collect and display the results, does. So about 100 + lines of the envelope must be rewritten by hand. + +#. Many standard functions are not yet implemented --- rand, find, + and others. They are on the issues list. + +#. Some matlab constructs, especially creating arrays by out of bound + assignment, are used in the submission, but not yet supported by + smop. Meanwhile, these lines should be rewritten in the Matlab + code. + +.. code:: matlab + + 01 function moves=solver(A,B,w0) + 02 [moves,optmove,optscore]=cbest(A,B,w0); + 03 curscore=sum(w0(moves(:,1))); + 04 lots=1; + 05 if length(moves)-optmove<20||curscore/optscore<1.05 + 06 lots=2; return + 07 else + 08 lenw=length(w0); + 09 [xx,nseq]=sort(rand(1,lenw)); + 10 A1=A; + 11 B1=B; + 12 w01=w0; + 13 for i=1:lenw + 14 A1(A==i)=nseq(i); + 15 B1(B==i)=nseq(i); + 16 w01(nseq(i))=w0(i); + 17 end; + 18 [moves2,optmove,optscore]=cbest(A1,B1,w01); + +becomes + +.. code:: python + + 01 def solver(A,B,w0): + 02 moves,optmove,optscore = cbest(A,B,w0) + 03 curscore=np.sum(w0[(moves[:,0]-1)]) + 04 lots=1 + 05 if max(moves.shape) - optmove < 20 or curscore / optscore < 1.05: + 06 lots=2 + 07 return moves + 08 else: + 09 lenw=max(w0.shape) + 10 xx,nseq=sort(rand(1,lenw)) + 11 A1=A + 12 B1=B + 13 w01=w0 + 14 for i in range(1,(lenw+1)): + 15 A1[A == i]=nseq[(i-1)] + 16 B1[B == i]=nseq[(i-1)] + 17 w01[(nseq[(i-1)]-1)]=w0[(i-1)] + 18 moves2,optmove,optscore = cbest(A1,B1,w01) + +Now some random notes. + +#. Line 03. Functions vs. arrays ambiguity is correctly resolved: + for example, `sum` is a function, but `w0` and `moves` are + arrays. + +#. Line 09, Matlab function `length` is correctly inlined as + `max(w0.shape)` --- that is the maximum of the array dimensions. + +#. For some functions, such as `abs`, `max`, and others, there are + both a builtin version and a different numpy version, and it is + important to use the right one. In line 03 `np.sum` is used, but + in line 09 builtin `max`. This is correct. + +#. Line 10. Functions rand and sort are not yet implemented. + +#. Lines 15-16. There is some support to boolean indexing. + +#. Lines 15-17. Array subscripts are modified to start with zero + index. Note that if all subscripts were decremented, it would + break boolean indexing. + +#. Line 13. Range specification in Matlab includes the upper bound. + In Python, it doesn't. So `for i=1:lenw` becomes `for i in + range(1,(lenw+1))`. Extra parentheses are known as issue #1. + +----------------------------------------------------------- + +The table below tries to summarize various features. + ++------------------------+----------------------------------+ +| Implemented features | | ++========================+==================================+ +| Lexical and syntactical| Mostly complete, including | +| analysis | some weird Matlab features | ++------------------------+----------------------------------+ +| Name resolution | For each occurrence of a | +| | variable, find a set of its | +| | possible definitions | ++------------------------+----------------------------------+ +| Inlining of small | | +| functions | | ++------------------------+----------------------------------+ +| Array subscripts | Also, end subscript implemented | +| translated from 1-based| | +| (Matlab and Fortran | | +| style) to 0-based (C | | +| and Python style) | | ++------------------------+----------------------------------+ +| from:step:to | | +| translated to | | +| from:to:step | | ++------------------------+----------------------------------+ +| Upper bound is n+1 | | ++------------------------+----------------------------------+ + ++------------------------+----------------------------------+ +| Unimplemented features | | +| | | ++========================+==================================+ +| Structs | | +| | To be implemented as soon as cc | +| | possible. | ++------------------------+----------------------------------+ +| Arrays silently become | In some cases it may break the | +| C=style (rows first). | code. Not detected. | ++------------------------+----------------------------------+ +| Function handles and | Handles break the heuristic that | +| lambda expressions | tells between function calls and | +| | array references. | ++------------------------+----------------------------------+ +| Graphics, | Never | ++------------------------+----------------------------------+ +| Auto-expanding arrays | Unlike other languages, matlab | +| | allows out-of-bounds assignment. | +| | As MathWorks tries to phase out | +| | this feature, there is a lot of | +| | legacy code depending on it. | ++------------------------+----------------------------------+ +| Sparse matrices | Have good chances of being | +| | implemented, especially taking | +| | into account that scipy have | +| | several implementations to choose| +| | from. | ++------------------------+----------------------------------+ +| Full support for | For example, x(x>0.5) = 1 | +| boolean indexing. | works, but y=x>0.5; x(y)=1 | +| Currently, some | does not work. | +| expressions don't work | | +| | | ++------------------------+----------------------------------+ +| Command syntax | Too complex to support | ++------------------------+----------------------------------+ +| Type, rank and shape | | +| inference | | ++------------------------+----------------------------------+ +| Strings | | ++------------------------+----------------------------------+ \ No newline at end of file From 67d5d01f7563f897346ee63fe3ff8559998b3d94 Mon Sep 17 00:00:00 2001 From: Victor Leikehman Date: Wed, 27 Jul 2016 14:57:45 +0300 Subject: [PATCH 6/7] WIP --- smop/mybench.py | 248 ------------------------------------------------ 1 file changed, 248 deletions(-) delete mode 100644 smop/mybench.py diff --git a/smop/mybench.py b/smop/mybench.py deleted file mode 100644 index ebd1a3e5..00000000 --- a/smop/mybench.py +++ /dev/null @@ -1,248 +0,0 @@ -# Autogenerated with SMOP version 0.26.3-15-gcf44923 -# /home/lei/.local/bin/smop -r core -r octave -o mybench.py mybench.m -from __future__ import division -from core import * -from octave import * -def mybench(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - global IS_OCTAVE,IS_IMAGE_PROCESSING - conf=struct_(char('noOfRepeats'),3,char('normalize'),true,char('imagep'),true,char('onlyTests'),[]) - conf=getargs(conf,varargin) - IS_OCTAVE=exist(char('OCTAVE_VERSION'),char('builtin')) > 0 - IS_IMAGE_PROCESSING=copy(false) - NO_REPETITIONS=conf.noOfRepeats - NORMALIZE_TIMES=conf.normalize - if exist(char('imrotate'),char('file')) > 0 and exist(char('imresize'),char('file')) > 0 and exist(char('imerode'),char('file')) > 0 and conf.imagep == true: - disp(char('Image processing toolbox found')) - IS_IMAGE_PROCESSING=copy(true) - if conf.noOfRepeats < 1: - conf.noOfRepeats = copy(1) - clc - mytests=getBenchmarkTests() - noOftests=length(mytests) - moVersio=strrep(version(),char(' '),char('_')) - outFileName=char('foo.txt') - fid=fopen(outFileName,char('w')) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%s\t%s\n'),[char('Name_'),moVersio],char('Time'),char('Norm_time')) - else: - fprintf(fid,char('%s\t%s\n'),[char('Name_'),moVersio],char('Time_[s]')) - avarage_time=0 - times_vector=matlabarray([]) - times_vector1=matlabarray([]) - if isempty(conf.onlyTests): - doTheseTests=arange(1,noOftests) - else: - doTheseTests=conf.onlyTests - noOftests=length(conf.onlyTests) - for i in doTheseTests.reshape(-1): - fprintf(1,char('Execute test %d/%d - %s\n'),i,noOftests,mytests[i].name) - if IS_OCTAVE: - fflush(stdout) - try: - x=mytests[i].input() - cumulative_time=0 - cumulative_time1=0 - goldResult=1 - for ii in arange(1,NO_REPETITIONS).reshape(-1): - fprintf(1,char('%d '),ii) - if IS_OCTAVE: - fflush(stdout) - t0=tic() - mytests[i].test(x) - t1=toc(t0) - if isfield(mytests[i],char('goldResult')) and NORMALIZE_TIMES == true: - goldResult=mytests[i].goldResult - cumulative_time=cumulative_time + t1 / goldResult - cumulative_time1=cumulative_time1 + t1 - avarage_time=cumulative_time / NO_REPETITIONS - avarage_time1=cumulative_time1 / NO_REPETITIONS - times_vector[end() + 1]=avarage_time - times_vector1[end() + 1]=avarage_time1 - finally: - pass - if isfield(mytests[i],char('post')): - mytests[i].post() - fprintf(1,char('\n\tTime %.2f [s]\n'),avarage_time1) - if NORMALIZE_TIMES == true: - fprintf(1,char('\tNormalized time %.2f \n'),avarage_time) - fprintf(1,char('\n')) - if IS_OCTAVE: - fflush(stdout) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%f\t%f\n'),mytests[i].name,avarage_time1,avarage_time) - else: - fprintf(fid,char('%s\t%f\n'),mytests[i].name,avarage_time1) - times_product=prod(times_vector) - times_mean=mean(times_vector) - times_geometric_mean=times_product ** (1 / length(times_vector)) - times_product1=prod(times_vector1) - times_mean1=mean(times_vector1) - times_geometric_mean1=times_product1 ** (1 / length(times_vector1)) - res.norm_geometric_mean = copy(times_geometric_mean) - res.norm_arithmetic_mean = copy(times_mean) - res.norm_min = copy(min(times_vector)) - res.norm_max = copy(max(times_vector)) - fprintf(1,char('\n\t --- SUMMARY ---\n')) - fprintf(1,char('\n\tMean: geometric %.3f [s], arithmetic %.3f [s]'),times_geometric_mean1,times_mean1) - fprintf(1,char('\n\tMin %.3f [s], Max %.3f [s]\n\n'),min(times_vector1),max(times_vector1)) - if NORMALIZE_TIMES == true: - fprintf(1,char('\tNormalized Mean: geometric %.3f, arithmetic %.3f'),times_geometric_mean,times_mean) - fprintf(1,char('\n\tNormalized Min %.3f [s], Max %.3f [s]\n\n'),min(times_vector),max(times_vector)) - if IS_OCTAVE: - fflush(stdout) - if NORMALIZE_TIMES: - fprintf(fid,char('%s\t%f\t%f\n'),char('Geom_mean'),times_geometric_mean1,times_geometric_mean) - else: - fprintf(fid,char('%s\t%f\t%f\n'),char('Geom_mean'),times_geometric_mean1) - fclose(fid) - disp(char('')) - disp([char('End of test. File '),outFileName,char(' was created.')]) - if exist(char('out_gray.png')): - delete(char('out_gray.png')) - if exist(char('out_1.png')): - delete(char('out_1.png')) - if exist(char('out_mtx')): - delete(char('out_mtx')) - if exist(char('out_mtx.mat')): - delete(char('out_mtx.mat')) - if exist(char('dlm.txt')): - delete(char('dlm.txt')) - clear(char('IS_OCTAVE'),char('IS_IMAGE_PROCESSING')) - return res -def getBenchmarkTests(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - global IS_OCTAVE,IS_IMAGE_PROCESSING - s=cellarray([]) - s[end() + 1]=struct_(char('name'),char('rand'),char('test'),lambda x: rand(x),char('input'),lambda : 4000,char('goldResult'),1.12) - s[end() + 1]=struct_(char('name'),char('randn'),char('test'),lambda x: randn(x),char('input'),lambda : 4000,char('goldResult'),0.58) - s[end() + 1]=struct_(char('name'),char('primes'),char('test'),lambda x: primes(x),char('input'),lambda : 10000000.0,char('goldResult'),0.74) - s[end() + 1]=struct_(char('name'),char('fft2'),char('test'),lambda x: fft2(x),char('input'),lambda : rand(3000),char('goldResult'),2.28) - s[end() + 1]=struct_(char('name'),char('square'),char('test'),lambda x: x ** 2,char('input'),lambda : rand(1000),char('goldResult'),1.35) - s[end() + 1]=struct_(char('name'),char('inv'),char('test'),lambda x: inv(x),char('input'),lambda : rand(1000),char('goldResult'),0.87) - s[end() + 1]=struct_(char('name'),char('eig'),char('test'),lambda x: eig(x),char('input'),lambda : rand(1000),char('goldResult'),9.45) - s[end() + 1]=struct_(char('name'),char('qr'),char('test'),lambda x: qr(x),char('input'),lambda : rand(1000),char('goldResult'),0.79) - s[end() + 1]=struct_(char('name'),char('schur'),char('test'),lambda x: schur(x),char('input'),lambda : rand(600),char('goldResult'),2.67) - s[end() + 1]=struct_(char('name'),char('roots'),char('test'),lambda x: roots(x),char('input'),lambda : rand(600,1),char('goldResult'),2.08) - s[end() + 1]=struct_(char('name'),char('interp2'),char('test'),lambda x: interp2(x,2,char('spline')),char('input'),lambda : rand(600),char('goldResult'),4.2) - s[end() + 1]=struct_(char('name'),char('binary'),char('test'),lambda x: eye(x) < 1,char('input'),lambda : 5000,char('goldResult'),0.51) - s[end() + 1]=struct_(char('name'),char('forLoop'),char('test'),lambda x: forLoop(x),char('input'),lambda : 200,char('goldResult'),0.06) - s[end() + 1]=struct_(char('name'),char('makeSparse'),char('test'),lambda x: sparse(x),char('input'),lambda : eye(5000),char('goldResult'),0.49) - s[end() + 1]=struct_(char('name'),char('multiplySparse'),char('test'),lambda x: sparse(x) * sparse(x),char('input'),lambda : eye(5000) * rand(1),char('goldResult'),0.98) - s[end() + 1]=struct_(char('name'),char('sLinearEq'),char('test'),lambda x: magic(x) / rand(1,x),char('input'),lambda : 2000,char('goldResult'),1.94) - s[end() + 1]=struct_(char('name'),char('saveLoadMtx'),char('test'),lambda x: saveLoadMtx(x),char('input'),lambda : rand(1000),char('goldResult'),0.93) - s[end() + 1]=struct_(char('name'),char('dlmwriteRead'),char('test'),lambda x: dlmwriteRead(x),char('input'),lambda : rand(500),char('goldResult'),5.03) - s[end() + 1]=struct_(char('name'),char('median'),char('test'),lambda x: median(x[:]),char('input'),lambda : rand(4000),char('goldResult'),3.32) - s[end() + 1]=struct_(char('name'),char('std'),char('test'),lambda x: std(x[:]),char('input'),lambda : rand(4000),char('goldResult'),0.84) - if IS_IMAGE_PROCESSING: - s[end() + 1]=struct_(char('name'),char('doImgAndSaveAsPNG'),char('test'),lambda x: doImgAndSaveAsPNG(x),char('input'),lambda : rand(1500,1500,3),char('post'),lambda : pause(2),char('goldResult'),2.0) - s[end() + 1]=struct_(char('name'),char('imageToGray'),char('test'),lambda I: imageToGray(I),char('input'),lambda : imread(char('out_1.png')),char('goldResult'),0.56) - s[end() + 1]=struct_(char('name'),char('imageRotate'),char('test'),lambda I: imageRotate(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),2.94) - s[end() + 1]=struct_(char('name'),char('imresize'),char('test'),lambda I: imresize(I,1.2),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),1.24) - s[end() + 1]=struct_(char('name'),char('imageFilter'),char('test'),lambda I: imageFilter(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),0.2) - s[end() + 1]=struct_(char('name'),char('imageErode'),char('test'),lambda I: imageErode(I),char('input'),lambda : imread(char('out_gray.png')),char('goldResult'),0.46) - s[end() + 1]=struct_(char('name'),char('medfilt2'),char('test'),lambda I: medfilt2(I),char('input'),lambda : magic(2000),char('goldResult'),1.03) - return s -def saveLoadMtx(out_mtx=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[out_mtx].count(None)+len(args) - - save(char('out_mtx')) - clear(char('out_mtx')) - load(char('out_mtx')) - return -def dlmwriteRead(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - dlmwrite(char('dlm.txt'),x) - dlmread(char('dlm.txt')) - return -def forLoop(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - for i in arange(1,x).reshape(-1): - for j in arange(1,x).reshape(-1): - for k in arange(1,x).reshape(-1): - i + j + k - return -def doImgAndSaveAsPNG(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - imwrite(x,char('out_1.png')) - return -def solveNonLinearEq(*args,**kwargs): - varargin = cellarray(args) - nargin = 0-[].count(None)+len(args) - - x,fval,info=fsolve(equationsToSolve,[[1],[1],[1],[1]],nargout=3) - return -def equationsToSolve(x=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[x].count(None)+len(args) - - y[1]=- 2 * x[1] ** 2 + 3 * x[1] * x[2] + 4 * sin(x[2]) + log(x[3]) - 6 - y[2]=3 * x[1] ** 2 - 2 * x[2] * x[2] ** 2 + 3 * cos(x[1]) + 4 - y[3]=1 * x[1] ** 2 - 2 * x[1] * x[2] * x[4] ** 2 + 3 * cos(x[1]) + 4 - y[4]=1 * x[1] ** 2 - 2 * x[1] * x[2] * x[3] ** 2 + 3 * cos(x[4]) + 4 - return y -def imageToGray(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - Igray=rgb2gray(I) - imwrite(Igray,char('out_gray.png')) - return -def imageRotate(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - I2=imrotate(I,2) - return -def imageFilter(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - h=fspecial(char('sobel')) - filteredI=imfilter(I,h) - return -def imageErode(I=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 1-[I].count(None)+len(args) - - SE=eye(5) - erodedI=imerode(I,SE) - return -def getargs(defs=None,varglist=None,*args,**kwargs): - varargin = cellarray(args) - nargin = 2-[defs,varglist].count(None)+len(args) - - l=length(varglist) - if l == 0: - return defs - if mod(l,2) != 0: - disp(char(' !!! Odd number of parameters !!!')) - defs=cellarray([]) - return defs - varnames=cellarray([varglist[1:2:l]]) - varvalues=cellarray([varglist[2:2:l]]) - given_vars=zeros(1,l / 2) - for i in arange(1,l / 2,1).reshape(-1): - existss=isfield(defs,varnames[i]) - given_vars[i]=existss - if min(given_vars) == 0: - disp(char('!!! No such parameter(s):')) - disp(varnames[not given_vars]) - defs=cellarray([]) - return defs - for i in arange(1,l / 2,1).reshape(-1): - setattr(defs,varnames[i],varvalues[i]) - return defs - -mybench() From d570edff9ad0375df1a72e0f62d74e9ba7575b19 Mon Sep 17 00:00:00 2001 From: Victor Leikehman Date: Wed, 27 Jul 2016 17:53:00 +0300 Subject: [PATCH 7/7] merge --- smop/backend.py | 413 +++++++++-------- smop/callgraph.py | 54 +-- smop/core.py | 51 ++- smop/lexer.py | 120 +++-- smop/main.py | 234 +++------- smop/node.py | 59 ++- smop/options.py | 100 +++- smop/parse.py | 1029 +++++++++++++++++++++++------------------- smop/reord.py | 4 + smop/resolve.py | 230 +++++----- smop/test_lexer.py | 1 + smop/test_primes.py | 2 + smop/test_runtime.py | 132 ++++++ smop/test_solver.py | 34 ++ 14 files changed, 1364 insertions(+), 1099 deletions(-) create mode 100644 smop/reord.py create mode 100644 smop/test_primes.py create mode 100644 smop/test_runtime.py create mode 100644 smop/test_solver.py diff --git a/smop/backend.py b/smop/backend.py index 37ed86e6..b04a20e8 100644 --- a/smop/backend.py +++ b/smop/backend.py @@ -1,5 +1,5 @@ # smop -- Simple Matlab to Python compiler -# Copyright 2011-2014 Victor Leikehman +# Copyright 2011-2016 Victor Leikehman """ Calling conventions: @@ -29,6 +29,8 @@ ".^": "**", "./": "/", ".*": "*", + ".*=" : "*", + "./=" : "/", } def backend(t,*args): @@ -47,78 +49,34 @@ def _backend(self,level=0): #import pdb; pdb.set_trace() return "cat(%s)" % self.args[0]._backend() -@extend(node.cellarrayref) -def _backend(self,level=0): - return "%s[%s]" % (self.func_expr._backend(), - self.args._backend()) -@extend(node.cellarray) -def _backend(self,level=0): - return "cellarray([%s])" % self.args._backend() - -#@extend(node.concat_list) -#def _backend(self,level=0): -# return ";".join([t._backend() for t in self]) - -@extend(node.ravel) -def _backend(self,level=0): - return "%s.ravel()" % self.args[0]._backend() - -@extend(node.transpose) -def _backend(self,level=0): - return "%s.T" % self.args[0]._backend() - -@extend(node.expr_stmt) -def _backend(self,level=0): - return self.expr._backend() - -@extend(node.return_stmt) -def _backend(self,level=0): - if not self.ret: - return "return" - else: - return "return %s" % self.ret._backend() - -@extend(node.continue_stmt) -def _backend(self,level=0): - return "continue" +# Sometimes user's variable names in the matlab code collide with Python +# reserved words and constants. We handle this in the backend rather than in +# the lexer, to keep the target language separate from the lexer code. -@extend(node.global_stmt) -def _backend(self,level=0): - return "global %s" % self.global_list._backend() +# Some names, such as matlabarray, may collide with the user defined names. +# Both cases are solved by appending a trailing underscore to the user's names. -@extend(node.global_list) -def _backend(self,level=0): - return ",".join([t._backend() for t in self]) - -@extend(node.break_stmt) -def _backend(self,level=0): - return "break" - -@extend(node.string) -def _backend(self,level=0): - if self.value.find("'")+1: - return 'char("%s")' % (self.value) - else: - return "char('%s')" % (self.value) - -@extend(node.number) -def _backend(self,level=0): - #if type(self.value) == int: - # return "%s.0" % self.value - return str(self.value) +reserved = set( + """ + and assert break class continue + def del elif else except + exec finally for from global + if import in is lambda + not or pass print raise + return try while + + Data Float Int Numeric Oxphys + array close float int input + open range type write zeros + + len + """.split()) -@extend(node.logical) -def _backend(self,level=0): - if self.value == 0: - return "false" - else: - return "true" + #acos asin atan cos e + #exp fabs floor log log10 + #pi sin sqrt tan -# @extend(node.range) -# def _backend(self,level=0): -# i = node.ident.new("I") -# return "[ (%s, %s=%s,%s) ]" % (i,i,self.args[0],self.args[1]) - + @extend(node.add) def _backend(self,level=0): if (self.args[0].__class__ is node.number and @@ -128,12 +86,56 @@ def _backend(self,level=0): else: return "(%s+%s)" % (self.args[0]._backend(), self.args[1]._backend()) - -@extend(node.sub) + +@extend(node.arrayref) def _backend(self,level=0): - return "(%s-%s)" % (self.args[0]._backend(), - self.args[1]._backend()) - + fmt = "%s[%s]" + return fmt % (self.func_expr._backend(), + self.args._backend()) + +@extend(node.break_stmt) +def _backend(self,level=0): + return "break" + +@extend(node.builtins) +def _backend(self,level=0): + #if not self.ret: + return "%s(%s)" % (self.__class__.__name__, + self.args._backend()) + +@extend(node.cellarray) +def _backend(self,level=0): + return "cellarray([%s])" % self.args._backend() + +@extend(node.cellarrayref) +def _backend(self,level=0): + return "%s[%s]" % (self.func_expr._backend(), + self.args._backend()) + +@extend(node.comment_stmt) +def _backend(self,level=0): + s = self.value.strip() + if not s: + return "" + if s[0] in "%#": + return s.replace("%","#") + #return self.value + assert 0 + +@extend(node.concat_list) +def _backend(self,level=0): + #import pdb; pdb.set_trace() + return ",".join(["[%s]"%t._backend() for t in self]) + +@extend(node.continue_stmt) +def _backend(self,level=0): + return "continue" + +@extend(node.dot) +def _backend(self,level=0): + return "%s.dot(%s)" % (self.args[0]._backend(), + self.args[1]._backend()) + @extend(node.expr) def _backend(self,level=0): if self.op == '@': # FIXME @@ -195,21 +197,35 @@ def _backend(self,level=0): return ret+"%s(%s)" % (self.op, ",".join([t._backend() for t in self.args])) -@extend(node.arrayref) + +@extend(node.expr_list) def _backend(self,level=0): -# if (len(self.args) == 1 and not -# (self.args[0].__class__== node.expr and self.args[0].op=="::")): -# fmt = "%s(%s)" -# else: - if options.subscripts == "round": - fmt = "%s(%s)" - elif options.subscripts == "square": - fmt = "%s[%s]" - else: - assert False, options.subscripts - return fmt % (self.func_expr._backend(), - self.args._backend()) + return ",".join([t._backend() for t in self]) + +@extend(node.expr_stmt) +def _backend(self,level=0): + return self.expr._backend() + +@extend(node.for_stmt) +def _backend(self,level=0): + fmt = "for %s in %s.reshape(-1):%s" + return fmt % (self.ident._backend(), + self.expr._backend(), + self.stmt_list._backend(level+1)) + +@extend(node.func_stmt) +def _backend(self,level=-1): + if len(self.args) == 1 and self.args[0].name == "varargin": + s = "\ndef %s(*args):" % self.ident._backend() + s += "\n varargin = cellarray(args)" + return s + + s = "\ndef %s(%s*args,**kwargs):" + t = self.ident._backend() + u = ",".join(["%s=None" % a for a in self.args if isinstance(a,node.ident)]) + return s % (t, (u+",") if u else "") + @extend(node.funcall) def _backend(self,level=0): #import pdb; pdb.set_trace() @@ -224,96 +240,108 @@ def _backend(self,level=0): self.args._backend(), self.nargout) + +@extend(node.global_list) +def _backend(self,level=0): + return ",".join([t._backend() for t in self]) + +@extend(node.ident) +def _backend(self,level=0): + return self.name if self.name not in reserved else self.name+'_' + +@extend(node.if_stmt) +def _backend(self,level=0): + s = "if %s:%s" % (self.cond_expr._backend(), + self.then_stmt._backend(level+1)) + if self.else_stmt: + # Eech. This should have been handled in the parser. + if self.else_stmt.__class__ == node.if_stmt: + self.else_stmt = node.stmt_list([self.else_stmt]) + s += "\n"+indent*level + s += "else:%s" % self.else_stmt._backend(level+1) + return s + +@extend(node.lambda_expr) +def _backend(self,level=0): + return 'lambda %s: %s' % (self.args._backend(), + self.ret._backend()) + @extend(node.let) def _backend(self,level=0): -# if options.line_numbering: -# s = "# %d\n" % self.lineno + level*indent -# else: -# s = '' + if options.line_numbers: + s = "# %s:%d\n%s" % (options.filename, + self.lineno, + level*indent) + else: + s = '' #if self.args.__class__ is node.funcall: # self.args.nargout = self.nargout if self.ret.__class__ is node.expr and self.ret.op == "." : try: if self.ret.args[1].op == 'parens': - s = "setattr(%s,%s,%s)" % (self.ret.args[0]._backend(), + s += "setattr(%s,%s,%s)" % (self.ret.args[0]._backend(), self.ret.args[1].args[0]._backend(), self.args._backend()) except: - s = "%s%s = copy(%s)" % (self.ret.args[0]._backend(), + s += "%s%s = copy(%s)" % (self.ret.args[0]._backend(), self.ret.args[1]._backend(), self.args._backend()) elif (self.ret.__class__ is node.ident and self.args.__class__ is node.ident): - s = "%s=copy(%s)" % (self.ret._backend(), + s += "%s=copy(%s)" % (self.ret._backend(), self.args._backend()) else: - s = "%s=%s" % (self.ret._backend(), + s += "%s=%s" % (self.ret._backend(), self.args._backend()) return s - -@extend(node.expr_list) + +@extend(node.logical) def _backend(self,level=0): - return ",".join([t._backend() for t in self]) - -@extend(node.concat_list) + if self.value == 0: + return "false" + else: + return "true" + +@extend(node.matrix) def _backend(self,level=0): - #import pdb; pdb.set_trace() - return ",".join(["[%s]"%t._backend() for t in self]) - -# @extend(node.call_stmt) -# def _backend(self,level=0): -# return "CALL %s(%s,%s)" % (self.func_expr._backend(), -# self.args._backend(), -# self.ret._backend()) - -fortran_type = { - '@': '***', - 'd': 'DOUBLE PRECISION', - 'i': 'INTEGER', - 'l': 'LOGICAL', - 'c': 'CHARACTER', -} - -# def decl__backend(i): -# assert isinstance(i,ident) -# try: -# if i._rank() == 0: -# return "%s :: %s\n" % (fortran_type[i._type()], -# i) -# return ("%s,DIMENSION(%s),ALLOCATABLE :: %s\n" % -# (fortran_type[i._type()], -# ",".join([":" for j in range(i._rank())]), i)) -# except: -# return "??? :: %s\n" % i -@extend(node.function) -def _backend(self,level=0): - s = self.head._backend(level) - t = self.body._backend(level+1) - return "%s%s" % (s,t) - - -# Sometimes variable names collide with _python reserved -# words and constants. We handle this in the _backend rather than in -# the lexer, to keep the target language separate from -# the lexer code. -reserved = set( - """ - abs and apply as assert basestring bin bool break buffer bytearray - callable chr class classmethod cmp coerce compile complex continue copyright - credits def del delattr dict dir divmod elif Ellipsis else enumerate eval - except exec execfile exit False file filter finally float for format from - frozenset getattr global globals hasattr hash help hex id if import __import__ - in input int intern is isinstance issubclass iter lambda len license list - locals long map memoryview next None not not NotImplemented object oct - open or ord pass pow print property quit raise range raw_input reduce reload - repr return reversed round set setattr slice sorted staticmethod str sum super - True try tuple type unichr unicode vars while with xrange yield zip struct - """.split()) - -@extend(node.ident) + # TODO empty array has shape of 0 0 in matlab + # size([]) + # 0 0 + if not self.args: + return "[]" + elif any(a.__class__ is node.string for a in self.args): + return " + ".join(a._backend() for a in self.args) + else: + #import pdb; pdb.set_trace() + return "cat(%s)" % self.args[0]._backend() + +@extend(node.null_stmt) def _backend(self,level=0): - return self.name if self.name not in reserved else self.name+'_' + return "" + +@extend(node.number) +def _backend(self,level=0): + #if type(self.value) == int: + # return "%s.0" % self.value + return str(self.value) + +@extend(node.persistent_stmt) #FIXME +@extend(node.global_stmt) +def _backend(self,level=0): + return "global %s" % self.global_list._backend() + +@extend(node.ravel) +def _backend(self,level=0): + return "%s.ravel()" % self.args[0]._backend() + +@extend(node.return_stmt) +def _backend(self,level=0): + if not self.ret: + return "return" + else: + return "return %s" % self.ret._backend() + @extend(node.stmt_list) def _backend(self,level=0): sep = "\n"+indent*level @@ -322,55 +350,25 @@ def _backend(self,level=0): else: return sep+"pass" -@extend(node.func_decl) -def _backend(self,level=0): - if self.args and isinstance(self.args[-1],node.ident) and self.args[-1].name == "varargin": - del self.args[-1] - s = ",".join(["%s=None" % a for a in self.args if isinstance(a,node.ident)]+["*args,**kwargs"]) - s = "def %s(%s):\n" % (self.ident._backend(), s) - s += ' nargout = kwargs["nargout"] if kwargs else None\n' - s += ' varargin = cellarray(args)\n' - s += " nargin = %d-[%s].count(None)+len(args)\n" % (len(self.args), - ",".join([(a._backend() if isinstance(a,node.ident) else a.ret._backend()) - for a in self.args])) # sic: a.name - return s -""" -@extend(node.allocate_stmt) -def _backend(self,level=0): - s = "ALLOCATE (%s(%s))" % (self.ident._backend(), - self.args._backend()) - return s -""" - -@extend(node.lambda_expr) -def _backend(self,level=0): - return 'lambda %s: %s' % (self.args._backend(), - self.ret._backend()) -@extend(node.for_stmt) + +@extend(node.string) def _backend(self,level=0): - fmt = "for %s in %s.reshape(-1):%s" - return fmt % (self.ident._backend(), - self.expr._backend(), - self.stmt_list._backend(level+1)) - -@extend(node.if_stmt) +# s = self.value.strip() +# if not s: +# return "" +# if s[0] in "%#": +# return "\n"+s.replace("%","#") + return '"%s"' % self.value.encode("string_escape").replace('"','\\"') + +@extend(node.sub) def _backend(self,level=0): - s = "if %s:%s" % (self.cond_expr._backend(), - self.then_stmt._backend(level+1)) - if self.else_stmt: - # Eech. This should have been handled in the parser. - if self.else_stmt.__class__ == node.if_stmt: - self.else_stmt = node.stmt_list([self.else_stmt]) - s += "\n"+indent*level - s += "else:%s" % self.else_stmt._backend(level+1) - return s - -@extend(node.while_stmt) + return "(%s-%s)" % (self.args[0]._backend(), + self.args[1]._backend()) + +@extend(node.transpose) def _backend(self,level=0): - fmt = "while %s:\n%s\n" - return fmt % (self.cond_expr._backend(), - self.stmt_list._backend(level+1)) - + return "%s.T" % self.args[0]._backend() + @extend(node.try_catch) def _backend(self,level=0): fmt = "try:%s\n%sfinally:%s" @@ -378,15 +376,12 @@ def _backend(self,level=0): indent*level, self.finally_stmt._backend(level+1)) -@extend(node.builtins) + +@extend(node.while_stmt) def _backend(self,level=0): - #if not self.ret: - return "%s(%s)" % (self.__class__.__name__, - self.args._backend()) - # else: - # return ("%s=%s(%s)" % (self.ret._backend(), - # self.__class__.__name__, - # self.args._backend())) + fmt = "while %s:\n%s\n" + return fmt % (self.cond_expr._backend(), + self.stmt_list._backend(level+1)) ### @extend(node.strcmp) ### def _backend(self,level=0): diff --git a/smop/callgraph.py b/smop/callgraph.py index 6115adf3..350bf154 100644 --- a/smop/callgraph.py +++ b/smop/callgraph.py @@ -1,49 +1,31 @@ -import parse,sys +import parse,sys,os import networkx as nx -import node,resolve +import node,resolve,options -def callgraph(func_list): +def callgraph(G, stmt_list): """ Build callgraph of func_list, ignoring built-in functions """ - G = nx.DiGraph() - for func in func_list: - G.add_node(func.head.ident.name) + func_list = [] + for stmt in stmt_list: + try: + G.add_node(stmt.head.ident.name) + func_list.append(stmt) + except: + pass for func in func_list: assert isinstance(func,node.function) func_name = func.head.ident.name - resolve.resolve(func) + #resolve.resolve(func) for s in node.postorder(func): if (s.__class__ is node.funcall and - s.func_expr.__class__ is node.ident and - s.func_expr.name in G.nodes()): - G.add_edge(func_name,s.func_expr.name) - return G - -G = nx.DiGraph() - -def postorder_edge(u): - if isinstance(u,node.node): - for v in u: - for t in postorder_edge(v): - yield (v,t) - yield (u,u) # returns only traversible objects - -def foo(tree): - G = nx.DiGraph() - for u,v in postorder_edge(tree): - G.add_edge(id(u),id(v)) - return G - -def main(): - func_list = parse.parse(open(sys.argv[1]).read()) - G = foo(func_list) - #G = callgraph(func_list) - nx.write_dot(G,"G.dot") - #H = nx.dfs_tree(G,'solver') - #nx.write_dot(H,"H.dot") - #print nx.topological_sort(H) - + s.func_expr.__class__ is node.ident): + #if s.func_expr.name in G.nodes(): + G.add_edge(func_name,s.func_expr.name) + #nx.write_dot(G,"G.dot") + #for u in G.nodes(): + # if G.out_degree(u) == 0: + # print u if __name__ == '__main__': main() diff --git a/smop/core.py b/smop/core.py index 0cbbaf4b..10aa8dee 100644 --- a/smop/core.py +++ b/smop/core.py @@ -6,8 +6,7 @@ import __builtin__ import numpy -from sparsearray import sparsearray as sparse -from numpy import sqrt,eye +from numpy import sqrt from numpy.fft import fft2 from numpy.linalg import inv from numpy.linalg import qr as _qr @@ -54,6 +53,10 @@ class matlabarray(np.ndarray): """ >>> matlabarray() matlabarray([], shape=(0, 0), dtype=float64) + >>> matlabarray([arange(1,5), arange(1,5)]) + matlabarray([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]) + >>> matlabarray(["hello","world"]) + matlabarray("helloworld") """ def __new__(cls,a=[],dtype=None): @@ -303,14 +306,16 @@ class char is a rectangular string matrix, which >>> print s hello world - - >>> s.shape - (2, 5) + >>> s=char([104, 101, 108, 108, 111, 119, 111, 114, 108, 100]) + >>> s.shape = 2,5 + >>> print s + hello + world """ def __new__(cls, a=""): if not isinstance(a,str): - raise NotImplementedError + a = "".join([chr(c) for c in a]) obj = np.array(list(a), dtype='|S1', copy=False, @@ -332,7 +337,7 @@ def __str__(self): return "\n".join("".join(s) for s in self) raise NotImplementedError -class struct_(object): +class struct(object): def __init__(self,*args): for i in range(0,len(args),2): setattr(self,str(args[i]),args[i+1]) @@ -356,7 +361,7 @@ def arange(start,stop,step=1,**kwargs): step, **kwargs).reshape(1,-1),**kwargs) def cat(*args): - return np.concatenate([matlabarray(a) for a in args],axis=1) + return matlabarray(np.concatenate([matlabarray(a) for a in args],axis=1)).reshape(-1) def ceil(a): return numpy.ceil(a) @@ -557,16 +562,27 @@ def rand(*args,**kwargs): except: pass -def rand(*args,**kwargs): - if not args: - return np.random.rand() - if len(args) == 1: - args += args - try: - return np.random.rand(np.prod(args)).reshape(args,order="F") - except: +def assert_(a,b=None): + if b is None: + assert a + else: + assert isequal(a,b) + +def shared(a): pass +def rand(*args,**kwargs): + """from core aka libsmop.py""" + return np.random.rand() + # if not args: + # return np.random.rand() + # if len(args) == 1: + # args += args + # try: + # return np.random.rand(np.prod(args)).reshape(args,order="F") + # except: + # pass + def randn(*args,**kwargs): if not args: return np.random.randn() @@ -665,6 +681,9 @@ def zeros(*args,**kwargs): args += args return matlabarray(np.zeros(args,**kwargs)) +def isa(a,b): + return True + if __name__ == "__main__": import doctest doctest.testmod() diff --git a/smop/lexer.py b/smop/lexer.py index 29f51574..6c7c0aa4 100644 --- a/smop/lexer.py +++ b/smop/lexer.py @@ -1,25 +1,26 @@ -# SMOP compiler -- Simple Matlab/Octave to Python compiler -# Copyright 2011-2014 Victor Leikehman +# SMOP -- Simple Matlab/Octave to Python compiler +# Copyright 2011-2016 Victor Leikehman import sys import re from zlib import adler32 -import lex -from lex import TOKEN +import ply.lex as lex +from ply.lex import TOKEN import readline +import options class IllegalCharacterError(Exception): pass -tokens = [ - "AND", "ANDAND", "ANDEQ", "BACKSLASH", "COLON", "COMMA", "DIV","DIVEQ", - "DOT", "DOTDIV", "DOTDIVEQ", "DOTEXP", "DOTMUL","DOTMULEQ", "END_EXPR", - "END_STMT", "EQ", "EQEQ", "EXP", "EXPEQ", "FIELD", "GE", "GT", "HANDLE", - "IDENT", "LBRACE", "LBRACKET", "LE", "LPAREN", "LT", - "MINUS","MINUSMINUS","MINUSEQ","MUL","MULEQ","NE", "NEG", - "NUMBER", "OR","OREQ", "OROR", "PLUS", "PLUSEQ","PLUSPLUS", - "RBRACE", "RBRACKET", "RPAREN", "SEMI", "STRING", "TRANSPOSE", -] +tokens = [ "AND", "ANDAND", "ANDEQ", "BACKSLASH", "COLON", "COMMA", + "DIV","DIVEQ", "DOT", "DOTDIV", "DOTDIVEQ", "DOTEXP", + "DOTMUL","DOTMULEQ", "END_EXPR", "END_STMT", "EQ", "EQEQ", + "EXP", "EXPEQ", "FIELD", "GE", "GT", "HANDLE", "IDENT", + "LBRACE", "LBRACKET", "LE", "LPAREN", "LT", + "MINUS","MINUSMINUS","MINUSEQ","MUL","MULEQ","NE", "NEG", + "NUMBER", "OR","OREQ", "OROR", "PLUS", "PLUSEQ","PLUSPLUS", + "RBRACE", "RBRACKET", "RPAREN", "SEMI", "STRING", + "TRANSPOSE", "ERROR_STMT", "COMMENT" ] reserved = { "break" : "BREAK", @@ -85,7 +86,8 @@ def new(): states = (("matrix","inclusive"), ("afterkeyword","exclusive")) - ws = r"(\s|(\#|%).*\n|\.\.\..*\n|\\\n)" + ws = r"(\s|\.\.\..*\n|\\\n)" + #ws = r"(\s|(\#|(%[^!])).*\n|\.\.\..*\n|\\\n)" ws1 = ws+"+" ws0 = ws+"*" ms = r"'([^']|(''))*'" @@ -94,11 +96,13 @@ def new(): id = r"[a-zA-Z_][a-zA-Z_0-9]*" def unescape(s): + """ + ffd52d5fc5 + """ if s[0] == "'": - return s[1:-1].replace("''","'") + return s[1:-1].replace("''","'").decode("string_escape") else: - return s[1:-1].replace('""','"') - #return s[1:-1].decode('string_escape').replace('""','"') + return s[1:-1].replace('""','"').decode("string_escape") @TOKEN(mos) def t_afterkeyword_STRING(t): @@ -133,26 +137,30 @@ def t_STRING(t): def t_IDENT(t): t.lexer.lineno += t.value.count("\n") if t.value[0] == ".": - # Reserved words are not reserved when used as fields. - # So return=1 is illegal, but foo.return=1 is fine. + # Reserved words are not reserved + # when used as fields. So return=1 + # is illegal, but foo.return=1 is fine. t.type = "FIELD" return t - if t.value in ("endwhile","endfunction","endif","endfor", - "endswitch","end_try_catch"): # octave + if t.value in ("endwhile","endfunction", + "endif","endfor", + "endswitch","end_try_catch"): t.type = "END_STMT" return t if t.value == "end": - if t.lexer.parens > 0 or t.lexer.brackets > 0 or t.lexer.braces > 0: + if (t.lexer.parens > 0 or + t.lexer.brackets > 0 or + t.lexer.braces > 0): t.type = "END_EXPR" else: t.type = "END_STMT" else: t.type = reserved.get(t.value,"IDENT") - if t.type != "IDENT" and t.lexer.lexdata[t.lexer.lexpos]=="'": + if (t.type != "IDENT" and + t.lexer.lexdata[t.lexer.lexpos]=="'"): t.lexer.begin("afterkeyword") return t - def t_LPAREN(t): r"\(" t.lexer.parens += 1 @@ -215,6 +223,8 @@ def t_SEMI(t): def t_NUMBER(t): r"(0x[0-9A-Fa-f]+)|((\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?[ij]?)" + # <-------------> <------------------><-------------> + # int,oct,hex float exp if t.value[-1] == 'i': t.value = t.value[:-1]+'j' t.value = eval(t.value) @@ -228,18 +238,25 @@ def t_NEWLINE(t): t.type = "SEMI" return t - def t_comment(t): - r"(%|\#).*" - pass + def t_ERROR_STMT(t): + r"%!(error|warning|test|demo).*" + return t + # keep multiline comments + def t_COMMENT(t): + r"(^[ \t]*[%#].*\n)+" + t.lexer.lineno += t.value.count("\n") + if not options.keep_comments: + return + t.type = "COMMENT" + return t + + # drop end-of-line comments + def t_comment(t): + r"(%|\#)!?" + if not options.testing_mode or t.value[-1] != "!": + t.lexer.lexpos = t.lexer.lexdata.find("\n",t.lexer.lexpos) -# @TOKEN(ws+r"(?=[-+]\S)") -# def t_matrix_WHITESPACE(t): -# #r"\s+(?=[-+]\S)" -# # Whitespace, followed by + or - followed by anything but whitespace -# t.lexer.lineno += t.value.count("\n") -# t.type = "COMMA" -# return t @TOKEN(r"(?<=\w)" + ws1 + r"(?=\()") def t_matrix_BAR(t): @@ -298,24 +315,33 @@ def t_error(t): column=t.lexer.lexpos - t.lexer.lexdata.rfind("\n",0,t.lexer.lexpos) raise IllegalCharacterError(t.lineno,column,t.value[0]) - lexer = lex.lex(reflags=re.I) + + lexer = lex.lex(reflags=re.MULTILINE) lexer.brackets = 0 # count open square brackets lexer.parens = 0 # count open parentheses lexer.braces = 0 # count open curly braces + lexer.stack = [] return lexer -if __name__ == "__main__": +def main(): lexer = new() - try: - while 1: - line = raw_input("=>> ") - if not line: - continue - while line[-1] == "\\": - line = line[:-1] + raw_input("... ") + line = "" + while 1: + try: + line += raw_input("=>> ").decode("string_escape") print len(line), [c for c in line] + except EOFError: + reload(sys.modules["lexer.py"]) lexer.input(line) - for tok in lexer: - print len(str(tok.value)), tok - except EOFError: - pass + print list(tok for tok in lexer) + line = "" + +if __name__ == "__main__": + options.testing_mode = 0 + options.debug_lexer = 0 + lexer = new() + buf = open(sys.argv[1]).read() + lexer.input(buf) + for tok in lexer: + print tok + diff --git a/smop/main.py b/smop/main.py index b8a4b953..61dd741e 100644 --- a/smop/main.py +++ b/smop/main.py @@ -1,189 +1,81 @@ -# SMOP compiler -- Simple Matlab/Octave to Python compiler -# Copyright 2011-2013 Victor Leikehman +# SMOP -- Simple Matlab/Octave to Python compiler +# Copyright 2011-2016 Victor Leikehman -import version +#import version import sys,cPickle,glob,os import getopt,re import lexer,parse,resolve,backend,options,node,graphviz +import callgraph import networkx as nx +import pickle import readline -#from runtime import * -#from version import __version__ -__version__ = version.__version__ - -def usage(): - print "SMOP compiler version " + __version__ - print """Usage: smop [options] file-list - Options: - -V --version - -X --exclude=FILES Ignore files listed in comma-separated list FILES. - Can be used several times. - -S --syntax-errors=FILES Ignore syntax errors in comma-separated list of FILES. - Can be used several times. - -S. Always gnore syntax errors - -d --dot=REGEX For functions whose names match REGEX, save debugging - information in "dot" format (see www.graphviz.org). - You need an installation of graphviz to use --dot - option. Use "dot" utility to create a pdf file. - For example: - $ python main.py fastsolver.m -d "solver|cbest" - $ dot -Tpdf -o resolve_solver.pdf resolve_solver.dot - -h --help - -o --output=FILENAME By default create file named a.py - -o- --output=- Use standard output - -s --strict Stop on the first error - -v --verbose -""" +import graphviz def main(): - """ - !a="def f(): \\n\\treturn 123" - !exec a - !print f - !print f() - !reload(backend) - =>> function t=foo(a) \\ - ... t=123 - !exec foo(3) - """ - try: - opts, args = getopt.gnu_getopt(sys.argv[1:], - "d:ho:vVsr:S:X:", - [ - "dot=", - "exclude=", - "help", - "syntax-errors=", - "output=", - "runtime=", - "strict", - "verbose", - "version", - ]) - except getopt.GetoptError, err: - # print help information and exit: - print str(err) # will print something like "option -a not recognized" - usage() - sys.exit(2) - - exclude_list = [] - output = None - strict = 0 - dot = None - runtime = [] - - for o, a in opts: - if o in ("-r", "--runtime"): - runtime += [a] - elif o in ("-s", "--strict"): - strict = 1 - elif o in ("-S", "--syntax-errors"): - options.syntax_errors += a.split(",") - elif o in ("-d", "--dot"): - dot = re.compile(a) - elif o in ("-X", "--exclude"): - exclude_list += [ "%s.m" % b for b in a.split(",")] - elif o in ("-v", "--verbose"): - options.verbose += 1 - elif o in ("-V", "--version"): - print "SMOP compiler version " + __version__ - sys.exit() - elif o in ("-h", "--help"): - usage() - sys.exit() - elif o in ("-o", "--output"): - output = a - else: - assert False, "unhandled option" - - """ - if not args: - usage() - sys.exit() - """ - if not args: - symtab = {} - print "? for help" - while 1: - try: - buf = raw_input("octave: ") - if not buf: - continue - while buf[-1] == "\\": - buf = buf[:-1] + "\n" + raw_input("... ") - if buf[0] == '?': - print main.__doc__ - continue - if buf[0] == "!": - try: - exec buf[1:] - except Exception as ex: - print ex - continue - t = parse.parse(buf if buf[-1]=='\n' else buf+'\n') - if not t: - continue - print "t=", repr(t) - print 60*"-" - resolve.resolve(t,symtab) - print "t=", repr(t) - print 60*"-" - print "symtab:",symtab - s = backend.backend(t) - print "python:",s.strip() - try: - print eval(s) - except SyntaxError: - exec s - except EOFError: - return - except Exception as ex: - print ex + #args = options.parser.parse_args() + #for key in dir(args): + # if not key.startswith("_"): + # setattr(options,key,getattr(args,key)) + if not options.filelist: + options.parser.print_help() + return + #assert options.filelist + #xfiles = options.exclude.split(",") if options.exclude else [] + if not options.output: + options.output = "a.py" + fp = open(options.output,"w") if options.output != "-" else sys.stdout - if not output: - output = "a.py" - fp = open(output,"w") if output != "-" else sys.stdout - print >> fp, "# Autogenerated with SMOP version " + __version__ - print >> fp, "# " + " ".join(sys.argv) + print >> fp, "# Autogenerated with SMOP "# + options.version + # for key in dir(options.args): + # if not key.startswith("_"): + # value = getattr(options,key) + # print >> fp, '# %s=%s' % (key,value) print >> fp, "from __future__ import division" - for a in runtime: - print >> fp, "from %s import *" % a + print >> fp, "from core import *" - for pattern in args: - for filename in glob.glob(os.path.expanduser(pattern)): - if not filename.endswith(".m"): - print "\tIngored file: '%s'" % filename + #if options.callgraph: + # C = nx.DiGraph() + for options.filename in options.filelist: + try: + #for options.filename in glob.glob(os.path.expanduser(pattern)): + print >> fp, "#", options.filename + if not options.filename.endswith((".m",".tst")): + print "\tIgnored file: '%s'" % options.filename continue - if os.path.basename(filename) in exclude_list: - print "\tExcluded file: '%s'" % filename + if os.path.basename(options.filename) in options.xfiles: + print "\tExcluded file: '%s'" % options.filename continue if options.verbose: - print filename - buf = open(filename).read().replace("\r\n","\n") - func_list = parse.parse(buf if buf[-1]=='\n' else buf+'\n',filename) - if not func_list and strict: - sys.exit(-1) - - for func_obj in func_list: - try: - func_name = func_obj.head.ident.name - if options.verbose: - print "\t",func_name - except AttributeError: - if options.verbose: - print "\tJunk ignored" - if strict: - sys.exit(-1) - continue - fp0 = open("parse_"+func_name+".dot","w") if dot and dot.match(func_name) else None - if fp0: - graphviz.graphviz(func_obj,fp0) - if options.do_resolve: - G = resolve.resolve(func_obj) - - for func_obj in func_list: - s = backend.backend(func_obj) + print options.filename + buf = open(options.filename).read().replace("\r\n","\n") + stmt_list=parse.parse(buf if buf[-1]=='\n' else buf+'\n') + #assert None not in stmt_list + if not stmt_list and options.strict: + return + if options.enumerate: + for i,stmt_obj in enumerate(stmt_list): + #stmt_class = stmt_obj.__class__.__name__ + print i, stmt_obj + # if i == options.debug_index: + # import pdb ; pdb.set_trace() + # if stmt_class == "func_stmt": + # func_name = stmt_obj.ident.name + # if options.verbose: + # print "\t",func_name + # else: + # func_name = "" + if not options.no_resolve: + G = resolve.resolve(stmt_list) + if not options.no_backend: + s = backend.backend(stmt_list) print >> fp, s + except Exception as e: + print e + if options.strict: + raise + # if options.callgraph: + # pickle.dump(C, open("callgraph.pickle","w")) + #nx.write_dot(C,"callgraph.dot") if __name__ == "__main__": main() diff --git a/smop/node.py b/smop/node.py index aadf2959..591cc7a8 100644 --- a/smop/node.py +++ b/smop/node.py @@ -39,6 +39,7 @@ def extend(cls): return lambda f: (setattr(cls,f.__name__,f) or f) def exceptions(f): + return f def wrapper(self,*args,**kwargs): try: return f(self,*args,**kwargs) @@ -46,6 +47,7 @@ def wrapper(self,*args,**kwargs): print "%s.%s()" % (self.__class__.__name__, f.__name__) raise wrapper.__name__ = f.__name__ + wrapper.__doc__ = f.__doc__ return wrapper class node(object): @@ -100,6 +102,9 @@ def __str__(self): return "\n".join([str(t) for t in self]) def __repr__(self): return "stmt_list(%s)" % list.__repr__(self) + def append(self,s): + assert isinstance(s,node), s.__class__ + list.append(self,s) ##################### # @@ -152,10 +157,18 @@ class let(stmt,recordtype("let", def __str__(self): return "%s=%s" % (str(self.ret), str(self.args)) -class func_decl(stmt,recordtype("func_decl","ident ret args decl_list use_nargin",default=None)): +class func_stmt(stmt,recordtype("func_stmt", + """ + ident + ret + args + stmt_list + use_nargin + """, + default=None)): pass -class lambda_expr(func_decl): +class lambda_expr(func_stmt): pass class function(stmt,recordtype("function","head body")): @@ -178,10 +191,18 @@ class global_stmt(stmt,recordtype("global_stmt","global_list")): def __str__(self): return "global %s" % str(self.global_list) +class persistent_stmt(stmt,recordtype("persistent_stmt","global_list")): + def __str__(self): + return "global %s" % str(self.global_list) + class return_stmt(stmt,namedtuple("return_stmt","ret")): def __str__(self): return "return" +class comment_stmt(stmt,namedtuple("comment_stmt","value")): + def __str__(self): + return "comment" + class end_stmt(stmt,namedtuple("end_stmt","dummy")): def __str__(self): return "end" @@ -194,6 +215,10 @@ class break_stmt(stmt,namedtuple("break_stmt","dummy")): def __str__(self): return "break" +class null_stmt(stmt,namedtuple("null_stmt","")): + def __str__(self): + return ";" + class expr_stmt(stmt,node,recordtype("expr_stmt","expr")): def __str__(self): return str(self.expr) @@ -281,21 +306,21 @@ def __str__(self): # names in caps correspond to fortran funcs builtins_list = [ - "ABS", - "ALL", - "ANY", - "CEILING", - "FIND", - "ISNAN", - "MAXVAL", - "MINVAL", - "MODULO", - "RAND", - "RESHAPE", - "SHAPE", - "SIGN", - "SIZE", - "SUM", +# "ABS", +# "ALL", +# "ANY", +# "CEILING", +# "FIND", +# "ISNAN", +# "MAXVAL", +# "MINVAL", +# "MODULO", +# "RAND", +# "RESHAPE", +# "SHAPE", +# "SIGN", +# "SIZE", +# "SUM", #"abs", "add", # synthetic opcode diff --git a/smop/options.py b/smop/options.py index 672e9ac6..aae26f23 100644 --- a/smop/options.py +++ b/smop/options.py @@ -1,34 +1,86 @@ -subscripts = "square" # "round" +import sys +import argparse,textwrap +from textwrap import dedent +from version import __version__ -"""Row vectors in Matlab can be expressed in Fortran as - either one or two dimensional arrays""" -row_vector_ndim = 1 # 2 +parser = argparse.ArgumentParser( + "smop", + usage="""python main.py [options][file.m ...file.m]""", + description= """SMOP is Small Matlab and Octave to Python compiler""", + epilog= + """ + $ python [-mpdb] lexer.py fastsolver.m [options] + $ python [-mpdb] main.py fastsolver.m [options] + emacs (pdb): pdb main.py fastsolver.m [options] + """, + formatter_class=argparse.RawTextHelpFormatter,) -"""Given Matlab code such as [a b c]=size(X) we heuristically - decide that X is 3-dimensional.""" -rank_guess_from_size = True -"""Given false(1,n), deduce that n is a scalar""" -rank_backward_propagate=1 +#parser.add_argument("--callgraph", metavar="F", type=str) -"""0=not even constants - 1=char constants supported -""" -has_char_constants = 0 +parser.add_argument("--enumerate", action="store_true", +help="""enumerate statements (see --debug-index)""") -do_allocate = 0 -do_resolve = 1 -do_rewrite = 0 -do_rename = 0 # SSA -do_typeof = 0 -do_listing = 0 +parser.add_argument("--debug-index", type=int, +help="""enter pdb when stmt index equals --debug-index""") -debug = False +parser.add_argument("--debug-lexer", action="store_true", +help="enable built-in debugging tools") -uppercase=True # active only with f90 +parser.add_argument("--debug-parser", action="store_true", +help="enable built-in debugging tools") -line_numbering=True #True # uses either # or ! or % +parser.add_argument("filelist", nargs="*", metavar="file.m", type=str) -syntax_errors = [] -verbose = 0 +#parser.add_argument("--graphviz", action="store_true") + +parser.add_argument("--keep-comments", action="store_true", +help="""preserve multiline comments""") + +parser.add_argument("--line-numbers", action="store_true", +help="""include line-numbering information """) + +parser.add_argument("-o", "--output", metavar="file.py", type=str, +help= """default output file name is 'a.py'""") + +parser.add_argument("--no-backend", action="store_true", +help="omit code generation") + +parser.add_argument("--no-resolve", action="store_true", +help="omit name resolution") + +parser.add_argument("-s", "--strict", action="store_true", +help="stop after first syntax error") + +parser.add_argument("--testing-mode",action="store_true", +help= """support special "testing" percent-bang +comments used to write Octave test suite. When +disabled, behave like regular comments.""") + +parser.add_argument("-V", '--version', action='version', + version=__version__) + +parser.add_argument("-v", "--verbose",action="store_true") + +parser.add_argument("-x", "--exclude", metavar="f,g,h", type=str, +help="""comma-separated list of files to ignore""") + +args = parser.parse_args(namespace=sys.modules[__name__]) + +xfiles = args.exclude.split(",") if args.exclude else [] filename = "" + +def foo(): + """ + >>> args = parser.parse_args("a b c".split()) + >>> print args.filelist + ['a', 'b', 'c' + + >>> args = parser.parse_args(["--keep-comments"]) + >>> print args.keep_comments + True + + """ +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/smop/parse.py b/smop/parse.py index 3f9b4e14..416a9393 100644 --- a/smop/parse.py +++ b/smop/parse.py @@ -6,16 +6,17 @@ import os import operator import sys +import readline import re -import yacc - -from lexer import tokens +import ply.yacc as yacc +import ply.lex as lex import lexer +from lexer import tokens #import builtins import node -#from node import * +from node import exceptions import resolve,options # ident properties (set in parse.py) @@ -58,68 +59,19 @@ class syntax_error(error): def p_top(p): """ top : - | stmt_list - | top func_decl stmt_list_opt - | top func_decl END_STMT semi_opt - | top func_decl stmt_list END_STMT semi_opt + | top stmt + | top END_STMT """ if len(p) == 1: p[0] = node.stmt_list() - elif len(p) == 2: - p[0] = p[1] else: - # we backpatch the func_decl node - assert p[2].__class__ is node.func_decl - p[2].use_nargin = use_nargin - p[2].use_varargin = use_varargin - - try: - if p[3][-1].__class__ is not node.return_stmt: - p[3].append(node.return_stmt(ret_expr)) - except: - raise syntax_error(p) - p[0] = p[1] - p[0].append(node.function(head=p[2],body=p[3])) - assert isinstance(p[0],node.stmt_list) - -def p_semi_opt(p): - """ - semi_opt : - | semi_opt SEMI - | semi_opt COMMA - """ - pass - -def p_stmt(p): - """ - stmt : continue_stmt - | break_stmt - | expr_stmt - | global_stmt - | persistent_stmt - | command - | for_stmt - | if_stmt - | null_stmt - | return_stmt - | switch_stmt - | try_catch - | while_stmt - | foo_stmt - | unwind - """ - # END_STMT is intentionally left out - p[0] = p[1] - -def p_unwind(p): - """ - unwind : UNWIND_PROTECT stmt_list UNWIND_PROTECT_CLEANUP stmt_list END_UNWIND_PROTECT - """ - p[0] = node.try_catch(try_stmt=p[2], - catch_stmt=node.expr_list(), - finally_stmt=p[4]) + if type(p[2]) != str: + p[0].append(p[2]) +########################################## + +@exceptions def p_arg1(p): """ arg1 : STRING @@ -132,101 +84,65 @@ def p_arg1(p): lineno=p.lineno(1), lexpos=p.lexpos(1)) -def p_args(p): + +@exceptions +def p_arg_list(p): """ - args : arg1 - | args arg1 + arg_list : ident + | arg_list COMMA ident """ if len(p) == 2: + p[1].__class__ = node.param p[0] = node.expr_list([p[1]]) - else: + elif len(p) == 4: p[0] = p[1] - p[0].append(p[2]) + p[3].__class__ = node.param + p[0].append(p[3]) + else: + assert 0 + assert isinstance(p[0],node.expr_list) + for ident in p[0]: + ident.props="A" -def p_command(p): - """ - command : ident args SEMI + +@exceptions +def p_args(p): """ -# if p[1].name == "load": -# # "load filename x" ==> "x=load(filename)" -# # "load filename x y z" ==> "(x,y,z)=load(filename)" -# ret=node.expr_list([node.ident(t.value) for t in p[2][1:]]) -# p[0] = node.funcall(func_expr=p[1], -# args=node.expr_list(p[2]), -# ret=ret) -# else: - p[0] = node.funcall(p[1],p[2]) - -#################### -def p_global_list(p): - """global_list : ident - | global_list ident + args : arg1 + | args arg1 """ if len(p) == 2: - p[0] = node.global_list([p[1]]) - elif len(p) == 3: + p[0] = node.expr_list([p[1]]) + else: p[0] = p[1] p[0].append(p[2]) -def p_global_stmt(p): - """ - global_stmt : GLOBAL global_list SEMI - | GLOBAL ident EQ expr SEMI - """ - p[0] = node.global_stmt(p[2]) - for ident in p[0]: - ident.props="G" # G=global - -def p_foo_stmt(p): - "foo_stmt : expr OROR expr SEMI" - expr1 = p[1][1][0] - expr2 = p[3][1][0] - ident = expr1.ret - args1 = expr1.args - args2 = expr2.args - p[0] = node.let(ret=ident, - args=node.expr("or",node.expr_list([args1,args2]))) - -def p_persistent_stmt(p): + +@exceptions +def p_args_opt(p): """ - persistent_stmt : PERSISTENT global_list SEMI - | PERSISTENT ident EQ expr SEMI + args_opt : + | LPAREN RPAREN + | LPAREN expr_list RPAREN """ -# if len(p) == 4: -# p[0] = node.global_stmt(p[2]) -# for ident in p[0]: -# ident.props="G" # G=global -# else: -# assert p[2].__class__ in (node.let,node.ident), p[2].__class__ -# p[0] = p[2] -# #print p[2] - - -def p_return_stmt(p): - "return_stmt : RETURN SEMI" - p[0] = node.return_stmt(ret=ret_expr) - -def p_continue_stmt(p): - "continue_stmt : CONTINUE SEMI" - p[0] = node.continue_stmt(None) + if len(p) == 1: + p[0] = node.expr_list() + elif len(p) == 3: + p[0] = node.expr_list() + elif len(p) == 4: + assert isinstance(p[2],node.expr_list) + p[0] = p[2] + else: + assert 0 + +@exceptions def p_break_stmt(p): "break_stmt : BREAK SEMI" p[0] = node.break_stmt(None) -# switch-case-otherwise - -def p_switch_stmt(p): - """ - switch_stmt : SWITCH expr semi_opt case_list END_STMT - """ - def backpatch(expr,stmt): - if isinstance(stmt,node.if_stmt): - stmt.cond_expr.args[1] = expr - backpatch(expr,stmt.else_stmt) - backpatch(p[2],p[4]) - p[0] = p[4] - + +@exceptions def p_case_list(p): """ case_list : @@ -248,222 +164,104 @@ def p_case_list(p): else: assert 0 -# try-catch - -def p_try_catch(p): + +@exceptions +def p_cellarray(p): """ - try_catch : TRY stmt_list CATCH stmt_list END_STMT - | TRY stmt_list END_STMT + cellarray : LBRACE RBRACE + | LBRACE expr_list RBRACE + | LBRACE concat_list RBRACE + | LBRACE concat_list SEMI RBRACE """ - assert isinstance(p[2],node.stmt_list) - #assert isinstance(p[4],node.stmt_list) - p[0] = node.try_catch(try_stmt=p[2], - catch_stmt=node.stmt_list(), # FIXME - finally_stmt=node.stmt_list()) + if len(p) == 3: + p[0] = node.cellarray(op="{}",args=node.expr_list()) + else: + p[0] = node.cellarray(op="{}",args=p[2]) + + +@exceptions +def p_cellarrayref(p): + """expr : expr LBRACE expr_list RBRACE + | expr LBRACE RBRACE + """ + args = node.expr_list() if len(p) == 4 else p[3] + assert isinstance(args,node.expr_list) + p[0] = node.cellarrayref(func_expr=p[1],args=args) -def p_null_stmt(p): + +@exceptions +def p_command(p): """ - null_stmt : SEMI - | COMMA + command : ident args SEMI """ - p[0] = None - -def p_func_decl(p): - """func_decl : FUNCTION ident args_opt SEMI - | FUNCTION ret EQ ident args_opt SEMI +# if p[1].name == "load": +# # "load filename x" ==> "x=load(filename)" +# # "load filename x y z" ==> "(x,y,z)=load(filename)" +# ret=node.expr_list([node.ident(t.value) for t in p[2][1:]]) +# p[0] = node.funcall(func_expr=p[1], +# args=node.expr_list(p[2]), +# ret=ret) +# else: + p[0] = node.funcall(p[1],p[2]) + +#################### + +@exceptions +def p_comment_stmt(p): """ - global ret_expr,use_nargin,use_varargin - use_varargin = use_nargin = 0 + comment_stmt : COMMENT + """ + p[0] = node.comment_stmt(p[1]) - if len(p) == 5: - assert isinstance(p[3],node.expr_list) - p[0] = node.func_decl(ident=p[2], - ret=node.expr_list(), - args=p[3]) - ret_expr = node.expr_list() - elif len(p) == 7: - assert isinstance(p[2],node.expr_list) - assert isinstance(p[5],node.expr_list) - p[0] = node.func_decl(ident=p[4], - ret=p[2], - args=p[5]) - ret_expr = p[2] + +@exceptions +def p_concat_list(p): + """ + concat_list : expr_list SEMI expr_list + | concat_list SEMI expr_list + """ + if p[1].__class__ == node.expr_list: + p[0] = node.concat_list([p[1],p[3]]) else: - assert 0 + p[0] = p[1] + p[0].append(p[3]) -def p_args_opt(p): + +@exceptions +def p_continue_stmt(p): + "continue_stmt : CONTINUE SEMI" + p[0] = node.continue_stmt(None) + + +@exceptions +def p_elseif_stmt(p): """ - args_opt : - | LPAREN RPAREN - | LPAREN expr_list RPAREN + elseif_stmt : + | ELSE stmt_list_opt + | ELSEIF expr sep stmt_list_opt elseif_stmt """ if len(p) == 1: - p[0] = node.expr_list() + p[0] = node.stmt_list() elif len(p) == 3: - p[0] = node.expr_list() - elif len(p) == 4: - assert isinstance(p[2],node.expr_list) p[0] = p[2] + elif len(p) == 6: + p[0] = node.if_stmt(cond_expr=p[2], + then_stmt=p[4], + else_stmt=p[5]) else: assert 0 -def p_arg_list(p): + + +@exceptions +def p_error_stmt(p): """ - arg_list : ident - | arg_list COMMA ident + error_stmt : ERROR_STMT SEMI """ - if len(p) == 2: - p[1].__class__ = node.param - p[0] = node.expr_list([p[1]]) - elif len(p) == 4: - p[0] = p[1] - p[3].__class__ = node.param - p[0].append(p[3]) - else: - assert 0 - assert isinstance(p[0],node.expr_list) - for ident in p[0]: - ident.props="A" - -def p_ret(p): - """ - ret : ident - | LBRACKET RBRACKET - | LBRACKET expr_list RBRACKET - """ - if len(p) == 2: - p[0] = node.expr_list([p[1]]) - elif len(p) == 3: - p[0] = node.expr_list([]) - elif len(p) == 4: - assert isinstance(p[2],node.expr_list) - p[0] = p[2] - else: - assert 0 - for ident in p[0]: - ident.props="F" -# end func_decl - -def p_stmt_list_opt(p): - """ - stmt_list_opt : - | stmt_list - """ - if len(p) == 1: - p[0] = node.stmt_list() - else: - p[0] = p[1] - -def p_stmt_list(p): - """ - stmt_list : stmt - | stmt_list stmt - """ - if len(p) == 2: - p[0] = node.stmt_list([p[1]] if p[1] else []) - elif len(p) == 3: - p[0] = p[1] - if p[2]: - p[0].append(p[2]) - else: - assert 0 - -def p_concat_list(p): - """ - concat_list : expr_list SEMI expr_list - | concat_list SEMI expr_list - """ - if p[1].__class__ == node.expr_list: - p[0] = node.concat_list([p[1],p[3]]) - else: - p[0] = p[1] - p[0].append(p[3]) - -def p_expr_list(p): - """ - expr_list : exprs - | exprs COMMA - """ - p[0] = p[1] - -def p_exprs(p): - """ - exprs : expr - | exprs COMMA expr - """ - if len(p) == 2: - p[0] = node.expr_list([p[1]]) - elif len(p) == 4: - p[0] = p[1] - p[0].append(p[3]) - else: - assert(0) - assert isinstance(p[0],node.expr_list) - -def p_expr_stmt(p): - """ - expr_stmt : expr_list SEMI - """ - assert isinstance(p[1],node.expr_list) - p[0] = node.expr_stmt(expr=p[1]) - -def p_while_stmt(p): - """ - while_stmt : WHILE expr SEMI stmt_list END_STMT - """ - assert isinstance(p[4],node.stmt_list) - p[0] = node.while_stmt(cond_expr=p[2], - stmt_list=p[4]) - -def p_separator(p): - """ - sep : COMMA - | SEMI - """ - p[0] = p[1] - -def p_if_stmt(p): - """ - if_stmt : IF expr sep stmt_list_opt elseif_stmt END_STMT - | IF expr error stmt_list_opt elseif_stmt END_STMT - """ - p[0] = node.if_stmt(cond_expr=p[2], - then_stmt=p[4], - else_stmt=p[5]) - -def p_elseif_stmt(p): - """ - elseif_stmt : - | ELSE stmt_list_opt - | ELSEIF expr sep stmt_list_opt elseif_stmt - """ - if len(p) == 1: - p[0] = node.stmt_list() - elif len(p) == 3: - p[0] = p[2] - elif len(p) == 6: - p[0] = node.if_stmt(cond_expr=p[2], - then_stmt=p[4], - else_stmt=p[5]) - else: - assert 0 - - -def p_for_stmt(p): - """ - for_stmt : FOR ident EQ expr SEMI stmt_list END_STMT - | FOR LPAREN ident EQ expr RPAREN SEMI stmt_list END_STMT - | FOR matrix EQ expr SEMI stmt_list END_STMT - """ - if len(p) == 8: - if not isinstance(p[2],node.ident): - raise NotImplementedError("for loop") - p[2].props="I" # I= for-loop iteration variable - p[0] = node.for_stmt(ident=p[2], - expr=p[4], - stmt_list=p[6]) -################ expr ################ + p[0] = node.null_stmt() + +@exceptions def p_expr(p): """expr : ident | end @@ -486,47 +284,8 @@ def p_expr(p): else: p[0] = p[1] -def p_lambda_args(p): - """lambda_args : LPAREN RPAREN - | LPAREN arg_list RPAREN - """ - p[0] = p[2] if len(p) == 4 else node.expr_list() - -def p_lambda_expr(p): - """lambda_expr : HANDLE lambda_args expr - """ - p[0] = node.lambda_expr(args=p[2], ret=p[3]) - -def p_expr_ident(p): - "ident : IDENT" - global use_nargin,use_varargin - if p[1] == "varargin": - use_varargin = 1 - if p[1] == "nargin": - use_nargin = 1 - #import pdb; pdb.set_trace() - p[0] = node.ident(name=p[1], - lineno=p.lineno(1), - lexpos=p.lexpos(1), - column=p.lexpos(1) - p.lexer.lexdata.rfind("\n",0,p.lexpos(1))) - -def p_expr_number(p): - "number : NUMBER" - p[0] = node.number(p[1],lineno=p.lineno(1),lexpos=p.lexpos(1)) - -def p_expr_end(p): - "end : END_EXPR" - p[0] = node.expr(op="end",args=node.expr_list([node.number(0), - node.number(0)])) - -def p_expr_string(p): - "string : STRING" - p[0] = node.string(p[1],lineno=p.lineno(1),lexpos=p.lexpos(1)) - -def p_expr_colon(p): - "colon : COLON" - p[0] = node.expr(op=":",args=node.expr_list()) - + +@exceptions def p_expr1(p): """expr1 : MINUS expr %prec UMINUS | PLUS expr %prec UMINUS @@ -537,76 +296,8 @@ def p_expr1(p): """ p[0] = node.expr(op=p[1],args=node.expr_list([p[2]])) -def p_cellarray(p): - """ - cellarray : LBRACE RBRACE - | LBRACE expr_list RBRACE - | LBRACE concat_list RBRACE - | LBRACE concat_list SEMI RBRACE - """ - if len(p) == 3: - p[0] = node.cellarray(op="{}",args=node.expr_list()) - else: - p[0] = node.cellarray(op="{}",args=p[2]) - -def p_matrix(p): - """matrix : LBRACKET RBRACKET - | LBRACKET concat_list RBRACKET - | LBRACKET concat_list SEMI RBRACKET - | LBRACKET expr_list RBRACKET - | LBRACKET expr_list SEMI RBRACKET - """ - if len(p) == 3: - p[0] = node.matrix() - else: - p[0] = node.matrix(p[2]) - -def p_paren_expr(p): - """ - expr : LPAREN expr RPAREN - """ - p[0] = node.expr(op="parens",args=node.expr_list([p[2]])) - -def p_field_expr(p): - """ - expr : expr FIELD - """ - p[0] = node.expr(op=".", - args=node.expr_list([p[1], - node.ident(name=p[2], - lineno=p.lineno(2), - lexpos=p.lexpos(2))])) - -def p_transpose_expr(p): - # p[2] contains the exact combination of plain and conjugate - # transpose operators, such as "'.''.''''". - "expr : expr TRANSPOSE" - p[0] = node.transpose(p[1],node.string(p[2])) - -def p_cellarrayref(p): - """expr : expr LBRACE expr_list RBRACE - | expr LBRACE RBRACE - """ - args = node.expr_list() if len(p) == 4 else p[3] - assert isinstance(args,node.expr_list) - p[0] = node.cellarrayref(func_expr=p[1],args=args) - -def p_funcall_expr(p): - """expr : expr LPAREN expr_list RPAREN - | expr LPAREN RPAREN - """ - if (0 and len(p)==5 and - len(p[3])==1 and - p[3][0].__class__ is node.expr and - p[3][0].op == ":" and not p[3][0].args): - # foo(:) => ravel(foo) - p[0] = node.funcall(func_expr=node.ident("ravel"), - args=node.expr_list([p[1]])) - else: - args = node.expr_list() if len(p) == 4 else p[3] - assert isinstance(args,node.expr_list) - p[0] = node.funcall(func_expr=p[1],args=args) - + +@exceptions def p_expr2(p): """expr2 : expr AND expr | expr ANDAND expr @@ -696,6 +387,9 @@ def p_expr2(p): p[3].nargout = len(p[1].args[0]) elif p[2] == ".*": p[0] = node.dot(p[1],p[3]) +# elif p[2] == "." and isinstance(p[3],node.expr) and p[3].op=="parens": +# p[0] = node.getfield(p[1],p[3].args[0]) +# raise NotImplementedError(p[3],p.lineno(3),p.lexpos(3)) elif p[2] == ":" and isinstance(p[1],node.expr) and p[1].op==":": # Colon expression means different things depending on the # context. As an array subscript, it is a slice; otherwise, @@ -707,49 +401,456 @@ def p_expr2(p): else: p[0] = node.expr(op=p[2],args=node.expr_list([p[1],p[3]])) -def p_error(p): - #import pdb; pdb.set_trace() - #print "p_error",p - if ("." in options.syntax_errors or - os.path.basename(options.filename) in options.syntax_errors): - return p - raise syntax_error(p) -parser = yacc.yacc(start="top") + +@exceptions +def p_expr_colon(p): + "colon : COLON" + p[0] = node.expr(op=":",args=node.expr_list()) + + +@exceptions +def p_expr_end(p): + "end : END_EXPR" + p[0] = node.expr(op="end",args=node.expr_list([node.number(0), + node.number(0)])) + + +@exceptions +def p_expr_ident(p): + "ident : IDENT" + global use_nargin,use_varargin + if p[1] == "varargin": + use_varargin = 1 + if p[1] == "nargin": + use_nargin = 1 + #import pdb; pdb.set_trace() + p[0] = node.ident(name=p[1], + lineno=p.lineno(1), + lexpos=p.lexpos(1), + column=p.lexpos(1) - p.lexer.lexdata.rfind("\n",0,p.lexpos(1))) + + +@exceptions +def p_expr_list(p): + """ + expr_list : exprs + | exprs COMMA + """ + p[0] = p[1] + + +@exceptions +def p_expr_number(p): + "number : NUMBER" + p[0] = node.number(p[1],lineno=p.lineno(1),lexpos=p.lexpos(1)) + + +@exceptions +def p_expr_stmt(p): + """ + expr_stmt : expr_list SEMI + """ + assert isinstance(p[1],node.expr_list) + p[0] = node.expr_stmt(expr=p[1]) + + +@exceptions +def p_expr_string(p): + "string : STRING" + p[0] = node.string(p[1],lineno=p.lineno(1),lexpos=p.lexpos(1)) + + +@exceptions +def p_exprs(p): + """ + exprs : expr + | exprs COMMA expr + """ + if len(p) == 2: + p[0] = node.expr_list([p[1]]) + elif len(p) == 4: + p[0] = p[1] + p[0].append(p[3]) + else: + assert(0) + assert isinstance(p[0],node.expr_list) + + +@exceptions +def p_field_expr(p): + """ + expr : expr FIELD + """ + p[0] = node.expr(op=".", + args=node.expr_list([p[1], + node.ident(name=p[2], + lineno=p.lineno(2), + lexpos=p.lexpos(2))])) + + +@exceptions +def p_foo_stmt(p): + "foo_stmt : expr OROR expr SEMI" + expr1 = p[1][1][0] + expr2 = p[3][1][0] + ident = expr1.ret + args1 = expr1.args + args2 = expr2.args + p[0] = node.let(ret=ident, + args=node.expr("or",node.expr_list([args1,args2]))) + + +@exceptions +def p_for_stmt(p): + """ + for_stmt : FOR ident EQ expr SEMI stmt_list END_STMT + | FOR LPAREN ident EQ expr RPAREN SEMI stmt_list END_STMT + | FOR matrix EQ expr SEMI stmt_list END_STMT + """ + if len(p) == 8: + if not isinstance(p[2],node.ident): + raise NotImplementedError("for loop") + p[2].props="I" # I= for-loop iteration variable + p[0] = node.for_stmt(ident=p[2], + expr=p[4], + stmt_list=p[6]) +################ expr ################ + + +@exceptions +def p_func_stmt(p): + """func_stmt : FUNCTION ident args_opt SEMI + | FUNCTION ret EQ ident args_opt SEMI + """ + global ret_expr,use_nargin,use_varargin + use_varargin = use_nargin = 0 + + if len(p) == 5: + assert isinstance(p[3],node.expr_list) + p[0] = node.func_stmt(ident=p[2], + ret=node.expr_list(), + args=p[3]) + ret_expr = node.expr_list() + elif len(p) == 7: + assert isinstance(p[2],node.expr_list) + assert isinstance(p[5],node.expr_list) + p[0] = node.func_stmt(ident=p[4], + ret=p[2], + args=p[5]) + ret_expr = p[2] + else: + assert 0 + + +@exceptions +def p_funcall_expr(p): + """expr : expr LPAREN expr_list RPAREN + | expr LPAREN RPAREN + """ + if (0 and len(p)==5 and + len(p[3])==1 and + p[3][0].__class__ is node.expr and + p[3][0].op == ":" and not p[3][0].args): + # foo(:) => ravel(foo) + p[0] = node.funcall(func_expr=node.ident("ravel"), + args=node.expr_list([p[1]])) + else: + args = node.expr_list() if len(p) == 4 else p[3] + assert isinstance(args,node.expr_list) + p[0] = node.funcall(func_expr=p[1],args=args) + + +@exceptions +def p_global_list(p): + """global_list : ident + | global_list ident + """ + if len(p) == 2: + p[0] = node.global_list([p[1]]) + elif len(p) == 3: + p[0] = p[1] + p[0].append(p[2]) + + +@exceptions +def p_global_stmt(p): + """ + global_stmt : GLOBAL global_list SEMI + | GLOBAL ident EQ expr SEMI + """ + p[0] = node.global_stmt(p[2]) + for ident in p[0]: + ident.props="G" # G=global + + +@exceptions +def p_if_stmt(p): + """ + if_stmt : IF expr sep stmt_list_opt elseif_stmt END_STMT + | IF expr error stmt_list_opt elseif_stmt END_STMT + """ + p[0] = node.if_stmt(cond_expr=p[2], + then_stmt=p[4], + else_stmt=p[5]) + + +@exceptions +def p_lambda_args(p): + """lambda_args : LPAREN RPAREN + | LPAREN arg_list RPAREN + """ + p[0] = p[2] if len(p) == 4 else node.expr_list() + + +@exceptions +def p_lambda_expr(p): + """lambda_expr : HANDLE lambda_args expr + """ + p[0] = node.lambda_expr(args=p[2], ret=p[3]) + + +@exceptions +def p_matrix(p): + """matrix : LBRACKET RBRACKET + | LBRACKET concat_list RBRACKET + | LBRACKET concat_list SEMI RBRACKET + | LBRACKET expr_list RBRACKET + | LBRACKET expr_list SEMI RBRACKET + """ + if len(p) == 3: + p[0] = node.matrix() + else: + p[0] = node.matrix(p[2]) + + +@exceptions +def p_null_stmt(p): + """ + null_stmt : SEMI + | COMMA + """ + p[0] = node.null_stmt() + + +@exceptions +def p_paren_expr(p): + """ + expr : LPAREN expr RPAREN + """ + p[0] = node.expr(op="parens",args=node.expr_list([p[2]])) + + +@exceptions +def p_persistent_stmt(p): + """ + persistent_stmt : PERSISTENT global_list SEMI + | PERSISTENT ident EQ expr SEMI + """ + p[0] = node.null_stmt() +# if len(p) == 4: +# p[0] = node.global_stmt(p[2]) +# for ident in p[0]: +# ident.props="G" # G=global +# else: +# assert p[2].__class__ in (node.let,node.ident), p[2].__class__ +# p[0] = p[2] +# #print p[2] + + + +@exceptions +def p_ret(p): + """ + ret : ident + | LBRACKET RBRACKET + | LBRACKET expr_list RBRACKET + """ + if len(p) == 2: + p[0] = node.expr_list([p[1]]) + elif len(p) == 3: + p[0] = node.expr_list([]) + elif len(p) == 4: + assert isinstance(p[2],node.expr_list) + p[0] = p[2] + else: + assert 0 + for ident in p[0]: + ident.props="F" +# end func_decl + + +@exceptions +def p_return_stmt(p): + "return_stmt : RETURN SEMI" + p[0] = node.return_stmt(ret=ret_expr) + + +@exceptions +def p_semi_opt(p): + """ + semi_opt : + | semi_opt SEMI + | semi_opt COMMA + """ + pass + + +@exceptions +def p_separator(p): + """ + sep : COMMA + | SEMI + """ + p[0] = p[1] + + +@exceptions +def p_stmt(p): + """ + stmt : continue_stmt + | comment_stmt + | func_stmt + | break_stmt + | expr_stmt + | global_stmt + | persistent_stmt + | error_stmt + | command + | for_stmt + | if_stmt + | null_stmt + | return_stmt + | switch_stmt + | try_catch + | while_stmt + | foo_stmt + | unwind + """ + # END_STMT is intentionally left out + p[0] = p[1] + #print p[0] + + +@exceptions +def p_stmt_list(p): + """ + stmt_list : stmt + | stmt_list stmt + """ + if len(p) == 2: + p[0] = node.stmt_list([p[1]] if p[1] else []) + elif len(p) == 3: + p[0] = p[1] + if p[2]: + p[0].append(p[2]) + else: + assert 0 + + +@exceptions +def p_stmt_list_opt(p): + """ + stmt_list_opt : + | stmt_list + """ + if len(p) == 1: + p[0] = node.stmt_list() + else: + p[0] = p[1] + + +@exceptions +def p_switch_stmt(p): + """ + switch_stmt : SWITCH expr semi_opt case_list END_STMT + """ + def backpatch(expr,stmt): + if isinstance(stmt,node.if_stmt): + stmt.cond_expr.args[1] = expr + backpatch(expr,stmt.else_stmt) + backpatch(p[2],p[4]) + p[0] = p[4] + + +@exceptions +def p_transpose_expr(p): + # p[2] contains the exact combination of plain and conjugate + # transpose operators, such as "'.''.''''". + "expr : expr TRANSPOSE" + p[0] = node.transpose(p[1],node.string(p[2])) + + +@exceptions +def p_try_catch(p): + """ + try_catch : TRY stmt_list CATCH stmt_list END_STMT + | TRY stmt_list END_STMT + """ + assert isinstance(p[2],node.stmt_list) + #assert isinstance(p[4],node.stmt_list) + p[0] = node.try_catch(try_stmt=p[2], + catch_stmt=node.stmt_list(), # FIXME + finally_stmt=node.stmt_list()) + +@exceptions +def p_unwind(p): + """ + unwind : UNWIND_PROTECT stmt_list UNWIND_PROTECT_CLEANUP stmt_list END_UNWIND_PROTECT + """ + p[0] = node.try_catch(try_stmt=p[2], + catch_stmt=node.expr_list(), + finally_stmt=p[4]) + + +@exceptions +def p_while_stmt(p): + """ + while_stmt : WHILE expr SEMI stmt_list END_STMT + """ + assert isinstance(p[4],node.stmt_list) + p[0] = node.while_stmt(cond_expr=p[2], + stmt_list=p[4]) + +@exceptions +def p_error(p): + raise syntax_error(p) + +parser = yacc.yacc(start="top") + -def parse(buf,filename=""): - options.filename = filename +@exceptions +def parse(buf): try: new_lexer = lexer.new() - p = parser.parse(buf,tracking=1,debug=0,lexer=new_lexer) + p = parser.parse(buf, + tracking=1, + debug=options.debug_parser, + lexer=new_lexer) return p except lexer.IllegalCharacterError as (lineno,column,c): #import pdb; pdb.set_trace() - print 'Error:%s:%s.%s:illegal character:%s' % (filename,lineno,column,c) + print 'Error:%s:%s.%s:illegal character:%s' % (options.filename, + lineno,column,c) return [] except NotImplementedError as e: - print 'Error:%s:not implemented:%s' % (filename,e) + print 'Error:%s:not implemented:%s' % (options.filename,e) return [] except syntax_error as e: try: #import pdb;pdb.set_trace() column=e[0].lexpos - new_lexer.lexdata.rfind("\n",0,e[0].lexpos) - print '%s:%s.%s:syntax error %s=<%s>' % (filename, + print '%s:%s.%s:syntax error %s=<%s>' % (options.filename, e[0].lineno, column, e[0].type, e[0].value) - except: - print "%s:unexpected EOF" % filename + except Exception as ex: + print "Unexpected EOF or other error",ex return [] # def fparse(filename): # buf = open(filename).read() # return parse(buf) -# if p is None: -# if p not in opt_exclude: -# raise syntax_error(p) -# elif p.lexpos not in opt_exclude: -# raise syntax_error(p) # vim: ts=4:sw=4:et:si:ai diff --git a/smop/reord.py b/smop/reord.py new file mode 100644 index 00000000..837279f9 --- /dev/null +++ b/smop/reord.py @@ -0,0 +1,4 @@ +import sys +inp = sys.stdin.read().split("\f") +inp.sort() +sys.stdout.write("\f".join(inp)) diff --git a/smop/resolve.py b/smop/resolve.py index d049478f..dc025c0f 100644 --- a/smop/resolve.py +++ b/smop/resolve.py @@ -132,62 +132,31 @@ def copy_symtab(symtab): new_symtab[k] = copy.copy(v) return new_symtab -@extend(node.function) -def _resolve(self,symtab): - self.head._resolve(symtab) - self.body._resolve(symtab) - self.head.ret._resolve(symtab) - -@extend(node.global_list) -@extend(node.concat_list) -@extend(node.expr_list) -def _resolve(self,symtab): - for expr in self: - expr._resolve(symtab) -@extend(node.global_list) -@extend(node.concat_list) -@extend(node.expr_list) +@extend(node.arrayref) +@extend(node.cellarrayref) +@extend(node.funcall) def _lhs_resolve(self,symtab): - for expr in self: - expr._lhs_resolve(symtab) - -@extend(node.stmt_list) -def _resolve(self,symtab): - for stmt in self: - stmt._resolve(symtab) - -@extend(node.number) -@extend(node.string) -def _resolve(self,symtab): - pass - -# @extend(node.call_stmt) -# def _resolve(self,symtab): -# # TODO: does the order of A and B matter? Only if the -# # evaluation of function args may change the value of the -# # func_expr. -# self.func_expr._resolve(symtab) # A -# self.args._resolve(symtab) # B -# self.ret._lhs_resolve(symtab) - -@extend(node.let) + # Definitely lhs array indexing. It's both a ref and a def. + # Must properly handle cases such as foo(foo(17))=42 + # Does the order of A and B matter? + self.func_expr._resolve(symtab) # A + self.args._resolve(symtab) # B + self.func_expr._lhs_resolve(symtab) + +@extend(node.expr) def _lhs_resolve(self,symtab): - self.args._resolve(symtab) - self.ret._lhs_resolve(symtab) - -@extend(node.let) -def _resolve(self,symtab): - self.args._resolve(symtab) - self.ret._lhs_resolve(symtab) - -@extend(node.func_decl) + if self.op == ".": # see setfield + self.args._resolve(symtab) + self.args[0]._lhs_resolve(symtab) + elif self.op == "[]": + for arg in self.args: + arg._lhs_resolve(symtab) + +@extend(node.expr_stmt) def _resolve(self,symtab): - if self.ident: - self.ident._resolve(symtab) - self.args._lhs_resolve(symtab) - self.ret._resolve(symtab) - + self.expr._resolve(symtab) + @extend(node.for_stmt) def _resolve(self,symtab): symtab_copy = copy_symtab(symtab) @@ -198,7 +167,36 @@ def _resolve(self,symtab): # Handle the case where FOR loop is not executed for k,v in symtab_copy.items(): symtab.setdefault(k,set()).update(v) - + +@extend(node.func_stmt) +def _resolve(self,symtab): + if self.ident: + self.ident._resolve(symtab) + self.args._lhs_resolve(symtab) + self.ret._resolve(symtab) + +@extend(node.global_list) +@extend(node.concat_list) +@extend(node.expr_list) +def _lhs_resolve(self,symtab): + for expr in self: + expr._lhs_resolve(symtab) + +@extend(node.global_list) +@extend(node.concat_list) +@extend(node.expr_list) +def _resolve(self,symtab): + for expr in self: + expr._resolve(symtab) + +@extend(node.global_stmt) +def _resolve(self,symtab): + self.global_list._lhs_resolve(symtab) + +@extend(node.ident) +def _lhs_resolve(self,symtab): + symtab[self.name] = set([self]) + @extend(node.if_stmt) def _resolve(self,symtab): symtab_copy = copy_symtab(symtab) @@ -208,46 +206,34 @@ def _resolve(self,symtab): self.else_stmt._resolve(symtab_copy) for k,v in symtab_copy.items(): symtab.setdefault(k,set()).update(v) - -@extend(node.continue_stmt) # FIXME -@extend(node.break_stmt) # FIXME -def _resolve(self,symtab): - pass - -@extend(node.global_stmt) -def _resolve(self,symtab): - self.global_list._lhs_resolve(symtab) - -@extend(node.return_stmt) + +@extend(node.let) +def _lhs_resolve(self,symtab): + self.args._resolve(symtab) + self.ret._lhs_resolve(symtab) + +@extend(node.let) def _resolve(self,symtab): - self.ret._resolve(symtab) - #symtab.clear() - -@extend(node.expr_stmt) + self.args._resolve(symtab) + self.ret._lhs_resolve(symtab) + +@extend(node.null_stmt) +@extend(node.continue_stmt) +@extend(node.break_stmt) def _resolve(self,symtab): - self.expr._resolve(symtab) - -@extend(node.where_stmt) # FIXME where_stmt ??? -@extend(node.while_stmt) + pass + +@extend(node.setfield) # a subclass of funcall def _resolve(self,symtab): - symtab_copy = copy_symtab(symtab) - self.cond_expr._resolve(symtab) - self.stmt_list._resolve(symtab) - self.cond_expr._resolve(symtab) - self.stmt_list._resolve(symtab) - # Handle the case where WHILE loop is not executed - for k,v in symtab_copy.items(): - symtab.setdefault(k,set()).update(v) - + self.func_expr._resolve(symtab) + self.args._resolve(symtab) + self.args[0]._lhs_resolve(symtab) + @extend(node.try_catch) def _resolve(self,symtab): self.try_stmt._resolve(symtab) self.catch_stmt._resolve(symtab) # ??? - -@extend(node.ident) -def _lhs_resolve(self,symtab): - symtab[self.name] = set([self]) - + @extend(node.ident) def _resolve(self,symtab): if self.defs is None: @@ -257,7 +243,7 @@ def _resolve(self,symtab): except KeyError: # defs == set() means name used, but not defined pass - + @extend(node.arrayref) @extend(node.cellarrayref) @extend(node.funcall) @@ -269,37 +255,51 @@ def _resolve(self,symtab): self.args._resolve(symtab) #if self.ret: # self.ret._lhs_resolve(symtab) - -@extend(node.setfield) # a subclass of funcall -def _resolve(self,symtab): - self.func_expr._resolve(symtab) - self.args._resolve(symtab) - self.args[0]._lhs_resolve(symtab) - -@extend(node.arrayref) -@extend(node.cellarrayref) -@extend(node.funcall) -def _lhs_resolve(self,symtab): - # Definitely lhs array indexing. It's both a ref and a def. - # Must properly handle cases such as foo(foo(17))=42 - # Does the order of A and B matter? - self.func_expr._resolve(symtab) # A - self.args._resolve(symtab) # B - self.func_expr._lhs_resolve(symtab) - + @extend(node.expr) def _resolve(self,symtab): for expr in self.args: expr._resolve(symtab) + +@extend(node.number) +@extend(node.string) +@extend(node.comment_stmt) +def _resolve(self,symtab): + pass -@extend(node.expr) -def _lhs_resolve(self,symtab): - if self.op == ".": # see setfield - self.args._resolve(symtab) - self.args[0]._lhs_resolve(symtab) - elif self.op == "[]": - for arg in self.args: - arg._lhs_resolve(symtab) - +# @extend(node.call_stmt) +# def _resolve(self,symtab): +# # TODO: does the order of A and B matter? Only if the +# # evaluation of function args may change the value of the +# # func_expr. +# self.func_expr._resolve(symtab) # A +# self.args._resolve(symtab) # B +# self.ret._lhs_resolve(symtab) + +@extend(node.return_stmt) +def _resolve(self,symtab): + self.ret._resolve(symtab) + #symtab.clear() + +@extend(node.stmt_list) +def _resolve(self,symtab): + for stmt in self: + stmt._resolve(symtab) + +@extend(node.where_stmt) # FIXME where_stmt ??? +@extend(node.while_stmt) +def _resolve(self,symtab): + symtab_copy = copy_symtab(symtab) + self.cond_expr._resolve(symtab) + self.stmt_list._resolve(symtab) + self.cond_expr._resolve(symtab) + self.stmt_list._resolve(symtab) + # Handle the case where WHILE loop is not executed + for k,v in symtab_copy.items(): + symtab.setdefault(k,set()).update(v) + @extend(node.function) +def _resolve(self,symtab): + self.head._resolve(symtab) + self.body._resolve(symtab) + self.head.ret._resolve(symtab) - diff --git a/smop/test_lexer.py b/smop/test_lexer.py index 2299861f..dc9e1cbd 100644 --- a/smop/test_lexer.py +++ b/smop/test_lexer.py @@ -144,6 +144,7 @@ def test118(self): tok = self.lexer.next() self.assertEqual(tok.value,r'hello\world') + @unittest.skip("FIXME") def test119(self): "Quotes and backslashes in octave strings" self.lexer.input(r'"hello""world"') diff --git a/smop/test_primes.py b/smop/test_primes.py new file mode 100644 index 00000000..3fb29aee --- /dev/null +++ b/smop/test_primes.py @@ -0,0 +1,2 @@ +import octave +octave.primes(1000) diff --git a/smop/test_runtime.py b/smop/test_runtime.py new file mode 100644 index 00000000..6ff4b746 --- /dev/null +++ b/smop/test_runtime.py @@ -0,0 +1,132 @@ +# SMOP compiler runtime support library +# Copyright 2014 Victor Leikehman + +# MIT license +import numpy as np +import unittest +from runtime import * + +class Getitem(unittest.TestCase): + def setUp(self): + self.a = matlabarray(arange_(1,20).reshape(4,5,order="F")) + # 2-dim + def test01(self): + self.assertEqual(self.a[1,1],1) + + def test02(self): + self.assertEqual(self.a[4,5],20) + + def test03(self): + self.assertTrue(isequal_(self.a[:,:],self.a)) + + # 1-dim + def test04(self): + a = matlabarray(arange_(1,20).reshape(4,5,order="F")) + aa = a[:] + bb = matlabarray(arange_(1,20).reshape(-1,1,order="F")) + self.assertTrue(isequal_(aa,bb)) + + def test05(self): + #import pdb; pdb.set_trace() + z = [[11,22,33,44], + [11,22,33,44], + [11,22,33,44], + [11,22,33,44]] + a = matlabarray([11,22,33,44], dtype=int) + self.assertTrue(isequal_(a[ [1,1,1,1] , 1:4], matlabarray(z))) + self.assertTrue(isequal_(a[ [1,1,1,1], : ], matlabarray(z))) + #self.assertTrue(isequal_(a[ [[1,1,1,1]], 1:4], matlabarray([z]))) + + def test06(self): + a=copy_(0) + a[6]=666 + self.assertTrue(isequal_(a, [[0.,0.,0.,0.,0.,666.]])) + +class Expand(unittest.TestCase): + """ + Expand on index error + """ + def setUp(self): + self.a = matlabarray(zeros_(1,4)) + + def test01(self): + self.a[1,5]=1 + self.assertTrue(isequal_(self.a, + matlabarray([[0,0,0,0,1]]))) + + def test02(self): + #with self.assertRaises(IndexError): + a = matlabarray(zeros_(1,4)) + a[5]=1 # single index + self.assertTrue(isequal_(a, + matlabarray([[0,0,0,0,1]]))) + + #@unittest.skip("") + def test03(self): + a=zeros_(1,4) + a[1:10:4]=1 + "[[ 1. 0. 0. 0. 1. 0. 0. 0. 1. 0.]]" + + #@unittest.skip("") + def test04(self): + a=zeros_(1,4) + with self.assertRaises(IndexError): + a[5,5]=1 + b = matlabarray( + [[0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,1]]) + self.assertTrue(isequal_(a,b)) + +class Strread(unittest.TestCase): + def test01(self): + a = strread_("0.11 0.22 0.33") + self.assertTrue(isequal_(a,[[0.11,0.22,0.33]])) + + def test02(self): + a,b,c = strread_("0.11 0.22 0.33",nargout=3) + self.assertEqual(a,0.11) + self.assertEqual(b,0.22) + self.assertEqual(c,0.33) + +class Core(unittest.TestCase): + def setUp(self): + self.a = arange_(1,10).reshape(2,5,order="F") + + def test01(self): + b = abs_(-self.a) + self.assertTrue(isequal_(self.a,b)) + + def test02(self): + b = ceil_(self.a) + self.assertTrue(isequal_(self.a,b)) + + def test03(self): + b = false_(2,3) + self.assertTrue(isequal_(size_(b), [[2,3]])) + + def test_zeros(self): + self.assertEqual(zeros_(), 0.0) + self.assertTrue(isequal_(zeros_(2), zeros_(2,2))) + self.assertTrue(isequal_(zeros_(2,2), zeros_(2,2))) + + +#class Copy(unittest.TestCase): +# def setUp(self): +# self.a = zeros_(1,4) +# +# def test01(self): +# b=self.a.copy() +# print self.a +# print b +# self.assertTrue(isequal_(self.a,b)) +# b[1,1]=123 +# self.assertTrue(not isequal_(self.a,b)) +# #c=b.copy() +# #c[1,:]=1 +# #self.assertTrue(not isequal_(b,c)) + +if __name__ == "__main__": + unittest.main() diff --git a/smop/test_solver.py b/smop/test_solver.py new file mode 100644 index 00000000..53e8053e --- /dev/null +++ b/smop/test_solver.py @@ -0,0 +1,34 @@ +import pstats,cProfile +import numpy,time +from solver import * +from core import * + +def main(): + ai = matlabarray(zeros (10,10,dtype=int),dtype=int) + af = copy(ai) + + ai[1,1]=2 + ai[2,2]=3 + ai[3,3]=4 + ai[4,4]=5 + ai[5,5]=1 + + af[9,9]=1 + af[8,8]=2 + af[7,7]=3 + af[6,6]=4 + af[10,10]=5 + + t0 = time.clock() + mv = solver(ai,af,0) + t1 = time.clock() + print t1-t0 + print mv.shape + +if __name__ == '__main__': + main() + """ + cProfile.runctx('main()',globals(),locals(),"Profile.prof") + s = pstats.Stats("Profile.prof") + s.strip_dirs().sort_stats("time").print_stats() + """