diff --git a/SMOP.rst b/SMOP.rst index 4e03cbb6..e549065e 100644 --- a/SMOP.rst +++ b/SMOP.rst @@ -105,9 +105,13 @@ b. A quote, immediately following any of: (1) an alphanumeric Keyword ``end`` -- expression and statement ------------------------------------------- -Any of: ``endwhile``, etc. are ``END_STMT``. Otherwise, -if ``end`` appears inside parentheses of any kind, -it's ``END_EXPR``. Otherwise, ``end`` is illegal. +Any of: ``endwhile``, etc. are ``END_STMT``. Otherwise, lonely ``end`` +is a keyword ``END_EXPR``. It is not allowed to be used as a variable, +except if appears inside subscripts, in which case it keeps the upper +bound of the corresponding dimension. It is frequently used with +the auto-expanding array idiom:: + + a(end+1) = b Optional ``end`` statement as function terminator ------------------------------------------------- @@ -137,26 +141,26 @@ if it was ``[1,2,3]``. Handle with care: ``[x + y]`` vs ``[x +y]`` Term T is:: - #. a name or a number - #. literal string enclosed in single or double quotes - #. (T) or [T] or {T} or T' or +T or -T + 1. a name or a number + 2. literal string enclosed in single or double quotes + 3. (T) or [T] or {T} or T' or +T or -T Terms end with:: - #. an alphanumeric charater \w - #. single quote (in octave also double-quote) - #. right parenthesis, bracket, or brace - #. a dot (after a number, such as 3. + 1. an alphanumeric charater \w + 2. single quote (in octave also double-quote) + 3. right parenthesis, bracket, or brace + 4. a dot (after a number, such as 3. The pattern for whitespace accounts for ellipsis as a whitespace, and for the trailing junk. Terms start with:: - #. an alphanumeric character - #. a single or double quote, - #. left paren, bracket, or brace and finally - #. a dot before a digit, such as .3 . + 1. an alphanumeric character + 2. a single or double quote, + 3. left paren, bracket, or brace and finally + 4. a dot before a digit, such as .3 . TODO: what about curly brackets ??? TODO: what about dot followed by a letter, as in field @@ -499,4 +503,29 @@ instance). ---------------------------------------------------------------------- + +Git hacks +--------- +:: + + git difftool --tool + +where ``tool`` is ``meld`` or ``kdiff3`` + +Vim hacks +--------- +:: + + http://learnvimscriptthehardway.stevelosh.com + https://www.ibm.com/developerworks/library/l-vim-script-1/index.html + https://devhints.io/vimscript + http://andrewscala.com/vimscript/ + +Pdf hacks +--------- +:: + + https://www.geeksforgeeks.org/working-with-pdf-files-in-python/ + + .. vim: tw=70:sw=2 diff --git a/run.py b/run.py new file mode 100644 index 00000000..9fb9d8aa --- /dev/null +++ b/run.py @@ -0,0 +1,4 @@ +from smop.main import main + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index f1de112f..7ebffed9 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,14 @@ import os from setuptools import setup -#from smop.version import __version__ as __VERSION__ +from smop.version import __version__ as __VERSION__ -try: - __VERSION__ = os.popen("git describe --tags").read().strip() -except OSError as e: - __VERSION__ = "''" - -open("smop/version.py","w").write("__version__='%s'" % __VERSION__) +#try: +# __VERSION__ = os.popen("git describe --tags").read().strip() +#except OSError as e: +# __VERSION__ = "" +# +#open("smop/version.py","w").write("__version__='%s'" % __VERSION__) setup( author = 'Victor Leikehman', diff --git a/smop/Makefile b/smop/Makefile index 2d44ace2..69938b79 100644 --- a/smop/Makefile +++ b/smop/Makefile @@ -12,17 +12,25 @@ COVERAGE = python3 -m coverage CYTHON = cython PYTHON = python$V XFILES = -x inputParser.m,dblquad.m,triplequad.m - +SMOP = smop SRCSMOP = main.py parse.py backend.py resolve.py options.py FLAGS = MYFLAGS= +PYTEST = python -m pytest #V = 2.7 V = 3 -all: - make -B FLAGS= liboctave.py +all: solver.py + $(PYTEST) test_matlabarray.py + $(PYTEST) test_parse.py + $(PYTEST) test_lexer.py + $(PYTEST) test_solver.py + #$(PYTEST) test_primes.py + #make -B FLAGS= liboctave.py + #$(COVERAGE) run -p main.py solver.m + #$(COVERAGE) combine foo: make -B FLAGS=-C liboctave.py make -B FLAGS=-N liboctave.py @@ -37,7 +45,7 @@ liboctave.py: #$(PYTHON) $@ clean: - rm -f a.* *.pyc solver.so solver.py octave.so + rm -f a.* *.pyc solver.so solver.py octave.so *.py,cover regress: make | sort -u | wc @@ -48,8 +56,7 @@ regress: gcc -Wno-cpp -I /usr/include/python$V -O2 -shared -o $@ $^ %.py: %.m - $(PYTHON) main.py -o $@ $^ - #$(PYTHON) $@ + $(SMOP) $^ %.pdf: %.dot dot -Tpdf -o $@ $^ diff --git a/smop/main.py b/smop/main.py index 9caac87a..bd554612 100644 --- a/smop/main.py +++ b/smop/main.py @@ -21,7 +21,7 @@ def print_header(fp): if options.no_header: return - print("# Running Python %s" % sys.version, file=fp) + #print("# Running Python %s" % sys.version, file=fp) print("# Generated with SMOP ", version.__version__, file=fp) print("from libsmop import *", file=fp) print("#", options.filename, file=fp) @@ -84,6 +84,3 @@ def main(): pass if nerrors: print("Errors:", nerrors) - -if __name__ == "__main__": - main() diff --git a/smop/graphviz.py b/smop/mygraphviz.py similarity index 63% rename from smop/graphviz.py rename to smop/mygraphviz.py index 7c8dfe96..8c987bfa 100644 --- a/smop/graphviz.py +++ b/smop/mygraphviz.py @@ -1,26 +1,11 @@ -import parse,sys -import node -from node import extend +import sys +import pickle import networkx as nx - - - -def resolve(t,fp,func_name): - fp.write("digraph %s {\n" % func_name) - fp.write('graph [rankdir="LR"];\n') - for u in node.postorder(t): - if u.__class__ in (node.ident,node.param): - fp.write("%s [label=%s_%s_%s];\n" % (u.lexpos,u.name,u.lineno,u.column)) - if u.defs: - for v in u.defs: - fp.write("%s -> %s" % (u.lexpos,v.lexpos)) - if u.lexpos < v.lexpos: - fp.write('[color=red]') - #else: - # fp.write('[label=%s.%s]' % (v.lineno,v.column)) - fp.write(';\n') - fp.write("}\n") +from . import node +from . import resolve +from . node import extend + @extend(node.node) def _graphviz(self,fp): if getattr(self,"__slots__",False): @@ -58,6 +43,3 @@ def graphviz(tree,fp): for u in node.postorder(tree): u._graphviz(fp) fp.write("}\n") - -if __name__ == '__main__': - main() diff --git a/smop/options.py b/smop/options.py index e5b9a411..97aebe7b 100644 --- a/smop/options.py +++ b/smop/options.py @@ -13,11 +13,10 @@ """, description= """ -SMOP is Small Matlab and Octave to Python -compiler, it takes MATLAB files and translates -them to Python. The names of the resulting -files are derived from the names of the source -files unless explicitly set with -o .""", +SMOP is Small Matlab and Octave to Python compiler, it takes MATLAB +files and translates them to Python. The names of the resulting files +are derived from the names of the source files unless explicitly set +with -o .""", epilog=""" Example: @@ -30,129 +29,87 @@ formatter_class=argparse.RawTextHelpFormatter, ) -parser.add_argument("-a", "--archive", - metavar="archive.tar", -help="""Read .m files from the archive. -Accepted formats: tar either uncompressed, -or compressed using gzip or bz2.""") - - -parser.add_argument("-g", "--glob-pattern", - metavar="PATTERN", - type=str, -help="""Apply unix glob pattern to the input file -list or to the archived files. For example -g -'octave-4.0.2/*.m' - -Quoted from fnmatch docs: - -Note that the filename separator ('/' on Unix) -is not special to this module. [...] Similarly, -filenames starting with a period are not special -for this module, and are matched by the * and ? -patterns. """) - -parser.add_argument("-o", "--output", - metavar="file.py", - type=str, -help="""Write the results to file.py. Use -o- -to send the results to the standard output. -If not specified explicitly, output file names -are derived from input file names by replacing -".m" with ".py". For example, - - $ smop filex.m filey.m filez.m - -generates files filex.py filey.py and filez.py""") - -# parser.add_argument("-l", "--link", -# metavar="file.py", -# help="""Import file.py . File core.py is -# always imported. For example, - -# smop test_primes.m -l octave.py - -# Option -l can be specified several times.""") - -parser.add_argument("-s", "--strict", - action="store_true", -help="""stop after first syntax error (by -default compiles other .m files)""") - -parser.add_argument("-V", '--version', - action='version', - version=__version__) - -parser.add_argument("-v", "--verbose", - action="store_true") - -parser.add_argument("-x", "--exclude", - metavar="filex.m,filey.m,filez.m", - type=str, -help="""comma-separated list of files to ignore""") - -parser.add_argument("-d", "--debug", -help="""Colon-separated codes. + +parser.add_argument("filelist", nargs="*", metavar="file.m", type=str) + +parser.add_argument("-A","--no-analysis", action="store_true", help=""" +skip analysis +""") + +parser.add_argument("-B","--no-backend", action="store_true", help=""" +omit code generation +""") + +parser.add_argument("-C","--no-comments", action="store_true", help=""" +discard multiline comments""") + +parser.add_argument("-D", "--debug", help=""" +Colon-separated codes. M Main L Lex P Parse """) -parser.add_argument("-L", "--debug-lexer", - action="store_true", -help="enable built-in debugging tools-") - -parser.add_argument("-P", "--debug-parser", - action="store_true", -help="enable built-in debugging tools") - -parser.add_argument("filelist", nargs="*", - metavar="file.m", type=str) - -parser.add_argument("-D","--delete-on-error", - action="store_false", -help="""By default, broken py-files are -kept alive to allow their examination and -debugging. Borrowed from gnu make option of -the same name and functionality. - - $ smop -v --delete-on-error *.m - $ rm -f libscripts.py - $ cat *.py > libscripts.py - $ python -... - >>> from libscripts import * - >>> factorial(9) - 362880.0 - >>> primes(9) -Oops, wrong results. +parser.add_argument("-E","--delete-on-error", action="store_false", help=""" +By default, broken ".py" files are kept alive to allow their +examination and debugging. Sometimes we want the opposite behavior""") + +parser.add_argument("-g", "--glob-pattern", metavar="PATTERN", type=str, help=""" +Apply unix glob pattern to the input file list or to files. For +example -g 'octave-4.0.2/*.m""") + +parser.add_argument("-H","--no-header", action="store_true", help=""" +use it if you plan to concatenate the generated files +""") + +parser.add_argument("-L", "--debug-lexer", action="store_true", help=""" +enable built-in debugging tools +""") + +parser.add_argument("-N", "--no-numbers", action="store_true", help=""" +discard line-numbering information +""") + +parser.add_argument("-o", "--output", metavar="FILE.py", type=str, help=""" +Write the results to FILE.py. Use -o- to send the results to the +standard output. If not specified explicitly, output file names are +derived from input file names by replacing ".m" with ".py". For example, + + $ smop FILE1.m FILE2.m FILE3.m + +generates files FILE1.py FILE2.py and FILE3.py +""") + +parser.add_argument("-P", "--debug-parser", action="store_true", help=""" +enable built-in debugging tools +""") + +parser.add_argument("-R","--no-resolve", action="store_true", help=""" +omit name resolution +""") + +parser.add_argument("-S", "--strict", action="store_true", help=""" +stop after first syntax error (by default compiles other .m files) +""") + + +parser.add_argument("-T","--testing-mode", action="store_true", help= """ +support special "testing" percent-bang comments used to write Octave +test suite. When disabled, behaves like regular comments +""") + +parser.add_argument("-x", "--exclude", metavar="FILE1.m,FILE2.m,FILE3.m", type=str, help=""" +comma-separated list of files to ignore +""") +parser.add_argument("-V", '--version', action='version', version=__version__) + +parser.add_argument("-v", "--verbose", action="store_true") + +parser.add_argument("-Z", "--archive", metavar="ARCHIVE.tar", help=""" +Read ".m" files from the archive; ignore other files. Accepted +format: "tar". Accepted compression: "gzip", "bz2". """) -parser.add_argument("-H","--no-header", - action="store_true", -help="""use it if you plan to concatenate -the generated files.""") - -parser.add_argument("-C","--no-comments", - action="store_true", -help="""discard multiline comments""") - -parser.add_argument("-N", "--no-numbers", - action="store_true", -help="""discard line-numbering information""") - -parser.add_argument("-B","--no-backend", - action="store_true", -help="omit code generation") -parser.add_argument("-R","--no-resolve", - action="store_true", -help="omit name resolution") - -parser.add_argument("-T","--testing-mode", - action="store_true", -help= """support special "testing" percent-bang -comments used to write Octave test suite. -When disabled, behaves like regular comments.""") args = parser.parse_args(namespace=sys.modules[__name__]) diff --git a/smop/resolve.py b/smop/resolve.py index 02cdd5f9..1e4b16f6 100644 --- a/smop/resolve.py +++ b/smop/resolve.py @@ -19,29 +19,11 @@ """ import copy +import networkx as nx from . import node from . node import extend - -def graphviz(t, fp, func_name): - fp.write("digraph %s {\n" % func_name) - fp.write('graph [rankdir="LR"];\n') - for u in node.postorder(t): - if u.__class__ in (node.ident, node.param): - fp.write("%s [label=%s_%s_%s];\n" % - (u.lexpos, u.name, u.lineno, u.column)) - if u.defs: - for v in u.defs: - fp.write("%s -> %s" % (u.lexpos, v.lexpos)) - if u.lexpos < v.lexpos: - fp.write('[color=red]') - # else: - # fp.write('[label=%s.%s]' % (v.lineno,v.column)) - fp.write(';\n') - fp.write("}\n") - - def as_networkx(t): G = nx.DiGraph() for u in node.postorder(t): @@ -51,81 +33,38 @@ def as_networkx(t): G.add_node(uu, ident=u) if u.defs: for v in u.defs: - assert type(v) is node.ident, type(v) - vv = "%s_%s_%s" % (v.name, v.lineno, v.column) - G.add_node(vv, ident=v) - if u.lexpos < v.lexpos: - G.add_edge(uu, vv, color="red") - else: - G.add_edge(uu, vv, color="black") + if v.__class__ is node.ident: + vv = "%s_%s_%s" % (v.name, v.lineno, v.column) + G.add_node(vv, ident=v) + if u.lexpos < v.lexpos: + G.add_edge(uu, vv, color="red") + else: + G.add_edge(uu, vv, color="black") return G + def resolve(t, symtab=None, fp=None, func_name=None): if symtab is None: symtab = {} do_resolve(t,symtab) - #G = as_networkx(t) - #import pdb;pdb.set_trace() -# for n in G.nodes(): -# u = G.node[n]["ident"] -# if u.props: -# pass -# elif G.out_edges(n) and G.in_edges(n): -# u.props = "U" # upd -# #print u.name, u.lineno, u.column -# elif G.in_edges(n): -# u.props = "D" # def -# elif G.out_edges(n): -# u.props = "R" # ref -# else: -# u.props = "F" # ??? -# G.node[n]["label"] = "%s\\n%s" % (n, u.props) - - for u in node.postorder(t): - #if u.__class__ is node.func_decl: - # u.ident.name += "_" - if u.__class__ is node.funcall: - try: - if u.func_expr.props in "UR": # upd,ref - u.__class__ = node.arrayref - #else: - # u.func_expr.name += "_" - except: - pass - - for u in node.postorder(t): - if u.__class__ in (node.arrayref,node.cellarrayref): - for i,v in enumerate(u.args): - if v.__class__ is node.expr and v.op == ":": - v.op = "::" -# for w in node.postorder(v): -# if w.__class__ is node.expr and w.op == "end": -# w.args[0] = u.func_expr -# w.args[1] = node.number(i) - - for u in node.postorder(t): - if u.__class__ is node.let: - if (u.ret.__class__ is node.ident and - u.args.__class__ is node.matrix): - u.args = node.funcall(func_expr=node.ident("matlabarray"), - args=node.expr_list([u.args])) - -# H = nx.connected_components(G.to_undirected()) -# for i,component in enumerate(H): -# for nodename in component: -# if G.node[nodename]["ident"].props == "R": -# has_update = 1 -# break -# else: -# has_update = 0 -# if has_update: -# for nodename in component: -# G.node[nodename]["ident"].props += "S" # sparse -# #S = G.subgraph(nbunch) -# #print S.edges() -# return G - + G = as_networkx(t) + for n in G.nodes(): + print(n.__class__.__name__) + u = G.node[n]["ident"] + if u.props: + pass + elif G.out_edges(n) and G.in_edges(n): + u.props = "U" # upd + #print u.name, u.lineno, u.column + elif G.in_edges(n): + u.props = "D" # def + elif G.out_edges(n): + u.props = "R" # ref + else: + u.props = "F" # ??? + G.node[n]["label"] = "%s\\n%s" % (n, u.props) + return G def do_resolve(t,symtab): t._resolve(symtab) @@ -286,12 +225,12 @@ def _resolve(self,symtab): 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): @@ -303,7 +242,8 @@ def _resolve(self,symtab): # Handle the case where WHILE loop is not executed for k,v in symtab_copy.items(): symtab.setdefault(k,[]).append(v) - @extend(node.function) + +@extend(node.function) def _resolve(self,symtab): self.head._resolve(symtab) self.body._resolve(symtab) diff --git a/smop/rewrite.py b/smop/rewrite.py new file mode 100644 index 00000000..f32f6422 --- /dev/null +++ b/smop/rewrite.py @@ -0,0 +1,272 @@ +# smop -- Matlab to Python compiler +# Copyright 2018 Victor Leikehman + +import copy + +from . import node +from . node import extend + +def graphviz(t, fp, func_name): + fp.write("digraph %s {\n" % func_name) + fp.write('graph [rankdir="LR"];\n') + for u in node.postorder(t): + if u.__class__ in (node.ident, node.param): + fp.write("%s [label=%s_%s_%s];\n" % + (u.lexpos, u.name, u.lineno, u.column)) + if u.defs: + for v in u.defs: + fp.write("%s -> %s" % (u.lexpos, v.lexpos)) + if u.lexpos < v.lexpos: + fp.write('[color=red]') + # else: + # fp.write('[label=%s.%s]' % (v.lineno,v.column)) + fp.write(';\n') + fp.write("}\n") + + +def peep(parsetree): + for u in parsetree: + to_arrayref(u) + colon_indices_and_expressions(u) + end_expressions(u) + let_statement(u) + +def to_arrayref(u): + """ + To the parser, funcall is indistinguishable + from rhs array reference. But LHS references + can be converted to arrayref nodes. + """ + if u.__class__ is node.funcall: + try: + if u.func_expr.props in "UR": # upd,ref + u.__class__ = node.arrayref + except: + pass # FIXME + +def colon_subscripts(u): + """ + Array colon subscripts foo(1:10) and colon expressions 1:10 look + too similar to each other. Now is the time to find out who is who. + """ + if u.__class__ in (node.arrayref,node.cellarrayref): + for w in u.args: + if w.__class__ is node.expr and w.op == ":": + w._replace(op="::") + +def end_expressions(u): + if u.__class__ in (node.arrayref,node.cellarrayref): + if w.__class__ is node.expr and w.op == "end": + w.args[0] = u.func_expr + w.args[1] = node.number(i) # FIXME + +def let_statement(u): + """ + If LHS is a plain variable, and RHS is a matrix + enclosed in square brackets, replace the matrix + expr with a funcall. + """ + if u.__class__ is node.let: + if (u.ret.__class__ is node.ident and + u.args.__class__ is node.matrix): + u.args = node.funcall(func_expr=node.ident("matlabarray"), + args=node.expr_list([u.args])) + +# H = nx.connected_components(G.to_undirected()) +# for i,component in enumerate(H): +# for nodename in component: +# if G.node[nodename]["ident"].props == "R": +# has_update = 1 +# break +# else: +# has_update = 0 +# if has_update: +# for nodename in component: +# G.node[nodename]["ident"].props += "S" # sparse +# #S = G.subgraph(nbunch) +# #print S.edges() +# return G + + +def do_resolve(t,symtab): + t._resolve(symtab) + +def copy_symtab(symtab): + new_symtab = copy.copy(symtab) + for k,v in new_symtab.items(): + new_symtab[k] = copy.copy(v) + return new_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 _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.expr_stmt) +def _resolve(self,symtab): + self.expr._resolve(symtab) + +@extend(node.for_stmt) +def _resolve(self,symtab): + symtab_copy = copy_symtab(symtab) + self.ident._lhs_resolve(symtab) + self.expr._resolve(symtab) + self.stmt_list._resolve(symtab) + self.stmt_list._resolve(symtab) # 2nd time, intentionally + # Handle the case where FOR loop is not executed + for k,v in symtab_copy.items(): + symtab.setdefault(k,[]).append(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] = [self] + +@extend(node.if_stmt) +def _resolve(self,symtab): + symtab_copy = copy_symtab(symtab) + self.cond_expr._resolve(symtab) + self.then_stmt._resolve(symtab) + if self.else_stmt: + self.else_stmt._resolve(symtab_copy) + for k,v in symtab_copy.items(): + symtab.setdefault(k,[]).append(v) + +@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.args._resolve(symtab) + self.ret._lhs_resolve(symtab) + +@extend(node.null_stmt) +@extend(node.continue_stmt) +@extend(node.break_stmt) +def _resolve(self,symtab): + pass + +@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.try_catch) +def _resolve(self,symtab): + self.try_stmt._resolve(symtab) + self.catch_stmt._resolve(symtab) # ??? + +@extend(node.ident) +def _resolve(self,symtab): + if self.defs is None: + self.defs = [] + try: + self.defs += symtab[self.name] + except KeyError: + # defs == set() means name used, but not defined + pass + +@extend(node.arrayref) +@extend(node.cellarrayref) +@extend(node.funcall) +def _resolve(self,symtab): + # Matlab does not allow foo(bar)(bzz), so func_expr is usually + # an ident, though it may be a field or a dot expression. + if self.func_expr: + self.func_expr._resolve(symtab) + self.args._resolve(symtab) + #if self.ret: + # self.ret._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.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,[]).append(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/sparsearray.py b/smop/sparsearray.py deleted file mode 100644 index 5e7e0935..00000000 --- a/smop/sparsearray.py +++ /dev/null @@ -1,105 +0,0 @@ -import numpy as np -import sys - -class sparsearray(dict): - def __init__(self,input_array=[[]]): - #import pdb;pdb.set_trace() - dict.__init__(self) - a = np.atleast_2d(input_array) - self.dtype = a.dtype - self.ndim = a.ndim - self._shape = None - if not a.size: - return - it = np.nditer(a, flags=['multi_index']) - while not it.finished: - index = tuple([i+1 for i in it.multi_index]) - self.setdefault(index,it[0].item()) - it.iternext() - - @property - def shape(self): - #import pdb;pdb.set_trace() - if self._shape is None: - s = [0] * self.ndim - for key in self.keys(): - for i,k in enumerate(key): - s[i] = max(k,s[i]) - self._shape = tuple(s) - return self._shape - - def todense(self): - a = np.zeros(self.shape,dtype=self.dtype) - for key,value in self.iteritems(): - key = tuple([i-1 for i in key]) - a.__setitem__(key,value) - return a - - def __str__(self): - return str(self.todense()) - - def __repr__(self): - return repr(self.todense()) - - def copy(self): - #return copy.copy(self) - return self.todense() - - def __setitem__(self,index,value): - if np.isscalar(value): - for key in self.iterkeys(index): - dict.__setitem__(self,key,value) - self._shape = None - else: - raise NotImplementedError - - def __getslice__(self,i,j): - if j == sys.maxint: - j = None - return self.__getitem__(slice(i,j,None)) - - def __getitem__(self,index): - try: - #a = [dict.__getitem__(self,key) for key in self.iterkeys(index)] - a = [self.get(key,0) for key in self.iterkeys(index)] - if len(a) == 1: - return a[0] - except ValueError: - raise IndexError # out of bound rhs indexing - #return a - #return sparsearray([a]) - return np.array(a) - - def iterkeys(self,index): - #import pdb; pdb.set_trace() - if not isinstance(index,tuple) and self.shape[0] == 1: - index = (1,index) - if isinstance(index, int): - key = np.unravel_index(index-1, self.shape, order='F') - yield tuple(k+1 for k in key) - elif isinstance(index,slice): - index = range((index.start or 1)-1, - index.stop or np.prod(self.shape), - index.step or 1) - for key in np.transpose(np.unravel_index(index, self.shape, order='F')): # 0-based - yield tuple(k+1 for k in key) - elif isinstance(index,(list,np.ndarray)): - index = np.asarray(index)-1 - for key in np.transpose(np.unravel_index(index, self.shape, order='F')): - yield tuple(k+1 for k in key) - else: - assert isinstance(index,tuple),index.__class__ - indices = [] # 1-based - for i,ix in enumerate(index): - if isinstance(ix,slice): - indices.append(np.arange((ix.start or 1), - (ix.stop or self.shape[i]) + 1, - ix.step or 1, - dtype=int)) - else: - indices.append(np.asarray(ix)) - assert len(index) == 2 - indices[0].shape = (-1,1) - for key in np.broadcast(*indices): - yield tuple(map(int,key)) - diff --git a/smop/test_core.py b/smop/test_core.py deleted file mode 100644 index c3c2c9d9..00000000 --- a/smop/test_core.py +++ /dev/null @@ -1,132 +0,0 @@ -# SMOP compiler runtime support library -# Copyright 2014 Victor Leikehman - -# MIT license -import numpy as np -import unittest -from core 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_matlabarray.py b/smop/test_matlabarray.py index 5c702b7a..6ff20ad8 100644 --- a/smop/test_matlabarray.py +++ b/smop/test_matlabarray.py @@ -1,5 +1,5 @@ import unittest -from core import * +from libsmop import * class test_matlabarray(unittest.TestCase): """Expanding matlabarray""" diff --git a/smop/test_parse.py b/smop/test_parse.py index 07a7c286..b26f940c 100644 --- a/smop/test_parse.py +++ b/smop/test_parse.py @@ -1,5 +1,6 @@ import unittest import parse +import node class TestParse(unittest.TestCase): def test_p03(self): @@ -8,34 +9,33 @@ def test_p03(self): t = parse.parse(s) self.assert_(t) - def test_p04(self): - """Dot has higher precedence than other operations""" - s = "a+b.c.d;" - t = parse.parse(s) - u = [parse.expr_stmt(id=1, expr=[('+', parse.ident(name='a', - lineno=1, lexpos=0), - parse.field(expr=parse.field(expr=parse.ident(name='b', - lineno=1, lexpos=2), ident=parse.ident(name='.c', lineno=1, - lexpos=3)), ident=parse.ident(name='.d', lineno=1, lexpos=5)))])] - self.assertEqual(t,u) +# FIXME +# def test_p04(self): +# """Dot has higher precedence than other operations""" +# s = "a+b.c.d;" +# t = parse.parse(s) +# u = [node.expr_stmt(node.expr=[('+', parse.ident(name='a'), +# parse.field(expr=parse.field(expr=parse.ident(name='b') +# lineno=1, lexpos=2), ident=node.ident(name='.c'), ident=parse.ident(name='.d')))])] +# self.assertEqual(t,u) -# def test_p05(self): -# """Iterate over LHS nodes (TBD)""" -# s = "[foo(A.x(B.y)).bar(C.z).bzz,hello.world] =1;" -# t = parse.parse_buf(s) -# u = ["foo",".bar",".bzz","hello",".world"] -# self.assertEqual([v[1] for v in dataflow.lhs(t[1][1])],u) -# - def test_p06(self): - """Cell arrays""" - s = """ - {1 ... - 'foo' ... - 'bar' ... - 'bzz'}; - """ - t = parse.parse(s) - self.assert_(t) +## def test_p05(self): +## """Iterate over LHS nodes (TBD)""" +## s = "[foo(A.x(B.y)).bar(C.z).bzz,hello.world] =1;" +## t = parse.parse_buf(s) +## u = ["foo",".bar",".bzz","hello",".world"] +## self.assertEqual([v[1] for v in dataflow.lhs(t[1][1])],u) +## +# def test_p06(self): +# """Cell arrays""" +# s = """ +# {1 ... +# 'foo' ... +# 'bar' ... +# 'bzz'}; +# """ +# t = parse.parse(s) +# self.assert_(t) if __name__ == "__main__": unittest.main() diff --git a/smop/test_runtime.py b/smop/test_runtime.py deleted file mode 100644 index 6ff4b746..00000000 --- a/smop/test_runtime.py +++ /dev/null @@ -1,132 +0,0 @@ -# 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 index 53e8053e..9396413d 100644 --- a/smop/test_solver.py +++ b/smop/test_solver.py @@ -1,10 +1,9 @@ -import pstats,cProfile import numpy,time from solver import * -from core import * +from libsmop import * def main(): - ai = matlabarray(zeros (10,10,dtype=int),dtype=int) + ai = matlabarray(zeros(10,10,dtype=int)) af = copy(ai) ai[1,1]=2 @@ -27,8 +26,3 @@ def main(): if __name__ == '__main__': main() - """ - cProfile.runctx('main()',globals(),locals(),"Profile.prof") - s = pstats.Stats("Profile.prof") - s.strip_dirs().sort_stats("time").print_stats() - """ diff --git a/smop/test_sparsearray.py b/smop/test_sparsearray.py deleted file mode 100644 index bb5f5d8c..00000000 --- a/smop/test_sparsearray.py +++ /dev/null @@ -1,232 +0,0 @@ -import numpy as np -import unittest -from core import * -from sparsearray import sparsearray - -class Slice(unittest.TestCase): - def setUp(self): - self.a = sparsearray(arange (1,4).reshape(2,2,order="F")) - - def test01(self): - #self.assertTrue(isequal (self.a[1:4],[1,2,3,4])) - self.assertEqual(self.a[1,1], 1) - self.assertEqual(self.a[2,1], 2) - self.assertEqual(self.a[1,2], 3) - self.assertEqual(self.a[2,2], 4) -# -# def test02(self): -# self.a[1,1] = 11 -# self.a[2,1] = 22 -# self.a[1,2] = 33 -# self.a[2,2] = 44 -# self.assertTrue(isequal (self.a[1:4],[11,22,33,44])) -# -# def test03(self): -# self.a[1] = 11 -# self.a[2] = 22 -# self.a[3] = 33 -# self.a[4] = 44 -# self.assertTrue(isequal (self.a[1:4],[11,22,33,44])) -# -# def test04(self): -# #with self.assertRaises(ValueError): -# self.a[1:4]=[11,22,33,44] -# self.assertTrue(isequal (self.a[1:4],[11,22,33,44])) -# -# def test05(self): -# self.a[:,:]=[[11,33],[22,44]] -# self.assertTrue(isequal (self.a[1:4],[11,22,33,44])) -# -# def test06(self): -# self.a[:]=[11,22,33,44] -# self.assertTrue(isequal (self.a[1:4],[11,22,33,44])) -# -# def test07(self): -# self.a[::3]=[11,44] -# self.assertTrue(isequal (self.a[1:4],[11,2,3,44])) -# -# def test08(self): -# self.a[1:4:3]=[11,44] -# self.assertTrue(isequal (self.a[1:4],[11,2,3,44])) -# -# def test_007(self): -# """ -# One-dimensional LHS indexing without expansion, -# using list index. -# """ -# -# a[[1,4]]=1 -# self.assertEqual(str(a), "[[ 1. 0.]\n [ 0. 1.]]") -# -# @unittest.skip("") -# def test_008(self): -# a=zeros (2,2) -# a[[4,3,2,1]]=[1,2,3,4] -# self.assertEqual(str(a), "[[ 1. 2.]\n [ 3. 4.]]") -# -# -# -# @unittest.skip("") -# def test_010(self): -# a=zeros (2,2) -# a[2,:]=[1,2] -# self.assertEqual(str(a), "[[ 0. 0.]\n [ 1. 2.]]") -# -# @unittest.skip("") -# def test_011(self): -# a=zeros (2,2) -# a[2,1:2]=[1,2] -# self.assertEqual(str(a), "[[ 0. 0.]\n [ 1. 2.]]") -# -# @unittest.skip("") -# def test_012(self): -# a=zeros (2,2) -# a[2,[1,2]]=[1,2] -# self.assertEqual(str(a), "[[ 0. 0.]\n [ 1. 2.]]") -# -# @unittest.skip("") -# def test_013(self): -# a=zeros (2,2) -# a[:,:]=[[1,2],[3,4]] -# self.assertEqual(str(a), "[[ 1. 2.]\n [ 3. 4.]]") -# -# @unittest.skip("") -# def test_014(self): -# a=zeros (2,2) -# with self.assertRaises(ValueError): -# a[:,:]=[1,2,3,4] - -class Sparsearray(unittest.TestCase): - def setUp(self): - self.a = np.arange(1,5).reshape(2,2,order="F") - self.b = sparsearray(self.a) - - def test01(self): - self.assertEqual(self.b[1,1],1) - self.assertEqual(self.b[2,1],2) - self.assertEqual(self.b[1,2],3) - self.assertEqual(self.b[2,2],4) - -class Index(unittest.TestCase): - def setUp(self): - self.a = sparsearray(arange (1,4).reshape(2,2,order="F")) - - def test01(self): - """ - One-dimensional LHS indexing of multi-dimensional array, - without expansion. Using scalar index. - """ - self.a[4]=9 - self.assertTrue(isequal (self.a, sparsearray([[1, 3],[2, 9]]))) - - def test02(self): - """ - Multi-dimensional LHS indexing, without expansion. - """ - self.a[1,1]=11 - self.a[2,2]=22 - self.a[1,2]=12 - self.a[2,1]=21 - self.assertTrue(isequal (self.a, sparsearray([[11,12],[21,22]]))) - -class Indices(unittest.TestCase): - def setUp(self): - self.a = sparsearray(np.arange(1,5).reshape(2,2,order="F")) - self.c = sparsearray(arange (1,20).reshape(4,5,order="F")) - - def test01(self): - with self.assertRaises(ValueError): - self.a[100]=100 - self.a[3,3]=33 - self.assertTrue(isequal (self.a.todense(), np.asarray([[1,3,0],[2,4,0],[0,0,33]]))) - - def test02(self): - self.assertTrue(isequal (self.a[:], [1,2,3,4])) - self.assertTrue(isequal (self.a[1:4], [1,2,3,4])) - - @unittest.skip("") - def test03(self): - self.assertTrue(isequal (self.a[:,:], [[1,3],[2,4]])) - self.assertTrue(isequal (self.a[1:2,1:2], [1,2,3,4])) - - def test04(self): - a = sparsearray([1,2,3]) - a[5]=100 - a[3,3]=33 - self.assertEqual(a.shape, (3,5)) - self.assertTrue(isequal (a.todense(), [[1,2,3,0,100],[0,0,0,0,0],[0,0,33,0,0]])) - - -class Slice(unittest.TestCase): - def setUp(self): - self.a = sparsearray(arange (1,4).reshape(2,2,order="F")) - - def test01(self): - #self.assertTrue(isequal (self.a[1:4],[1,2,3,4])) - self.assertEqual(self.a[1,1], 1) - self.assertEqual(self.a[2,1], 2) - self.assertEqual(self.a[1,2], 3) - self.assertEqual(self.a[2,2], 4) - -class Sparsearray(unittest.TestCase): - def setUp(self): - self.a = np.arange(1,5).reshape(2,2,order="F") - self.b = sparsearray(self.a) - - def test01(self): - self.assertEqual(self.b[1,1],1) - self.assertEqual(self.b[2,1],2) - self.assertEqual(self.b[1,2],3) - self.assertEqual(self.b[2,2],4) - -class Index(unittest.TestCase): - def setUp(self): - self.a = sparsearray(arange (1,4).reshape(2,2,order="F")) - - def test01(self): - """ - One-dimensional LHS indexing of multi-dimensional array, - without expansion. Using scalar index. - """ - self.a[4]=9 - self.assertTrue(isequal (self.a, sparsearray([[1, 3],[2, 9]]))) - - def test02(self): - """ - Multi-dimensional LHS indexing, without expansion. - """ - self.a[1,1]=11 - self.a[2,2]=22 - self.a[1,2]=12 - self.a[2,1]=21 - self.assertTrue(isequal (self.a, sparsearray([[11,12],[21,22]]))) - -class Indices(unittest.TestCase): - def setUp(self): - self.a = sparsearray(np.arange(1,5).reshape(2,2,order="F")) - self.c = sparsearray(arange (1,20).reshape(4,5,order="F")) - - def test01(self): - with self.assertRaises(ValueError): - self.a[100]=100 - self.a[3,3]=33 - self.assertTrue(isequal (self.a.todense(), np.asarray([[1,3,0],[2,4,0],[0,0,33]]))) - - def test02(self): - self.assertTrue(isequal (self.a[:], [1,2,3,4])) - self.assertTrue(isequal (self.a[1:4], [1,2,3,4])) - - @unittest.skip("") - def test03(self): - self.assertTrue(isequal (self.a[:,:], [[1,3],[2,4]])) - self.assertTrue(isequal (self.a[1:2,1:2], [1,2,3,4])) - - def test04(self): - a = sparsearray([1,2,3]) - a[5]=100 - a[3,3]=33 - self.assertEqual(a.shape, (3,5)) - self.assertTrue(isequal (a.todense(), [[1,2,3,0,100],[0,0,0,0,0],[0,0,33,0,0]])) - -if __name__ == "__main__": - unittest.main()