From c1f332038e4419126b8f5c115c71f678d9582dba Mon Sep 17 00:00:00 2001 From: Victor Lei Date: Fri, 26 Aug 2016 20:06:22 +0300 Subject: [PATCH] WIP --- smop/Makefile | 54 +++++++++++++++++++------------------------------ smop/backend.py | 42 +++++++++++++++++++++----------------- smop/lexer.py | 20 ++++++++++++------ smop/main.py | 27 +++++++++++++++++-------- smop/node.py | 2 +- smop/options.py | 9 +++++++-- smop/parse.py | 17 +++++++++++++++- 7 files changed, 101 insertions(+), 70 deletions(-) diff --git a/smop/Makefile b/smop/Makefile index 5c857340..69859e9a 100644 --- a/smop/Makefile +++ b/smop/Makefile @@ -1,51 +1,40 @@ - # for py3: make PYTHON=python3 CYTHON="cython -3" V=3.4 -VPATH = $(SCRIPTS)/strings $(SCRIPTS)/specfun OCTAVE = /home/lei/octave-4.0.2 SCRIPTS = $(OCTAVE)/scripts CYTHON = cython PYTHON = python -XFILES = -x inputParser.m,quadgk.m,quadl.m,triplequad.m,dblquad.m,reallog.m,nthroot.m,factorial.m -FLAGS = -V = 2.7 -ALL = \*.m +XFILES = -x inputParser -factorial.py: factorial.m - $(PYTHON) main.py -v $^ $(FLAGS) -all: mybench.py - $(PYTHON) -c "import mybench ; mybench.mybench()" +#m,uiputfile.m,uigetfile.m,qmr.m -#octave.py: primes.m interp2.m meshgrid.m repmat.m -# $(PYTHON) main.py --callgraph $(FLAGS) $^ -r core -o $@ -# dot -Tpdf -o G.pdf G.dot +#quadgk.m,quadl.m,triplequad.m,dblquad.m +#installed_packages.m,stft.m,legend.m,__plt_get_axis_arg__.m,rotate.m,print.m,__ghostscript__.m,__gnuplot_print__.m,set.m,edit.m,what.m,usejava.m,javachk.m,__go_draw_axes__.m,interp3.m,interp2.m,interpn.m,randi.m,interp1.m,spdiags.m,importdata.m,stemleaf.m +FLAGS = +MYFLAGS=--ignore=999 -#test_primes: octave.py -# $(PYTHON) -c "import octave ; octave.primes(1000)" +V = 2.7 -octave.py: - find $(SCRIPTS)/general $(SCRIPTS)/specfun -name \*.m | xargs python main.py -o $@ $(FLAGS) $(XFILES) +all: + make -B FLAGS= liboctave.py | wc + make -B FLAGS=-C liboctave.py | wc + make -B FLAGS=-N liboctave.py | wc + make -B FLAGS=-T liboctave.py | wc + make -B FLAGS=-CN liboctave.py | wc + make -B FLAGS=-TN liboctave.py | wc + make -B FLAGS=-CT liboctave.py | wc + make -B FLAGS=-CTN liboctave.py | wc +liboctave.py: + find $(SCRIPTS) -name \*.m | xargs $(PYTHON) main.py -o $@ $(MYFLAGS) $(FLAGS) $(XFILES) $^ + #$(PYTHON) $@ clean: rm -f a.* *.pyc solver.so solver.py octave.so -solver.py: solver.m #r8_random.m octave.py - $(PYTHON) main.py $^ -o $@ - -check: solver.py - $(PYTHON) test_core.py - $(PYTHON) test_matlabarray.py - $(PYTHON) test_sparsearray.py - $(PYTHON) test_lexer.py - $(PYTHON) test_solver.py -# $(PYTHON) test_primes.py - -#$(PYTHON) main.py $(FLAGS) -r core solver.m r8_random.m -o solver.py && python go.py -#test: -# find $(SCRIPTS) -name $(ALL) | xargs python main.py $(FLAGS) - +regress: + make | sort -u | wc %.c: %.py $(CYTHON) $^ @@ -53,7 +42,6 @@ check: solver.py gcc -Wno-cpp -I /usr/include/python$V -O2 -shared -o $@ $^ %.py: %.m - #smop $(FLAGS) -o $@ $^ $(PYTHON) main.py -o $@ $^ $(PYTHON) $@ diff --git a/smop/backend.py b/smop/backend.py index 25c4883d..d5a6a34c 100644 --- a/smop/backend.py +++ b/smop/backend.py @@ -26,6 +26,7 @@ "||": "or", "&&": "and", "^" : "**", + "**": "**", ".^": "**", "./": "/", ".*": "*", @@ -125,15 +126,18 @@ def _backend(self,level=0): @extend(node.expr) def _backend(self,level=0): - if self.op in ("!","not"): # ??? - return "not %s" % self.args[0]._backend() - if self.op in ("&","and"): + if self.op in ("!","~"): + return "logical_not(%s)" % self.args[0]._backend() + + if self.op == "&": return "logical_and(%s)" % self.args._backend() + if self.op == "&&": return "%s and %s" % (self.args[0]._backend(), self.args[1]._backend()) - if self.op in ("|","or"): + if self.op == "|": return "logical_or(%s)" % self.args._backend() + if self.op == "||": return "%s or %s" % (self.args[0]._backend(), self.args[1]._backend()) @@ -216,17 +220,18 @@ def _backend(self,level=0): @extend(node.func_stmt) def _backend(self,level=0): - 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 - + if (self.args and + isinstance(self.args[-1],node.ident) and + self.args and + self.args[-1].name == "varargin"): + self.args[-1].name = "*varargin" s = """ @function -def {0}({1}): +def %s(%s): nargin = sys._getframe(1).f_locals["nargin"] -""".format(self.ident._backend(), - self.args._backend()) + varargin = cellarray(varargin) +""" % (self.ident._backend(), + self.args._backend()) return s @@ -251,7 +256,7 @@ def _backend(self,level=0): @extend(node.ident) def _backend(self,level=0): - return self.name if self.name not in reserved else self.name+'_' + return (self.name if self.name not in reserved else self.name+'_') + ("=" + self.init._backend() if self.init is not None else '') @extend(node.if_stmt) def _backend(self,level=0): @@ -365,12 +370,11 @@ def _backend(self,level=0): @extend(node.string) def _backend(self,level=0): -# s = self.value.strip() -# if not s: -# return "" -# if s[0] in "%#": -# return "\n"+s.replace("%","#") - return '"%s"' % self.value.encode("string_escape").replace('"','\\"') + if "\n" in self.value: + fmt = '"""%s"""' + else: + fmt = '"%s"' + return fmt % self.value @extend(node.sub) def _backend(self,level=0): diff --git a/smop/lexer.py b/smop/lexer.py index 5e068267..ba543840 100644 --- a/smop/lexer.py +++ b/smop/lexer.py @@ -20,7 +20,7 @@ class IllegalCharacterError(Exception): "MINUS","MINUSMINUS","MINUSEQ","MUL","MULEQ","NE", "NEG", "NUMBER", "OR","OREQ", "OROR", "PLUS", "PLUSEQ","PLUSPLUS", "RBRACE", "RBRACKET", "RPAREN", "SEMI", "STRING", - "TRANSPOSE", "ERROR_STMT", "COMMENT", "END_FUNCTION", ] + "TRANSPOSE", "ERROR_STMT", "COMMENT", "END_FUNCTION","POW", ] reserved = { "break" : "BREAK", @@ -73,6 +73,7 @@ def new(): t_MINUSEQ = r"\-=" t_MINUSMINUS = r"\--" t_MUL = r"\*" + t_POW = r"\*\*" t_MULEQ = r"\*=" t_NE = r"(~=)|(!=)" t_NEG = r"\~|\!" @@ -99,10 +100,16 @@ def unescape(s): """ ffd52d5fc5 """ - if s[0] == "'": - return s[1:-1].replace("''","'").decode("string_escape") - else: - return s[1:-1].replace('""','"').decode("string_escape") + try: + if s == r"'\'" or s == r'"\"': + return s[1:-1] + if s[0] == "'": + return s[1:-1].replace("''","'").decode("string_escape") + else: + return s[1:-1].replace('""','"').decode("string_escape") + except ValueError: + print s + raise @TOKEN(mos) def t_afterkeyword_STRING(t): @@ -241,7 +248,8 @@ def t_NEWLINE(t): return t def t_ERROR_STMT(t): - r"%!error.*\n" + r"%!(error|warning|test).*\n" + t.lexer.lineno += 1 # keep multiline comments def t_COMMENT(t): diff --git a/smop/main.py b/smop/main.py index be0e8033..ff5f9c4e 100644 --- a/smop/main.py +++ b/smop/main.py @@ -2,7 +2,7 @@ # Copyright 2011-2016 Victor Leikehman from os.path import splitext,basename import version -import sys,cPickle,glob,os +import sys,cPickle,glob,os,tarfile import getopt,re import lexer import parse @@ -17,9 +17,14 @@ import graphviz def main(): + tar = None if not options.filelist: options.parser.print_help() return + if (len(options.filelist) == 1 and + options.filelist[0].endswith(".tar")): + tar = tarfile.open(options.filelist[0]) + options.filelist = tar.getnames() if options.output == "-": fp = sys.stdout elif options.output: @@ -39,19 +44,24 @@ def main(): for i, options.filename in enumerate(options.filelist): try: - if not options.filename.endswith((".m",".tst")): - print "\tIgnored file: '%s' (unexpected file type)" % options.filename + if not options.filename.endswith((".m")): + if options.verbose: + print "\tIgnored: '%s' (unexpected file type)" % options.filename continue if os.path.basename(options.filename) in options.xfiles: - print "\tExcluded file: '%s'" % options.filename + print "\tExcluded: '%s'" % options.filename continue if options.verbose: print options.filename - buf = open(options.filename).read().replace("\r\n","\n") + if tar: + buf = tar.extractfile(options.filename).read() + else: + buf = open(options.filename).read() + buf = buf.replace("\r\n","\n") buf = buf.decode("ascii",errors="ignore") 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: + if not stmt_list: return if not options.no_resolve: G = resolve.resolve(stmt_list) @@ -59,9 +69,10 @@ def main(): s = backend.backend(stmt_list) print >> fp, s except Exception as e: - print e - if options.strict: + print "\tFailed: ",options.filename + if not options.ignore: raise + options.ignore -= 1 if __name__ == "__main__": main() diff --git a/smop/node.py b/smop/node.py index 487de3b5..e1f1f6a4 100644 --- a/smop/node.py +++ b/smop/node.py @@ -123,7 +123,7 @@ class number(atom,recordtype("number","value lineno lexpos",default=None)): def __str__(self): return str(self.value) -class ident(atom,recordtype("ident","name lineno column lexpos defs props", +class ident(atom,recordtype("ident","name lineno column lexpos defs props init", default=None)): def __str__(self): return self.name diff --git a/smop/options.py b/smop/options.py index 785db723..926f36d9 100644 --- a/smop/options.py +++ b/smop/options.py @@ -9,7 +9,7 @@ smop [options][file.m ...file.m][-l file.py...] or - python -m smop.main [options][file.m ...file.m][-l file.py...] + smop [options] library.tar [-l file.py...] """, description= """ SMOP is Small Matlab and Octave to Python compiler. @@ -19,7 +19,11 @@ compiled file unless explicitly set with -o . Additional libraries can be specified with -l (lowercase L), for example -loctave.py.""", -# epilog= +epilog="""Hint: put into your Makefile the following rule + +%.py: %.m + $(SMOP) $^ $(FLAGS) + ($(PYTHON) $@ && cat $@ >> libscripts.py)""", formatter_class=argparse.RawTextHelpFormatter, ) @@ -88,6 +92,7 @@ write Octave test suite. When disabled, behaves like regular comments.""") +parser.add_argument("-I", "--ignore", type=int) args = parser.parse_args(namespace=sys.modules[__name__]) diff --git a/smop/parse.py b/smop/parse.py index 5fb64389..4f880fd5 100644 --- a/smop/parse.py +++ b/smop/parse.py @@ -52,7 +52,7 @@ class syntax_error(error): ("left", "MUL","DIV","DOTMUL","DOTDIV","BACKSLASH"), ("right","UMINUS","NEG"), ("right","TRANSPOSE"), - ("right","EXP", "DOTEXP"), + ("right","EXP", "DOTEXP", "POW"), ("nonassoc","LPAREN","RPAREN","RBRACE","LBRACE"), ("left", "FIELD","DOT","PLUSPLUS","MINUSMINUS"), ) @@ -137,6 +137,7 @@ def p_args_opt(p): | LPAREN RPAREN | LPAREN expr_list RPAREN """ + flag = False if len(p) == 1: p[0] = node.expr_list() elif len(p) == 3: @@ -144,9 +145,18 @@ def p_args_opt(p): elif len(p) == 4: assert isinstance(p[2],node.expr_list) p[0] = p[2] + flag = True else: assert 0 + if flag: + t = p[2][-1] + if isinstance(t,node.ident) and t.name=="varargin": + t.name = "*varargin" + for t in p[2]: + if isinstance(t,node.ident) and t.name != '*varargin': + t.init = node.ident("None") + @exceptions def p_break_stmt(p): @@ -323,6 +333,7 @@ def p_expr2(p): | expr DOTMUL expr | expr DOTMULEQ expr | expr EQEQ expr + | expr POW expr | expr EXP expr | expr EXPEQ expr | expr GE expr @@ -831,6 +842,10 @@ def p_while_stmt(p): @exceptions def p_error(p): + if p.type == "COMMENT": + #print "Discarded comment", p.value + parser.errok() + return raise syntax_error(p) parser = yacc.yacc(start="top")