* documentation
TODO: update most of this. tranforms have changed
** type switches
 pyel-switch forms are inserted in a backquoted form so an ',' unquote
 may be used. DO NOT UNQUOTE THE ARGUMENTS, they will be replaced later

** from py ast to e-lisp transform
#+Begin_SRC emacs-lisp
  ;;First, get the python ast:
  (py-ast "(a,b)")
  "Module(body=[Expr(value=Tuple(elts=[Name(id='a', ctx=Load()), Name(id='b', ctx=Load())], ctx=Load()))])
  "
  
  ;;Second create any newly needed python AST interpretor.
  ;; Here we need Tuple(elts,ctx)
  
  def Tuple(elts, ctx):
      return l_str(["tuple ", ll_str(elts), ctx])
  
  ;;Third, define a tranform to convert the function to lisp
  (def-transform tuple pyel ()
    (lambda (elts ctx) ;;Ignoring ctx for now
      (concat "["
              (mapconcat 'transform elts " ")
              "]")))
  
  
  ;;Forth, create tests. Evaluate the expresstion, the test are sent to the kill ring
  (pyel-create-tests 'Tuple
                     "()"
                     "(a, b)"
                     "(a, (b, (c,d)))"
                     "((((((((a))))))))")
#+END_SRC
** ways to define new transforms
TODO:
 how to define a macro transform
TODO: pyel-define-function-translation
*** change a function name
Example:
#+Begin_SRC emacs-lisp
(pyel-translate-function-name 'range 'py-range)
(pyel-translate-function-name 'map 'mapcar)
#+END_SRC
*** change a variable name
Example:
#+Begin_SRC emacs-lisp
(pyel-translate-variable-name 'None 'nil)
#+END_SRC
*** define a general translation
Example:
#+Begin_SRC emacs-lisp
(def-transform assert pyel ()
    (lambda (test msg)
      `(assert ,(transform test) t ,(transform msg))))
#+END_SRC

*** define a method translation
$var replaces var with name of the original var
   (expands to ,var in the macro definition)
 other parameter references are evaluated and assigned to tmp variables
#+Begin_SRC emacs-lisp
(pyel-method-transform append (obj thing)
                  (list _) -> (setq $obj (append obj (list thing)))
                  (_ _)    -> (append obj thing))
#+END_SRC

*** define a function translation
#+Begin_SRC emacs-lisp
  (pyel-func-transform len (thing)
                       (object) -> (__len__ ,thing)
                       (_)      -> (length ,thing))
#+END_SRC
*** type switching transforms
  (pyel-dispatch-func ** (l r) ;;pow
                    (number number) -> (expt l r)
                    (object _ )
                    (_ object) -> (--pow-- l r))

*** declare a name to be a macro
(pyel-declare-macro "save_excursion")
** writing faster code
*** large difference
*** small difference
prevent pyel from converting *varargs to a vector. Instead manipulate it in python as if it were a list instead of a tuple like it normally is.
put the following line in your config:
  (setq pyel-use-list-for-varargs t)
  ==> NO! ALL CHANGES TO CODE TRANSFORMS SHOULD BE INDECATED IN THE PYTHON SOURCE

* headers
** main
#+Begin_SRC emacs-lisp :tangle pyel.el
  ;; This is a tangled file  -- DO NOT EDIT --  Edit in pyel.org
  
  ;;  PYEL -> translate PYthon to Emacs Lisp
#+END_SRC
** transforms
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  ;; This is a tangled file  -- DO NOT EDIT --  Edit in pyel.org
#+END_SRC
** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  ;; This is a tangled file  -- DO NOT EDIT --  Edit in pyel.org
#+END_SRC
** py-lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;; This is a tangled file  -- DO NOT EDIT --  Edit in pyel.org
#+END_SRC
** python ast
#+Begin_SRC python :shebang #!/usr/bin/python3 :tangle py-ast.py
  # This is a tangled file  -- DO NOT EDIT --  Edit in pyel.org
#+END_SRC

* main
** require
#+Begin_SRC emacs-lisp :tangle pyel.el
  (require 'eieio)
  (require 'cl)
  
  (require 'pyel-type-reconstruction)
  ;;other requires are at the end
#+END_SRC
** main
main
#+Begin_SRC emacs-lisp :tangle pyel.el
  
  (defun mpp (code)
    (let ((pp (pp-to-string code)))
      (insert "\n" pp)))
  
  ;;TODO: more robust checking, return type of error as well
  (defun pyel-is-error (code)
    "return the line of the error in CODE, else nil"
    (let ((line))
      (if (string-match "^Traceback" code)
          (with-temp-buffer
            (insert code)
            (goto-char (point-max))
            (if (re-search-backward "line \\([0-9]+\\)")
                (setq line (match-string 1)))))
      ;;TODO: why is this the actual line number...it should be relative the a bunch of code it's appended to ...
      (and line (string-to-number line))))
  
  (defvar pyel-transform-status nil
    "indicate status (success/fail) of last pyel transform
  status types:
  nil for no error
  ('python ast transform error'  <line number>)
  ")
  
  (defvar pyel-error-string "PYEL ERROR"
    "string returned by `pyel' when it failed to transform")
  
  (defvar pyel-ast-backtrace nil
    "python backtrace, set by `pyel' when a syntax error occurs")
  
  (defun pyel-py-ast-file-name ()
    "return the full file name of py-ast.el"
    (file-path-concat pyel-directory "py-ast.py"))
  
  (defun pyel (python &optional no-line-and-col-nums include-defuns py-ast-only)
    "translate PYTHON into Emacs Lisp.
  PYTHON is a string.
  include line and column nums in output unless NO-LINE-AND-COL-NUMS is non-nil
  If PY-AST-ONLY, return the un-evaled ast.
  If INCLUDE-DEFUNS, include the list of pyel defined functions in the output
    this is ignored if PY-AST-ONLY is non-nil"
    (assert (eq lexical-binding nil) "pyel requires dynamic scoping")
  
    ;;this is called recursively to transform code in macro bodies
    ;;so this cannot be cleared here - it removes previous work
    ;;(setq pyel-marked-ast-pieces nil)
    
    (setq pyel-transform-status nil) ;;so far so good...
  
    (setq pyel-last-python-code python)
  
    (let* ((py-ast "")
           (el-code "")
           (current-transform-table (get-transform-table 'pyel))
           (python (with-temp-buffer
                     (insert python)
                     (pyel-preprocess-buffer)
                     (buffer-string)))
           (pyel-context nil)
           (type-env (pyel-make-type-env pyel-global-type-env))
           (tmp-file (make-temp-file "pyel-" nil ".py"))
           (ast-command (format "cd %s;./%s %s"
                                pyel-directory
                                "py-ast.py";(pyel-py-ast-file-name)
                                tmp-file))
           return-type line ret)
  
      (with-temp-buffer
        (insert python)
        (write-region
         nil
         nil
         tmp-file nil 'silent))
  
      (setq py-ast (shell-command-to-string ast-command))
      (setq _x py-ast)
      (if (setq py-error (pyel-is-error py-ast))
          (progn
            (setq pyel-ast-backtrace py-ast
                  pyel-python-error-line py-error)
            pyel-error-string)
        ;;else: no error
        (if py-ast-only
            py-ast
          ;;      (pyel-do-splices (transform (read py-ast))))))
          ;;read can only return one sexp so we need to put it in a progn or something
          ;;similar
  
          (setq ret (pyel-do-splices (if include-defuns
                                         (list '@ (cons '@ pyel-function-definitions)
                                                  (transform (read (format "(@ %s)" py-ast))))
                                       (transform (read  (format "(@ %s)" py-ast))))))
          ;;TODO: this is a temp solution for convenience
          (mapc 'eval pyel-function-definitions)
          (delete-file tmp-file)
          (setq pyel-last-type-env type-env)
          ret
          ))))
  
  (defun pyel-file (file &optional el-file extras-file no-error compile)
    "Convert python FILE to e-lisp file named EL-FILE
  EL-FILE defaults to FILE.el, if such a file exists, it will be
  overwritten without warning
  If EXTRAS-FILE is given, save all the pyel generated functions/macros
  in it. If EXTRAS-FILE is 't', automatically create a file name with
  the format FILE_pyel-extras.el. if EXTRAS-FILE is nil, save the
  the generated functions at the top of the output file.
  If COMPILE is non-nil, byte compile resulting e-lisp.
  If "
    (let ((pyel-function-definitions nil)
          (pyel-defined-functions nil)
          (pyel-context nil)
          (el-file (or el-file (format "%s.el" file)))
          (extras-file (if extras-file
                           (if (eq extras-file t)
                               (format "%s_pyel-extras.el" (remove-file-extension
                                                            file))
                             extras-file)))
          python error)
      (with-temp-buffer
        (insert-file-contents file)
        (setq python (pyel (buffer-string) t t))
        (if (equal python pyel-error-string)
            (if no-error
                (setq error t)
              (error "pyel-load: Error loading file %s" file)))  ;;x
        (if (not error)
            (progn
              (erase-buffer)
              (emacs-lisp-mode)
              (mapc (lambda (x)
                      (insert (format "(require '%s)\n" x)))
                    pyel-required)
              (if extras-file
                  (progn
                    (insert (format "(require '%s)\n\n"
                                    (file-name-base extras-file)))
                    (with-temp-buffer
                      (emacs-lisp-mode)
                      (mapc '(lambda (x)
                               (pyel-prettyprint x)
                               (insert "\n"))
                            (pyel-sort-functions
                             pyel-function-definitions))
                      (insert (format "(provide '%s)\n"
                                      (file-name-base extras-file)))
                      (write-file extras-file)))
                (mapc '(lambda (x)
                         (pyel-prettyprint x)
                         (insert "\n"))
                      (pyel-sort-functions
                       pyel-function-definitions)))
  
              (pyel-prettyprint python)
              (untabify (point-min) (point-max))
              (write-file el-file))))))
  
  (defmacro pyel-eval (code)
    "translate and eval python CODE"
    `(eval (let (lexical-binding)
             (pyel ,code))
           :lexical))
  
  (defun pyel-sort-functions (functions)
    (sort (copy-list functions) (lambda (a b) (string< (symbol-name (nth 1 a))
                                                       (symbol-name (nth 1 b))))))
  (defsubst remove-file-extension (filename)
    (expand-file-name (file-name-base filename) (file-name-directory filename)))
  
  (defvar pyel-last-python-code nil
    "last python string `pyel' attempted to transform")
    
  (defun pyel-buffer-to-lisp (&optional ast-only)
    "transform and return python in current buffer to emacs-lisp"
    (pyel (buffer-string) nil nil ast-only))
  
  (defvar pyel-pp-function 'pp-to-string
    "function that pretty prints pyel e-lisp code")
  
  ;; (defun pyel-buffer (&optional out-buff)
  ;;   "transform python in current buffer and display in OUT-BUFF,
  ;; OUT-BUFF defaults to *pyel-output*"
  ;;   (interactive)
  ;;   (let ((out (pyel-buffer-to-lisp))
  ;;         (buff (or out-buff "*pyel-output*")))
  ;;       (switch-to-buffer-other-window buff)
  ;;       (erase-buffer)
  ;;       ;;    (insert (funcall pyel-pp-function out))
  ;;       (lisp-interaction-mode)
  ;;       (pyel-prettyprint out)
  ;;       (goto-char 1)
  ;;       ;;(emacs-lisp-mode)
  ;;       ))
  
  (defun pyel-buffer-to-string (&optional ast-only)
      (let ((lisp (pyel-buffer-to-lisp ast-only)))
        (with-temp-buffer
          (emacs-lisp-mode)
          (pyel-prettyprint lisp)
          (buffer-string))))
  
  (defun pyel-transform-ast (ast &optional no-splice)
    "transform a python AST to Emacs Lisp, AST must be a string
  AST can be generated by `pyel' with (pyel py-string t)"
    (with-transform-table 'pyel
                          (let ((code (transform (read  (format "(@ %s)" ast)))))
                            (if no-splice
                                code
                              (pyel-do-splices code)))))
  
  (defun pyel-file-ast (file-name)
    "return the ast from .py file FILE-NAME"
    )
  
  (defmacro pyel-with-known-types (known-types &rest code)
    "translate CODE while faking the known types"
    `(flet ((pyel-get-possible-types
             (&rest args)
             (mapcar* (lambda (arg type) (cons arg type))
                      args ,known-types)))
  
       (pyel ,@code)))
  
  (defun py-ast (code &optional pp include-attributes python2)
    "Return the python abstract syntax tree for python CODE
  useing python3 unless PYTHON2 is non-nil"
    (let ((py-ast "")
          (el-code "")
          ret)
  
      (with-temp-buffer
        (insert "import ast" "\n")
        (insert (format "print(ast.dump(ast.parse(\"\"\"%s\"\"\")%s))"
                        code
                        (if include-attributes ",include_attributes=True" "")))
        (write-region nil nil pyel-tmp-file nil 'silent))
  
      (setq ret (shell-command-to-string (format "python%s %s"
                                                 (if python2 "" "3")
                                                 pyel-tmp-file)))
      (if pp
          (mapconcat 'identity (split-string ret ",") ",\n")
        ret)))
  
  ;;'(a (@ b (c)))) => (a b (c))
  ;;'(a (@ b c)))   => (a b c)
  ;;'(@ a b c) => (progn a b c)
  ;;'(@ (a b)) = > (a b)
  ;;'(a (@) b) = > (a b)
  
  (defun pyel-do-splices (code)
    (if (listp code)
        (let (c)
          (if (eq (car code) '@) ;;special case: outer most list
              (if (> (length code) 2)
                  (pyel-do-splices `(progn ,code))
                (pyel-do-splices (cadr code)))
            (let ((ncode nil))
              (while code
                (setq c (pop code))
                (cond ((and (listp c) (not (listp (cdr c)))) ;;cons cell
                       (push (cons (pyel-do-splices (car c))
                                   (pyel-do-splices (cdr c))) ncode))
                      ((listp c)
                       (if (equal (car c) '@)
                           (setq ncode (append (reverse (pyel-do-splices (cdr c)))
                                               ncode))
                         (push (pyel-do-splices c) ncode)))
                      (t (push c ncode))))
              (if (listp ncode) (reverse ncode) ncode))))
      code))
  
  (defun pyel-reload ()
    (interactive)
    (pyel-reset)
    (setq pyel-global-type-env (pyel-make-type-env))
    (dolist (f '(pyel
                 pyel-tests
                 pyel-transforms
                 pyel-mode
                 pyel-pp
                 pyel-preprocessor
                 pyel-tests-generated
                 py-lib
                 transformer
                 pyel-type-reconstruction))
      (setq features (remove f features)))
    (let ((start (current-time)))
      (if (require 'pyel)
          (message "pyel load time: %.06f" (float-time (time-since start))))))
  
  (defun pyel-reset()
    "reset internal variables"
    (interactive)
    (setq pyel-method-name-arg-signature (make-hash-table :test 'eq)
          pyel-function-definitions nil
          pyel-defined-functions nil
          pyel-method-transforms nil
          pyel-func-transforms nil
          pyel-func-transforms2 nil
          pyel-marked-ast-pieces nil
          pyel-ast-backtrace nil        
          pyel-context nil
          pyel-func-kwarg-transforms nil
          pyel-method-kwarg-transforms nil))
  
  (defvar pyel-method-name-format-string "_%s%s-method%s"
    "format string for the method transform names
  It must accept two args, the name of the method
  and its arg signature")
  
  (defun pyel-method-transform-name (method-name &optional arglist kwarg)
    "Return the name of the temlate that transform the method METHOD-NAME.
      template names are modified to avoid potential conflict with other templates
    the arglist must be placed in a list before passing so that the code can
     tell if the arglist is empty or not provided. 
    ARGLIST is used to generate a name that is unique to that arglist signature"
    (assert (and (listp arglist) (listp (car arglist)))
            "Invalid arglist. Expected a list of a list")
    (let* ((signature (if arglist (pyel-arglist-signature (car arglist)) "_"))
           (name (format pyel-method-name-format-string
                         (symbol-name method-name)
                         (if kwarg "-kwarg" "")
                         signature)))
      (if arglist
          (assert (equal (pyel-extract-arg-descriptor signature)
                         (pyel-arg-descriptor (car arglist)))
                  "Invalid method name"))
      (intern name)))
  
  (defun pyel-func-transform-name (func-name &optional kwarg)
    "like `pyel-method-transform-name' for functions"
    (intern (format "_%s%s-function_"
                    (if kwarg "-kwarg" "")
                    (symbol-name func-name))))
  
  (defmacro push-back (val place)
    "Add VAL to the end of the sequence stored in PLACE. Return the new
  value."
    `(setq ,place (append ,place (list ,val))))
  
  (defun pyel-translate-function-name (name new-name)
    "translate python NAME to e-lisp NEW-NAME"
    (push (list name new-name) pyel-function-name-translations))
  
  (defun pyel-translate-variable-name (name new-name)
    "translate python  NAME to e-lisp NEW-NAME"
    (push (list name new-name) pyel-variable-name-translations))
  
  (defun pyel-not-implemented (message)
    "signify that a feature is not implemented"
    ;;TODO
    (message message) ;;tmp
    )
  
  (defmacro insert-at (list nth value)
    "insert VALUE at NTH index in LIST"
    `(setq ,list (append (subseq ,list 0 ,nth)
                         (list ,value)
                         (subseq ,list  ,nth))))
  
  (defun pyel-replace-in-thing (from to thing)
    "replace character FROM to TO in THING
  THING may be a symbol, string or list"
    (cond
     ;;tmp fix:
     ;;special case for numbers to handle being called
     ;;on the arg list in the def transform
     ;;(numbers are from the default values for kwonlyargs)
     ;;TODO: call _to- only on names, not such large lists
     ((numberp thing) thing)
  
     ((stringp thing)
      (replace-regexp-in-string from to  thing))
     ((symbolp thing)
      (intern (replace-regexp-in-string from to  (symbol-name thing))))
     ((and (listp thing) (listp (cdr thing)))
      (mapcar (lambda (x) (pyel-replace-in-thing from to x)) thing))
     ((listp thing) ;;cons cell
      (cons (pyel-replace-in-thing from to (car thing))
            (pyel-replace-in-thing from to (cdr thing))))
     (t (error "invalid thing"))))
  
  
  (defun _to- (thing)
    (pyel-replace-in-thing "_" "-" thing))
  (defun -to_ (thing)
    (pyel-replace-in-thing "-" "_" thing))
  
  (defun pyel-change-ctx (form ctx)
    "change ctx of form to CTX"
    (let ((type (and (listp form) (car form))))
      (cond ((eq type 'name)
             (list (car form) (cadr form) (list 'quote ctx)))
            ;;TODO: attribute and other forms (if needed)
            (t form))))
  
  (defun pyel-make-ast (type &rest args)
    "Generate pyhon ast.
  This is used when the ast form is needed by a transform that is manually
   called from another transform"
    (flet ((assert_n_args (type expect have)
                          (assert (= expect have)
                                  (format "pyel-make-ast -- ast type '%s'expects %s args. received %s args" type expect have)))
           (correct_ctx (ctx)
                        (if (symbolp ctx)
                            (pyel-make-ast ctx)
                          ctx))
           (correct_to_string (name)
                              (if (stringp name)
                                  name
                                (if (symbolp name)
                                    (symbol-name name)
                                  (error "invalid type for 'name'")))))
  
      ;;TODO: should have seporate functions to check
      ;;      the validity of the ast instead of having
      ;;      the correction functions do it
      (case type
  
        (subscript ;;args: value slice ctx
         (assert_n_args 'subscript 3 (length args))
  
         (let ((ctx (correct_ctx (car (last args)))))
           (list 'subscript (car args) (cadr args) ctx)))
  
        (name ;;args: name ctx
         (assert_n_args 'name 2 (length args))
         (let* ((name (correct_to_string (car args)))
  
                (ctx (correct_ctx (car (last args)))))
  
           (list 'name name ctx)))
        (load
         '(quote load))
        (store
         '(quote store)))))
  
  (defmacro macrop (sym)
    (if (boundp sym)
        (list 'macrop-1 sym)
      `(macrop-1 (quote ,sym))))
  
  (defun macrop-1 (function)
    ;;this is mostly taken from `describe-function-1'
    (let* ((advised (and (symbolp function) (featurep 'advice)
                         (ad-get-advice-info function)))
           ;; If the function is advised, use the symbol that has the
           ;; real definition, if that symbol is already set up.
           (real-function
            (or (and advised
                     (let ((origname (cdr (assq 'origname advised))))
                       (and (fboundp origname) origname)))
                function))
           ;; Get the real definition.
           (def (if (symbolp real-function)
                    (symbol-function real-function)
                  function)))
      (eq (car-safe def) 'macro)))
  
  (defun callable-p (object)
    (if (symbolp object)
        (or (fboundp object)
            (functionp object)) ;;necessary?
      (functionp object)))
  
  (defun pyel-split-list (lst sym)
    "split list LST into two sub-lists at separated by SYM
    The return value is the two sub-lists consed together"
    (let ((current (not sym))
          first)
      
      (while (and (not (eq current sym))
                  lst)
        (setq current (pop lst))
        (push current first)
        )
      
      (cons (reverse (if (eq (car first) sym) (cdr first) first)) lst)))
  
  
  (defun pyel-eval-last-sexp-1-function (eval-last-sexp-arg-internal)
    "Evaluate sexp before point; print value in minibuffer.
  With argument, print output into current buffer.
  
  This function is redefined to print python objects in a
  reasonable manner. The origional definition has been stored
  in `pyel-orig-eval-last-sexp-1'"
    (let ((standard-output (if eval-last-sexp-arg-internal (current-buffer) t))
          (val (eval (eval-sexp-add-defvars (preceding-sexp)) lexical-binding)))
      ;; Setup the lexical environment if lexical-binding is enabled.
      (if (and pyel-object-prettyprint
               (fboundp 'py-object-p)
               (py-object-p val))
          (setq val (pyel-repr val)))
      (eval-last-sexp-print-value val)))
  
  (fset 'pyel-orig-eval-last-sexp-1 'eval-last-sexp-1)
  (fset 'eval-last-sexp-1 'pyel-eval-last-sexp-1-function)
  
  (defvar pyel-object-prettyprint t
    "if non-nil, objects will be printed with their pyel repr value
  during interactive emacs-lisp sessions where possible")
          
  (defun pyel-toggle-object-prettyprint ()
    (interactive)
    (setq pyel-object-prettyprint (not pyel-object-prettyprint)))
  
  (defun pyel-get-generated-function (name)
    "return the generated function/macro definition for NAME"
    (get-matching-item pyel-function-definitions
                       (lambda (x) (eq (cadr x) name))))
  
  (defun pyel-strip-leading-nil (list)
    "remove all nil items from the front of LIST until the first non-nil item"
    (while (and (not (null list))
                (not (car list)))
      (setq list (cdr list)))
    list)
  
  (defun pyel-strip-trailing-nil (list)
    "remove all nil items from the back of LIST until the last non-nil item"
    (setq list (reverse list))
    (while (and (not (null list))
                (not (car list)))
      (setq list (cdr list)))
    (reverse list))
  
  (defun filter (func list)
    (let ((newlist ()))
      (dolist (e list)
        (if (funcall func e)
            (setq newlist (cons e newlist))))
      (reverse newlist)))
#+END_SRC
** variables
#+Begin_SRC emacs-lisp :tangle pyel.el
  (defvar pyel-directory (file-name-directory (if load-in-progress
                                                  load-file-name
                                                buffer-file-name))
    "Path to pyel files. must include py-ast.py, pyel.el etc")
  
  (defmacro vfunction-p (f)
    `(and (boundp ',f)
          (functionp ,f)))
  
  (defmacro pyel-empty-list-p (list)
    `(eq ,list 'py-empty-list))
  
  (set (defvar pyel-type-test-funcs nil
         "alist of types used in pyel-call-transform for the switch-type
              and the function used to test for that type")
       '((string stringp)
         (number numberp)
         (integer integerp)
         (int integerp)
         (float floatp)
         (vector vectorp)
         (empty-list pyel-empty-list-p)
         (list listp)
         (cons consp)
         (hash hash-table-p)
         (hash-table hash-table-p)
         (symbol symbolp)
         (array arrayp)
         ;;         (object object-p)
         (class py-class-p)
         (instance py-instance-p)
         (object py-object-p)
         (function functionp)
         (func fboundp)
         (vfunction vfunction-p)
         (vfunc vfunction-p)
         (callable callable-p)))
  
  (defvar pyel-negated-function-tests nil
    "A list of automatically created negated functions from `pyel-type-test-funcs'
    stored here just for convenient inspection")
  
  ;;create negated test functions
  (let (new func fname)
    (setq pyel-negated-function-tests nil)
    (mapc (lambda (x)
            (setq name (cadr x)
                  !name (intern (concat "!" (symbol-name name)))
                  func `(defsubst ,!name (x) (not (,name x))))
            (add-to-list 'pyel-negated-function-tests func)
            (eval func)
            (push (list (intern (concat "!" (symbol-name (car x))))
                        !name)
                  new))
          pyel-type-test-funcs)
    (setq pyel-type-test-funcs (append pyel-type-test-funcs new)))
  
  (defvar pyel-defined-classes nil
    "list of call class names defined by pyel")
  
  (defvar pyel-defined-functions nil
    "list of some functions defined pyel
            used by some templates to determine if a needed function has been defined yet")
  
  (defvar pyel-function-definitions nil
    "used to store function definitions created by pyel, not the user.")
  
  (defvar pyel-replace-args nil
    "if non-nil, pyel-do-call-transform will replace the arg symbols with their
            value, used if the code is to be inlined
            TODO: the option to replace the args should probably be obsoletede")
  
  (defvar pyel-unique-obj-names nil
    "if non-nil, uniquely name object instantces")
  
  (defvar pyel-context-groups nil ;;TODO: still used?
    "groups of contexts that cannot exist at the same time.
          `context-p' will stop at the first one in the list,")
  
  (setq pyel-context-groups
        '((assign-target assign-value)))
  
  (defvar pyel-function-name-translations nil
    "alist of function name translations, python->e-lisp.
      
          Entries in `pyel-function-name-translations' are applied before
          checking for function transforms.
          If a translation len->length is defined then the function transform for
          'len' will not be detected because the name is now 'length'
          ")
  
  (defvar pyel-variable-name-translations nil
    "alist of variable name translations, python->e-lisp.")
  
  (setq pyel-function-name-translations `(
                                          
                                          ))
  ;;TODO: list, vector, etc
  ;;      map?
  
  (setq pyel-variable-name-translations '((True t)
                                          (False nil)
                                          (None nil)))
  
  (defvar pyel-method-transforms nil
    "List of names of methods for which a transform has been defined
          For internal use only--do not modify by hand"
    )
  
  (defvar pyel-func-transforms nil
    "list of function names that have transforms defined for them")
  
  (defvar pyel-func-kwarg-transforms nil
    "list of function names that have kwarg transforms defined for them")
  
  (defvar pyel-method-kwarg-transforms nil
    "list of method transforms that accept kwargs")
  
  (defconst pyel-nothing '(@)
    "value to return from a function/transform when it should
          not contribute to the output code")
  
  (defconst pyel-python-version "3.2.3"
    "python interpreter version whose ast pyel is written for")
  
  (defvar test-variable-values nil
    "variables values for running tests")
  
  (setq test-variable-values
        '((pyel-defined-classes nil)
          (pyel-function-definitions nil)
          (pyel-defined-functions nil)
          (pyel-obj-counter 0)
          (pyel-unique-obj-names nil)
          (pyel-fully-functional-functions nil)
          ;;(pyel-method-transforms nil)
          ;;(pyel-func-transforms nil)
          (pyel-marker-counter 0)))
  
  (defvar pyel-marker-counter 0)
  
  (defvar pyel-tmp-file  "/tmp/pyel-ast.py"
    "Name of temp file to use for AST generation")
  
  (defvar pyel-interactive nil
    "non-nil during interactive session translation and evaluation")
  
  (defvar pyel-last-type-env nil
    "reverence to type environment for testing purposes")
  (defvar pyel-last-func-type-env nil
    "reverence to type environment for testing purposes")
  (defvar pyel-last-class-type-env nil
    "reverence to type environment for testing purposes")
  
  (defvar expand-resolved-transforms t
    "when non-nil, expand transforms that have been resolved
  to one case instead of generating a macro call.")
#+END_SRC
** syntax output variables
variables that effect the form of the output code
#+Begin_SRC emacs-lisp :tangle pyel.el
  (defvar pyel-default--init--method
    "(defmethod --init-- ((self %s))
       \"Default initializer\"
      )"

    "default initializer for pyel objects.")

  (defvar pyel-use-list-for-varargs nil
    "Determines if *varargs will be passed to function as a list or a vector,
    non-nil for list, otherwise vector.
    To be like python (vectors), this should be nil
    To be consistent with Emacs-Lisp (lists), this should be t.
       On the python side, this means that *varargs is a list instead of a tuple")
#+End_SRC
** pyel-block
This will have to be removed after the code is generated
#+Begin_SRC emacs-lisp :tangle pyel.el
  (defmacro pyel-block (&rest code)
    `(progn ,@ code))
#+END_SRC
** context
#+Begin_SRC emacs-lisp :tangle pyel.el
  ;;TODO: this should be generalized and added to the transform code
  (defvar pyel-context nil
    "list of current expansion contexts")
  
  (defmacro using-context (context &rest code)
    `(progn
       (let ((__using-context_n_ 0)
             _using-context_ret_)
         (if (listp ,context)
             (dolist (c ,context)
               (when c
                 (push c pyel-context)
                 (incf __using-context_n_)))
           (push ,context pyel-context)
           (setq __using-context_n_ 1))
         (condition-case err
             (setq _using-context_ret_ (progn ,@code))
           (error (pop pyel-context)
                  (error (format "context %s: %s" ',context err))))
         (dotimes (_ __using-context_n_)
           (pop pyel-context))
         _using-context_ret_)))
  
  (def-edebug-spec using-context (sexp &rest form))
  
  (defmacro remove-context (context &rest code)
    "remove CONTEXT and translate CODE, then restore context"
    `(let ((pyel-context (remove ',context pyel-context)))
       ,@code))
  
  (def-edebug-spec remove-context (symbolp &rest form))
  
  (defmacro context-switch (&rest forms)
    `(cond ,@(mapcar (lambda (x)
                       `(,(let ((context (car x)))
                            (if (eq context t) t
                              `(member ',context pyel-context))) ,@(cdr x)))
                     forms)))
  
  (defun get-context-group (context)
    (let ((groups pyel-context-groups)
          (found nil)
          group)
      (while groups
        (setq group (car groups)
              groups (cdr groups))
        (dolist (g group)
          (when (equal g context)
            (setq found group
                  groups nil))))
      found))
  
  ;; (defun context-p (context)
  ;;   (member context pyel-context))
  (defun context-p (context)
      ;;;;TODO: the extra features that this provides is probably not being used anywere...
    (let ((group (get-context-group context))
          (cont pyel-context)
          (ret nil)
          c)
      (while cont
        (setq c (car cont)
              cont (cdr cont))
        ;;if the context is in a group only return t if it is the first one in pyel-context
        (if (member c group)
            (setq ret (equal c context)
                  cont nil)
          (when (equal c context)
            (setq ret t
                  cont nil))))
      ret))
  
  (defun context-depth (context)
    "get the depth of CONTEXT in `pyel-context'"
    ;;TODO:
    )
#+end_src
** type dispatching transforms
#+Begin_SRC emacs-lisp :tangle pyel.el
  (defvar pyel-last-transform nil
    "name of the last transform that was expanded with
  `pyel-do-call-transform'")
  
  (defun pyel-filter-non-args(args)
    "remove '&optional' and '&rest' from ARGS list"
    (filter (lambda (x) (not (or (eq x '&optional)
                                 (eq x '&rest))))
            args))
  
  (defun pyel-group-&rest (lst num)
    "return a list of NUM items from LST with the remainder elements
  grouped into a list as the last element"
    (setq _ii lst)
    (let ((len (length lst))
          fst rst)
      (cond ((< len num) ;;?
             (setq fst lst
                   rst nil))
            ((= len num)
             (setq fst (subseq lst 0 (1- len))
                   rst (last lst)))
            (t
             (setq fst (subseq lst 0 (setq num (1- num)))
                   rst (subseq lst num))))
      (setq _rr (append fst (list rst)))))
  
  (defmacro pyel-dispatch-func (name args &rest type-switches)
    "Define a transform that creates a runtime function that
  dispatches on argument type as defined by TYPE-SWITCHES.
  The transform will have the same NAME and ARGS and must be called with a
  function like `call-transform', it will return a call to
  the function it creates.
  After the resulting transform is called, it adds the name of the
  created function in `pyel-defined-functions' and adds the function
  definition to `pyel-function-definitions'
  
  Use `pyel-func-transform' to define transforms for functions that
  will be automatically called.
  
  NOTE: if the name of the function to be created is already in
   `pyel-defined-functions', the function will not be updated
  "
    ;;temp solution: does not check types etc
    (let* ((striped-args (mapcar 'strip_ args))
           (args-just-vars (pyel-filter-non-args striped-args))
           (rest-arg (if (eq (car (last striped-args 2)) '&rest)
                         (car (last striped-args)) nil))
           (non-rest (if rest-arg (subseq striped-args 0 -2)))
           (name-base (concat "pyel-" (symbol-name name) ""))
           ;;(fsym (intern (concat "pyel-" (symbol-name name) "")))
           ;;no-macro
           )
  
      `(def-transform ,name pyel ()
         (lambda ,striped-args
           (setq pyel-last-transform ',name)
           (let ((body (pyel-do-call-transform (pyel-get-possible-types
                                                ;;,(cons 'list args-just-vars))
                                                ,(if rest-arg
                                                     `(append ,(cons 'list non-rest)
                                                              ,rest-arg)
                                                   (cons 'list args-just-vars)))
                                               ',args
                                               ',type-switches))
                 (_pyel_name (intern (concat ,name-base
                                             (number-to-string _pyel-type-sig))))
                 ;;(_pyel_name (intern ,name-base ))
                 )
             (unless (member _pyel_name pyel-defined-functions)
               (push (list 'defmacro _pyel_name ',striped-args
                           body)
                     pyel-function-definitions)
               (push _pyel_name pyel-defined-functions)
               (fset _pyel_name (lambda () nil)))
             (setq args ,(if rest-arg
                             `(append (list ,@(subseq args-just-vars 0 -1)) ,rest-arg)
                           (cons 'list args-just-vars)))
             (if (and pyel--transform-resolved
                      expand-resolved-transforms)
                 (progn
                   (setq args (pyel-do-splices args))
                   ,(if rest-arg
                        `(setq args
                               (pyel-group-&rest args ,(length args-just-vars))))
  
                   (progv
                       ',args-just-vars
                       args
                     (eval body)))
               (cons _pyel_name args))
             )))))
  
  
  (defmacro pyel-method-transform (name args &rest type-switches)
    (add-to-list 'pyel-method-transforms name)
    `(pyel-method-transform-1 ,name ,args nil ,@type-switches))
  
  (defmacro pyel-method-kwarg-transform (name args &rest type-switches)
    "transforms will have the kwarg alist passed as the first parameter
  This transform is only called if there are kwargs. If the kwargs are
  optional to the transform, then a separate transform should be defined
  with `pyel-func-transform' to handle that case."
    (add-to-list 'pyel-method-kwarg-transforms name)
    `(pyel-method-transform-1 ,name ,args t ,@type-switches))
  
  (defmacro pyel-method-transform-1 (name args kwargs &rest type-switches)
    "Defines a transform for methods that dispatches on NAME and ARG length.
  The syntax and the function creation is the same as with `pyel-dispatch-func'.
  These transforms are automatically called for methods during translation time.
  The transform will be dispatched on NAME and the possible number
  of arguments that ARGS allows.
  During translation time, if no transform is found for a method call that
  matches NAME and has the proper arg length then no transform will be called."
    (add-to-list 'pyel-method-transforms name)
   
    ;;temp solution: does not check types etc
    (let* ((striped-args (mapcar 'strip_ args))
           (args-just-vars (pyel-filter-non-args striped-args))
           (rest-arg (if (eq (car (last striped-args 2)) '&rest)
                         (car (last striped-args)) nil))
           (non-rest (if rest-arg (subseq striped-args 0 -2)))
           (name-base (format "pyel-%s%s-method%s"
                              (symbol-name name)
                              (if kwargs "-kwarg" "")
                              (pyel-arglist-signature args)))
           ;;(transform-name (pyel-method-transform-name name kwargs (list args))))
           (transform-name (pyel-method-transform-name name (list args))))
  
      (pyel-add-method-name-sig name args)
      
      `(def-transform ,transform-name pyel ()
         (lambda ,striped-args
           (setq pyel-last-transform ',name)
           (let ((body (pyel-do-call-transform (pyel-get-possible-types
                                                ,(if rest-arg
                                                     `(append ,(cons 'list non-rest)
                                                              ,rest-arg)
                                                   (cons 'list args-just-vars)))
                                               ',args
                                               ',type-switches))
                 (_pyel_name (intern (concat ,name-base
                                       (number-to-string _pyel-type-sig)))))
  
             (unless (member _pyel_name pyel-defined-functions)
               (push (list 'defmacro _pyel_name ',striped-args
                           body)
                     pyel-function-definitions)
               (push _pyel_name pyel-defined-functions)
               (fset _pyel_name (lambda () nil)))
             (cons _pyel_name ,(if rest-arg
                               `(append (list ,@(subseq args-just-vars 0 -1)) ,rest-arg)
                             (cons 'list args-just-vars))))))))
  
  (defvar pyel-method-transforms2 nil
    "list of functions whose translations are defined
    with the macro `pyel-define-method-translation'")
   
  (defmacro pyel-define-method-translation (name args &rest body)
    "BODY will form the body of a function that is called during transform time
  to translate the method NAME, variables 'ojb', 'args', and 'kwargs'
  are available at this point. 'obj' is the object the method is called on,
  'args' will be a list and 'kwargs will be an alist This is called at the
  same time `pyel-method-transform' would be called ARGS discribe the type
  of argument list that this transformation willbe called for. Only the number
  and type of arguments matter, the names have no effect on the transform
  and cannot be used to reference the actual argument values"
    (add-to-list 'pyel-method-transforms2 name)
    (pyel-add-method-name-sig name args)
  
    `(def-transform ,(pyel-func-transform-name name) pyel ()
       (lambda (obj args kwargs)
         ,@body
         )))
  
  (defmacro pyel-func-transform (name args &rest type-switches)
    (add-to-list 'pyel-func-transforms name)
    ;;TODO: should name be modified to avoid conflicts ?
    `(pyel-func-transform-1 ,name ,args nil ,@type-switches))
  
  (defmacro pyel-func-kwarg-transform (name args &rest type-switches)
    "transforms will have the kwarg alist passed as the first parameter
  This transform is only called if there are kwargs. If the kwargs are
  optional to the transform, then a separate transform should be defined
  with `pyel-func-transform' to handle that case."
    (add-to-list 'pyel-func-kwarg-transforms name)
    `(pyel-func-transform-1 ,name ,args t ,@type-switches))
  
  (defmacro pyel-func-transform-1 (name args is-kwarg-transform &rest type-switches)
    "Define a transform for function calls.
  This is just like `pyel-method-transform' except that the
  ARG signature has no effect on the transform dispatch"
    
    (let* ((striped-args (mapcar 'strip_ args))
           (args-just-vars (pyel-filter-non-args striped-args))
           (rest-arg (if (eq (car (last striped-args 2)) '&rest)
                         (car (last striped-args)) nil))
           (non-rest (if rest-arg (subseq striped-args 0 -2)))
           (name-base (concat "pyel-"
                              (symbol-name name) "-"
                              (if is-kwarg-transform "kwarg-" "")
                              "function")))
      `(def-transform ,(pyel-func-transform-name name is-kwarg-transform) pyel ()
         (lambda ,striped-args
           (setq pyel-last-transform ',name)
           (let ((body (pyel-do-call-transform (pyel-get-possible-types
                                                ,(if rest-arg
                                                     `(append ,(cons 'list non-rest)
                                                              ,rest-arg)
                                                   (cons 'list args-just-vars)))
                                               ',args
                                               ',type-switches))
                 ;;NOTE: if the way this name is constructed changes,
                 ;; the aliases for py-list, pyel-str-function,
                 ;; and pyel-repr-function will have to be updated.
                 (name (intern (concat ,name-base
                                       (number-to-string _pyel-type-sig)))))
             (unless (member name pyel-defined-functions)
               (push (list 'defmacro name ',striped-args
                           body)
                     pyel-function-definitions)
               (push name pyel-defined-functions)
               (fset name (lambda () nil)))
             (cons name ,(if rest-arg
                             `(append (list ,@(subseq args-just-vars 0 -1)) ,rest-arg)
                           (cons 'list args-just-vars))))))))
  
  (defvar pyel-func-transforms2 nil
    "list of functions whose translations are defined
  with the macro `pyel-define-function-translation'")
  
  (defmacro pyel-define-function-translation (name &rest body)
    "BODY will form the body of a function that is called during transform time
  to tranlate a call to NAME, variables 'args' and 'kwargs'  are available at
  this point. 'args' will be a list and 'kwargs will be an alist
  This is called at the same time `pyel-func-transform' would be called"
    (add-to-list 'pyel-func-transforms2 name)
  
    `(def-transform ,(pyel-func-transform-name name) pyel ()
       (lambda (args kwargs)
         ,@body
         )))
  
  ;;TODO: this should be more general to allow for things like subscript to use it
  
  (defmacro pyel-def-funcall (name args &rest type-switches)
    "Define how to call the function NAME.
        NAME is a function that is called differently based on its argument types.
        An attempt will be made to test the least possible number of types.
  
        This defines a transforms in the pyel transform table with NAME and ARGS"
    `(def-transform ,name pyel ()
       (lambda ,args
         (setq pyel-last-transform ',name)
         (pyel-do-call-transform (pyel-get-possible-types ,(cons 'list args))
                                 ',args
                                 ',type-switches))))
  
  ;;TODO: rename pyel-def-funcall -> pyel-def-type-transform
  (defmacro pyel-def-type-transform (name args &rest type-switches)
    "Define a transform NAME that produces code based on the types of ARGS
      TYPE-SWITCHES
  
      This defines a transforms in the pyel transform table with NAME and ARGS"
    `(def-transform ,name pyel ()
       (lambda ,args
         (setq pyel-last-transform ',name)
         (pyel-do-call-transform (pyel-get-possible-types ,(cons 'list args))
                                 ',args
                                 ',type-switches))))
  
  ;; (defmacro pyel-def-call-func (name args &rest type-switches)
  ;;   "like `pyel-def-call-template' except that it generates a macro that
  ;; is called directly, because of this NAME must be unique"
  ;;   `(defun ,name ,args
  ;;      ;;   (let (,(mapcar (lambda (arg) `(,arg ',arg)) args))
  ;;      (pyel-do-call-transform (pyel-get-possible-types ,@args)
  ;;                              ',args
  ;;                              ',type-switches)))
  
  (defun pyel-expand-type-switch (type-switch)
    "expands the types switch form to a list of cond clauses"
    (flet ((helper (arg form)
                   (let ((type (car form))
                         (varlist (cadr form))
                         mod types ret and-or)
                     (if (consp type)
                         (progn
                           ;;TODO: this can never be 'and'
                           (setq and-or (car type))
                           (dolist (tp (cdr type))
                             (push `((,arg ,tp) ,varlist) ret))
                           `(,and-or ,@(reverse ret)))
                       `((,arg ,type) ,varlist))))
           (expander (type-switch)
  
                     (let ((args (car type-switch))
                           (forms (cdr type-switch))
                           and-or ret inner tmp tests)
                       (if (consp args)
                           (progn
                             (setq and-or (car args)) ;;TODO: verify and/or
                             (if (eq and-or 'and)
                                 (dolist (form forms)
                                   (setq tests nil)
                                   (dolist (arg (cdr args))
                                     (push (car (helper arg form)) tests))
                                   (push `(and ,(reverse tests) ,(cadr form)) ret))
                               ;;else == or
                               (dolist (form forms)
                                 (dolist (arg (cdr args))
                                   (setq tmp (helper arg form))
                                   (if (equal (car tmp) 'or)
                                       (dolist (x (cdr tmp))
                                         (push x ret))
                                     (push (helper arg form) ret))))))
  
                         ;;else single arg
                         (dolist (form forms)
                           (setq tmp (helper args form))
                           (if (equal (car tmp) 'or)
                               (dolist (x (cdr tmp))
                                 (push x ret))
                             (push tmp ret))))
                       (reverse ret))))
      (let ((ret nil))
        (dolist (ts type-switch)
          (dolist (e (expander ts))
            (push e ret)))
        (reverse ret))))
  
  ;;TODO: fix bug with  `pyel-expand-type-switch-2'
  ;;      the arg pattern (x x) should not expand unless
  ;;      x is a possible type of both args
  
  (defun pyel-expand-type-switch-2 (arglist patterns)
    "has output identical to `pyel-expand-type-switch' just translates
        different syntax"
    (let ((group nil)
          (groups nil)
          (ngroups nil)
          (ret nil)
          code)
  
      ;;stage1: collect into groups
      (while patterns
        (setq p (pop patterns))
  
        (if (not (eq p '->))
            (push-back p group)
          (push-back (pop patterns) group)
          (push-back group groups)
          (setq group nil)))
  
      (dolist (g groups)
        (let* ((g (reverse g))
               (code (car g))
               (g (cdr g))
               ;;      (param-types (make-vector (length (car g)) nil))
               type)
  
          (dolist (arg-pattern (reverse g)) ;;for each arg pattern
            (setq group-patterns nil)
            (dotimes (i (length arg-pattern)) ;;for each type or '_
              (setq type (nth i arg-pattern))
              (if  (not (eq type '_))
                  (push `((,(nth i arglist) ,type) ,code) group-patterns)))
            (if (null group-patterns) ;;all types where _
                (setq ret (append ret (list (list t code))))
              (if (= (length group-patterns) 1)
                  (setq ret (append ret (reverse group-patterns)))
                (setq ret (append ret (list (cons 'and
                                                  (list (mapcar 'car
                                                                (reverse group-patterns))
                                                        code))))))))))
      ret))
  
  (defun pyel-remove-nil (list)
    "remove all nil items from LIST"
    (let ((new nil))
      (dolist (e list)
        (when e
          (setq new (cons e new))))
      (reverse new)))
  
  (defvar _pyel-type-sig 0
    "set by `pyel-do-call-transform'. Its value uniquely describes
  which types swiches where successful eliminated from the most
  recent transform. It is a binary number ranging from 1 to 2^n, where n
  is the number of type switches. Each digit corresponds to a type switch
  (in order), a value of 1 indicates that the type switch was included.")
  
  
  (defvar pyel--transform-resolved nil
    "used to enform callers of `pyel-do-call-transform' that
  the transform has been resolved to one case.
  Passed via dynamic scoping.")
  
  (defun pyel-do-call-transform (possible~types args type-switch)
    "This is responsible for  producing a call to NAME in the most
        efficient way possible with the known types"
  
    ;; the args of the type transform are evaled here so there must
    ;; be no conflicts with the naming of internal variables
    ;; To avoid such conflicts, the let bound variables are all
    ;; defined with a tilde
  
    (let* ((n~args (length possible~types))
           (possible~types (let (ret arg)
                             ;;get entries in form (arg . type)
                             (dolist (p-t possible~types)
                               (setq arg (car p-t))
                               (dolist (type (cdr p-t))
                                 (push (cons arg type) ret)))
                             ret))
           ;;if there is one possible type for every arg then we don't need to
           ;;use the default case unless the default is the only matchine case
           (use~default~p (not (= n~args (length possible~types))))
           (args~just~vars (pyel-filter-non-args (mapcar 'strip_ args)))
           (new~args (loop for a in args
                           collect (if (or (eq a '&optional)
                                           (eq a '&rest)
                                           (string-match-p "\\(^_\\)\\(.+\\)"
                                                           (symbol-name a))) nil
                                     (intern (format "__%s__" (symbol-name a))))))
           (arg~replacements4 (let (ar)
                                (mapcar (lambda (x) (if (string-match-p "\\(^_\\)\\(.+\\)"
                                                                        (symbol-name x))
                                                        (push (list (strip_ x) (list '\, (strip_ x))) ar)))
  
                                        args)
                                ar))
           ;;list of symbols to replace
           ;;format: (symbol replace)
           (let~vars (let (lv) (mapcar* (lambda (a b) (if b
                                                          (push (list a b) lv)))
                                        args new~args)
                          lv))
           ;;strip any leading underscores
           (args~ (mapcar (lambda (a)
                            (if (string-match "\\(^_\\)\\(.+\\)" (symbol-name a))
                                (intern (match-string 2 (symbol-name a))) a))
                          args))
  
           ;;the __x__ type replacements interfere with the (\, x) type replacements
           ;;so they must be seporated and done one at a time
           (arg~replacements1 let~vars)
           (arg~replacements2 (mapcar (lambda (x)
                                        (list  (intern (format "$%s" x)) (list '\, x)))
                                      args~just~vars))
           (arg~replacements3 (mapcar (lambda (x)
                                        (list (intern (format "$$%s" x)) (list 'quote (list '\, x))))
                                      args~just~vars))
           (arg~replacements (append arg~replacements1 arg~replacements2))
  
           (arg~quote~replacements (mapcar (lambda (x)
                                             (list x (list '\, x)))
                                           args~just~vars))
           ;;replacements for the case in which all types are known and nothing
           ;;is let-bound, arg~quote~replacements must come first
           (all~known~replacements (append arg~quote~replacements
                                           arg~replacements3
                                           arg~replacements2))
           (current~replace~list nil)
           
           ;; (arg~replacements (append let~vars
           ;;                           (mapcar (lambda (x)
           ;;                                     (list  (intern (format "$%s" x)) (list '\, x)))
           ;;                                   args~)))
  
           (c~ 0)
           (n -1)
           valid~ ;;list of valid arg--types
           found~ all~good len~ default~ default~n)
  
      (setq _pyel-type-sig 0)
      
      ;;collect all the arg-type--code pairs that are valid possibilities,
      ;;that is, members of possible~types.
      ;;This essentially throws out all the arg types that have been ruled out.
      (dolist (t~s (pyel-expand-type-switch-2 args~just~vars type-switch))
        (incf n)
        (if (equal (car t~s) 'and)
            (progn (setq all~good t)
                   (dolist  (~x (cadr t~s)) ;;for each 'and' member type-switch
                     (setq found~ nil) 
                     (dolist (pos~type possible~types) ;;for each arg type
                       (if (and (equal (eval (car ~x)) (car pos~type))
                                (pyel-type-compare (cadr ~x) (cdr pos~type)))
                           (setq found~ t)))
                     (setq all~good (if (and all~good found~) t nil)))
                   (when all~good
                     (push t~s valid~)
                     (setq _pyel-type-sig (logior _pyel-type-sig (expt 2 n)))))
          ;;else
          (if (eq (car t~s) t);when all types are _
              (progn (setq default~ t~s
                           default~n n))
            ;;otherwise check if the type is one of the valid types
            (dolist (pos~type possible~types)
              (when (and (equal (eval (caar t~s)) (car pos~type))
                         (pyel-type-compare (strip$ (cadar t~s)) (cdr pos~type)))
                (push t~s valid~);TODO: break if found?
                (setq _pyel-type-sig (logior _pyel-type-sig (expt 2 n))))))));
  
      (when (and (or (= (length valid~) 0)
                   use~default~p)
               default~)
          (push default~ valid~)
          (setq _pyel-type-sig (logior _pyel-type-sig (expt 2 default~n))))
          
      ;;generate code to call NAME
      ;;if there is 2 posible types, use IF. For more use COND
      (setq len~ (length valid~))
      (setq pyel--transform-resolved nil)
      (cond ((<= len~ 0)
             (error "unable to match dispatch types: %s" pyel-last-transform))
            ((= len~ 1)
             ;;there is only one possibility, so replace the args with their
             ;;quoted counterpart instead of replacing with the let bound vars
             (setq pyel--transform-resolved t)
             (list 'backquote (pyel-replace-backquoted (if (eq (caar valid~) 'and)
                                                           (caddar valid~)
                                                         (cadar valid~))
                                                       all~known~replacements)))
            
            ;;?TODO: are there possible problems with evaluating the arguments
            ;;       multiple times? Maybe they should be put in a list
            (t (let* ((clauses (mapcar 'pyel-gen-cond-clause valid~))
                      (clauses (if (eq (caar clauses) t)
                                   clauses
                                 (cons
                                  '(t (error "invalid type, expected <TODO>"))
                                  clauses)))
                      (varlist (pyel-gen-varlist)))
                 `(backquote ,(if varlist
                                  `(let ,varlist
                                     (cond ,@(reverse clauses)))
                                `(cond ,@(reverse clauses)))))))))
  
  (defsubst pyel-type-compare (switch-type possible-type)
    (if (eq switch-type 'object)
        (or (eq possible-type 'instance)
            (eq possible-type 'class)
            (eq possible-type 'object))
      (eq switch-type possible-type)))
  
  ;;helper functions for pyel-do-call-transform
  (defun pyel-replace (code replacements)
    (let ((ret nil)
          found)
      (dolist (c code)
        (setq found nil)
        (if (consp c)
            (push (pyel-replace c replacements) ret)
          (dolist (r replacements)
            (if (and (equal c (car r))
                     (not found))
                (progn (push (cadr r) ret)
                       (setq found t))))
          (unless found
            (push c ret))))
      (reverse ret)))
  
  (defun pyel-replace-backquoted (code replacements)
    "only replace in code that is backquoted.
  unquote or splice regions are ignored"
    ;;TODO: nested backquotes
      (let ((ret nil)
            found)
        (dolist (c code)
          (setq found nil)
          (if (and (consp c)
                   (not (eq (car c) '\,))
                   (not (eq (car c) '\,@)))
              (push (pyel-replace c replacements) ret)
            (dolist (r replacements)
              (if (and (equal c (car r))
                       (not found))
                  (progn (push (cadr r) ret)
                         (setq found t))))
            (unless found
              (push c ret))))
        (reverse ret)))
  
  (defsubst pyel-type-tester (x)
    (cadr (assoc x pyel-type-test-funcs)))
  
  (defsubst pyel-and-type-tester (x)
    (cadr (assoc (car x) pyel-type-test-funcs)))
  
  (defsubst pyel-get-replacement (arg) ;;returns arg replacement
    (cadr (assoc arg current~replace~list)))
  
  ;;bug fix maybe...
  (defsubst pyel-get-replacement-OLD (arg) ;;returns arg replacement
    (or (cadr (assoc arg arg~replacements))
        (cadr (assoc arg arg~replacements4))))
  
  ;;replaces the vars, one type at a time
  (defun pyel-replace-vars (code)
    (let* ((current-replace-list arg~replacements1)
           (code (pyel-replace code arg~replacements1))
           (current-replace-list arg~replacements2)
           (code (pyel-replace code arg~replacements2))
           (current-replace-list arg~replacements3))
      (pyel-replace code arg~replacements3)))
  
  (defun pyel-gen-cond-clause (t-s--c) ;;Type-Switch--Code
    (if (equal (car t-s--c) 'and)
        (progn `((and ,@(mapcar '(lambda (x)
                                   ;;TODO: test
                                   `(,(pyel-type-tester (cadr x))
                                     ,(pyel-get-replacement-OLD
                                       (car x))))
                                (cadr t-s--c)))
                 ,(pyel-replace-vars (caddr t-s--c))))
  
      ;;TODO
      (progn (if (equal (car t-s--c) t) ;;all types where _
                 `(t ,(pyel-replace-vars (cadr t-s--c)))
               (let* ((str (symbol-name (cadar t-s--c)))
                      (quote-arg-p (string-match-p "\\(^\\$\\)\\(.+\\)"
                                                   str))
                      (type (if quote-arg-p (intern (match-string 2 str)) (cadar t-s--c)))
                      (tester (pyel-type-tester type))
                      (body (pyel-replace-vars (cadr t-s--c)))
                      (arg (pyel-get-replacement-OLD (caar t-s--c))))
                 `((,tester ,(if quote-arg-p (list 'quote (list '\, (caar t-s--c))) arg))
                   ,body))))))
  
  (defsubst pyel-gen-varlist ()
    (mapcar (lambda (x) `(,(cadr x) ,(list '\, (car x))))
            let~vars))
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  (defun pyel-get-trans--type (form)
    "return a cons cell with the form (transformed . type) where
  'transformed is the transformed value of FORM and 'type' is the
  possible type or types of FORM"
    (let* (t-form
           (type (if (symbolp form)
                     (progn (setq t-form form)
                            (pyel-env-get form type-env))
                   (setq t-form (using-context 'return-type?
                                               (transform form)))
                   return-type)))
      (setq type (cond ((pyel-is-func-type type)
                        (pyel-func-return-type type))
                       ((pyel-is-class-type type)
                        '(class)) ;;for transforms, they are all the same
                       ((pyel-is-instance-type type)
                        '(instance))
                       (t type)))
      (if (null type)
          ;;if type is not known, it could be anything
          (setq type pyel-possible-types))
      ;;type for form must be a list
      (if (listp type)
          (cons t-form type)
        (cons t-form (list type)))))
  
  (defun call-transform (template-name &rest args)
    "expand TEMPLATE-NAME with ARGS in the same way that `transform' would
  if was called as (transform '(template-name args))
  NOTE: this calls `transform' on all ARGS, but not TEMPLATE-NAME"
    (let (t-args) ;;t-args is set by 
      (setq known-types (mapcar (lambda (x)
                                  (setq x (pyel-get-trans--type x)
                                        t-args (cons (car x) t-args))
                                  (cdr x))
                                args)
  
            t-args (reverse t-args))
      (eval `(transform '(,template-name ,@t-args)))))
  
  
  (defun call-transform-no-trans (template-name &rest args)
    "like call-transform, except that ARGS are not transformed"
    (eval `(transform '(,template-name ,@args))))
  
  (defun strip$ (sym)
    (let ((str (symbol-name sym)))
      (if (string-match "\\(^\\$\\)\\(.+\\)" str)
          (intern (match-string 2 str))
        sym)))
  (defun strip_ (sym)
    (let ((str (symbol-name sym)))
      (if (string-match "\\(^_\\)\\(.+\\)" str)
          (intern (match-string 2 str))
        sym)))
  
  (defun pyel-arg-descriptor (arglist)
    "return the number of values that may be passed to ARGLIST
  If ARGLIST contains &optional or &rest then return a cons of
  the min and max values that may be passed.
  
  This does not check if ARGLIST has a valid form"
  
    (let ((min 0)
          (max 0)
          optional)
      (when arglist
        (if (member '&rest arglist)
            (setq max 'I
                  arglist (subseq arglist 0 -2)))
        (if (member '&optional arglist)
            (setq optional (pyel-split-list arglist '&optional)
                  min (length (car optional)) ;;positional args
                  max (if (eq max 'I) max
                        (+ min (length (cdr optional))))) ;;optional args
          (setq min (length arglist)
                max (if (eq max 'I)
                        max
                      min))))
      (if (or (equal min max)
              (and (= min 0)
                   (eq max 'I)))
          max
        (cons min max))))
  
  (defun pyel-arglist-signature (arglist)
    (let ((num (pyel-arg-descriptor arglist)))
      (format "->%s<-" (if (or (numberp num)
                               (eq num 'I))
                           num
                         (format "%s~%s" (car num) (cdr num))))))
  
  (defun pyel-extract-arg-descriptor (name)
    "extract the arglist descriptor from name"
  
    (assert (stringp name) "Name must be a string")
    (if (symbolp name) (setq name (symbol-name name)))
    (let (min max I?)
      (cond ((string-match "->\\([0-9I]+\\)<-" name)
             (setq min (match-string 1 name)
                   max min))
  
            ((string-match "->\\([0-9]+\\)~\\([0-9I]+\\)<-" name)
             (setq min (match-string 1 name)
                   max (match-string 2 name))))
  
      (if min
          (setq I? (intern max)
                max (if (eq I? 'I) 'I (string-to-number max))
                min (string-to-number min)))
  
      (cond ((null min) nil)
            ((or (equal min max)
                 (and (= min 0)
                      (eq max 'I)))
             max)
            (t (cons min max)))))
  
  (defun pyel-arg-descriptor-to-signature (descriptor)
    (format "->%s<-" (if (or (numberp descriptor)
                             (eq descriptor 'I))
                         descriptor
                       (format "%s~%s" (car descriptor) (cdr descriptor)))))
  
  (defvar pyel-method-name-arg-signature (make-hash-table :test 'eq)
    "mapping of method transform names to a list of argument signatures")
  
  (defun pyel-add-method-name-sig (name args)
    "Add the argument signature of ARGS to NAME in `pyel-method-name-arg-signature'"
    (let* ((signatures (gethash name pyel-method-name-arg-signature)))
      (add-to-list 'signatures (pyel-arg-descriptor args))
      (puthash name signatures pyel-method-name-arg-signature)))
  
  (defun pyel-find-method-transform-name (name num-args &optional kwarg)
    "find a matching method transform for NAME with NUM-ARGS
  will return the name of the first match, nil if there are no matches
  An error will be raised if transform NAME was never defined"
    (let ((signatures (gethash name pyel-method-name-arg-signature))
          found min max)
      (if signatures
          (progn (while signatures
                   (setq sig (car signatures)
                         signatures (cdr signatures))
  
                   (if (or (and (numberp sig)
                                (= sig num-args))
  
                           (eq sig 'I)
  
                           (and (consp sig)
                                (setq min (car sig)
                                      max (cdr sig))
                                (and (>= num-args min)
                                     (or (and (numberp max) (<= num-args max))
                                         (eq max 'I)))))
                       (setq signatures nil
                             found sig)))
                 (if found
                     (intern (format pyel-method-name-format-string
                                     name
                                     (if kwarg "-kwarg" "")
                                     (pyel-arg-descriptor-to-signature found)))))
        (error "method transform '%s' does not exist in the signature table"
               name))))
  
#+END_SRC
*** example usage
(pyel-def-call-func pyel+ (lhs rhs)
		    ((or lsh rhs)
		     (string (concat lhs rhs))
		     (number (+ lhs rhs))
		     (list (append lhs rhs)
			   (vector (ldk lhs rhs)))))


(setq known-types '((string list) (number vector)))

(pyel+ 'a 'b)
==>  (cond
      ((listp a)
       (append a b))
      ((numberp b)
       (+ a b))
      ((stringp a)
       (concat a b))
      (t
       (error "invalid type, expected <TODO>")))


TODO: describe alternative syntax
==> look at the bin-op heading for examples
(pyel-def-funcall * (l r)
                    (number number) ->  (* l r)
                    (object _)
                    (_ object)  -> (--mul-- l r)
                    (_ string)
                    (string _)  -> (pyel-num*str l r))

*** tests
auto generated tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;
#+END_SRC
hand written
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (ert-deftest pyel-test-expand-type-switch ()
    (should (equal (pyel-expand-type-switch-2 '(l r)
                                              '((number number) ->  (* l r)
                                                (object _)
                                                (_ object)  -> (--mul-- l r)
                                                (_ string)
                                                (string _)  -> (pyel-num*str l r)))
                   '((and ((l number) (r number)) (* l r)) ((l object) (--mul-- l r)) ((r object) (--mul-- l r)) ((r string) (pyel-num*str l r)) ((l string) (pyel-num*str l r))))))
#+END_SRC

** translation messages
#+Begin_SRC emacs-lisp :tangle pyel.el
(defvar pyel-translation-messages nil
  "collects messages during pyel translations")

(defvar pyel-message-formats '((error "ERROR: %s")
			       (warn "WARNING: %s")
			       (recommend "RECOMMENDATION: %s"))
  "alist of message type and their format strings")

(defun pyel-notify (type msg)
  "add MSG to `pyel-translation-messages', TYPE specifies the format string
in `pyel-message-formats'"
  (push (format (or (cadr (assoc type pyel-message-formats))
		    (format "[%s]: %%s" (upcase (symbol-name type))))
		msg) pyel-translation-messages))

#+END_SRC
** line/column numbers
#+Begin_SRC emacs-lisp :tangle pyel.el
  (defun pyel-skip-whitespace ()
    (skip-chars-forward " \t\n\r"))
  
  (defun char-at-point ()
    (buffer-substring-no-properties (point) (1+ (point))))
  
  (defun read-tree-positions ()
    "Create a tree of buffer positions corresponding to the source tree at the point
  format [start end list-of-sub-trees] list-of-sub-trees is nil for leaves"
    (pyel-skip-whitespace)
    (let ((start (point))
          inner)
      (goto-char (1+ start))
      (setq inner (read-list-positions)) ;;asssumes we start on a list
      (vector start (point) inner)))
  
  (defun read-list-positions ()
    (let (start end elems)
      (condition-case nil
          (while t
            (pyel-skip-whitespace)
            (setq start (point))
            (if (string= (char-at-point) "(")
                (push (get-tree-positions) elems)
              (setq end (scan-sexps start 1))
              (push (vector start end nil) elems)
              (goto-char end)))
        (scan-error (goto-char (1+ start))))
      (reverse elems)))
#+END_SRC
** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (ert-deftest pyel-test-do-splices ()
    (should (equal (pyel-do-splices '(a (@ b (c)))) '(a b (c))))
    (should (equal (pyel-do-splices '(a (@ b c)))  '(a b c)))
    (should (equal (pyel-do-splices '(a (@ b (c (@ 2 (n (x 1 (@ 2))) 3 (@ 3) (@ a b (2)))))))
                   '(a b (c 2 (n (x 1 2)) 3 3 a b (2)))))
  
    (should (equal (pyel-do-splices '(@ (a b (@ d (e 2 (@ a b c ))))))
                   '(a b d (e 2 a b c))))
  
    (should (equal (pyel-do-splices '(@ (a b (@ d (e 2 (@ a b c )))) last))
                   '(progn (a b d (e 2 a b c)) last)))
  
    (should (equal (pyel-do-splices '(@)) nil))
    )
#+END_SRC

* python preprocessor
The preprocessor is no longer tangled

(lambda (a b) (print a))
lambda((a,b)
       print(a))

(cond ((> a b) (print a))
      (t (print b)))
cond([a > b, print(a)]
     [true, print(b)])

(py-ast "lambda([a,b],
print(a)) " t)

preprocessor expand separately
  scan for regex
  copy form to seporate file and expand to pyel-ast
    store in hash table with unique ID
  in py-ast replace with variable that matches some regex and contains that id
  in pyel expanstion match names, when match found, transform ast from hash table

* transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
(make-transform-table 'pyel)
#+END_SRC
** tests
* python ast
This is for all non template specific code.
#+Begin_SRC python :tangle py-ast.py
  import ast
  import sys
  
  options = sys.argv[1:]
  if len(options) != 1:
      print("invalid usage")
      exit()
  
  #TODO: use *args
  
  def l_str(x):
      return "(" + " ".join(map(str,x)) + ")"
  
  def ll_str(x):
      if x == []:
          return "nil"
      elif type(x) == str:
          return x
      return "(" + " ".join(map(str,x)) + ")"
  #    return " ".join(map(str,x))
  #    return "(list " + " ".join(map(str,x)) + ")"
  # commented out for 'assign'. cause problems elsewhere?
  
  def untuple(x):
      return ll_str(x)
      if x == []:
          return "nil"
      elif type(x) == str:
          return x
  
  def Module(body):
      return "\n".join(body)
  
  def keyword(arg, value, lineno='nil', col_offset='nil'):
      return l_str(["(keyword ", arg, " ", value, lineno, col_offset, ")"])
  
  def Param():
      return "'param"
  
  def Load():
      return "'load"
  
  def Store():
      return "'store"
  
  def Expr(value, lineno='nil', col_offset='nil'): #?
      return value
  
  def Import(names, lineno=None, col_offset='None'):
      return l_str(["import ",ll_str(names), lineno, col_offset])
  
  def alias(name, asname):
      if asname: asname = "\"" + asname + "\""
      return l_str(["alias ", "\"" + name + "\"", asname or "nil"])
  
  def ImportFrom(module, names, level, lineno=None, col_offset='None'):
      return l_str(["import-from ","\"" + module + "\"", ll_str(names), level, lineno, col_offset])
#+END_SRC

* ---------------------
* ast transforms
** assign
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  ;; should not fset functions because the effect takes place globally
  ;; even when the name being set is let bound.
  (pyel-dispatch-func set (_sym _val)
                      ;;NOTE: if is changed, `setter-functions' 
                      ;; may need to be updated
                      (_ $function) -> (setq $sym $$val)
                      (_ _) -> (setq $sym $val)) ;;TODO: other?
  
  (def-transform assign pyel ()
    (lambda (targ val &optional line col) (py-assign targ val line col)))
  
  ;;TODO: put all setq's in a single form: (setq a 1 b 2) etc
  
  (defun py-assign (targets values &optional line col)
  
    (let ((wrap-values t)
          unpack i)
      ;;make sure targets and lists are both in a list form
      ;;the 'unpack' flag is needed because it leaves no difference
      ;;between a,b=c and a=b=c
      (if (and (listp (car targets))
               (eq (caar targets) 'tuple))
          (progn
  
            (if (eq (car values) 'tuple)
                (progn
  
                  (setq values (cadr values))
                  (setq wrap-values nil))
              (setq unpack t) ;;targets is a tuple and values is not
              ;;(setq values (list values))
              )
  
            (setq targets (cadar targets)))
        )
  
      (when wrap-values
        (setq values (list values)))
  
  
      ;;py-sssign2 does the main transforms
      ;;TODO: check for the special case a,b=b,a and create temp variables
      ;;TODO: check that legnth of the lists are the same
  
      ;;TODO:
      (cond (unpack
             ;;TODO: pyel error unless: (and (> (length targets) 1)  (= (length values) 1)
  
             (let* ((code '(@))
                   (tmp-var '~~value~~)
                   (name-ast (pyel-make-ast 'name tmp-var 'load))
                   type)
               `(let ((,tmp-var ,(prog1 (using-context 'return-type?
                                                       (transform (car values)))
                                   (pyel-env-set tmp-var return-type type-env))))
                  
                  ,(dotimes (i (length targets) (reverse code))
                   ;;;TODO: will have to help the transform know what type __value__ is
                     ;;(setq return-type type)
                     (push (py-assign2 (nth i targets)
  
                                       (pyel-make-ast 'subscript name-ast i 'load))
                           code))
                  )))
  
            ((= (length targets) 1)
             ;;form: a=b
             (py-assign2 (car targets)
                         (car values))) ;;if this is the second call of a "a,b = c" type form, then the ctx of values will be store instead of load which leads to an error
  
            ;;form: a = b = c
            ((and (> (length targets) 1) (= (length values) 1))
             ;; (list '@ (py-assign2 (car (last targets)) (car values))
             ;;          (py-assign  (butlast targets)
             ;;                      (pyel-change-ctx (car (last targets)) 'load))))
             (let ((out (py-assign2 (car (last targets)) (car values))))
               (setq values (car (last targets))
                     targets (butlast targets))
               (while targets
                 (setq out (py-assign2 (car (last targets)) out)
                       targets (butlast targets)))
               out))
  
  
            ;;form: a,b = x,y
            (t (let* ((tmp-vars (loop for i from 1 to (length targets)
                                      collect (intern (format "__%s__" i))))
                      (let-vars (mapcar* (lambda (a b)
                                           (list a (transform b)))
                                         tmp-vars values)))
                 `(let ,let-vars
                    ,(cons '@ (mapcar* 'py-assign2 targets tmp-vars))))))))
  
  ;;DOC: tranforms must be carefull not to transform code multiple times
  
  (defun py-assign2 (target value)
    ;;access line and col values from `py-assign' calling env.
    (let ((ctx (eval (car (last target 3))))
  
          (assign-value value))
  
  
      ;;the target code is responsible for providing the correct assign function
      ;;
  
      ;;TODO:     is context-value still used?
      ;; (using-context 'assign-target
      ;;             (setq t-target (transform target)))
      ;; (using-context 'assign-value
      ;;             (setq t-value (transform value)))
  
      ;;The target transform is responsible for generating the code
      ;;The value being assigned to the target is available to the
      ;;target transform via the variable 'assign-value'.
      ;;'assign-value' is untransformed, the target transform must tranform it.
  
      ;; (using-context 'assign-value
      ;;             (setq assign-value (transform value)))
      ;;problem: code was being transformed multiple times
      (setq assign-value value)
      (using-context 'assign-target
                     (transform target))
      ;;    (list assign-func t-target t-value)
  
      ))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Assign(targets, value, lineno='nil', col_offset='nil'):
      #    print("targets: ", targets)
      return l_str(["assign ", untuple(targets), value, lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   assign
   ("a = 1" ("a" 1))
   ("class a: pass
  a.b = 1" ("a.b" 1))
   "a.b = c"
   "a.b.c = 1"
   "a.b = d.c"
   ("a,b = 1,2"
    ("a" 1)
    ("b" 2))
   ("x = [1,0,9]
  f = lambda: 3
  class C: pass
  C.a = 3
  a, C.v, x[2] = C.a,1.1, x[x[1]]"
    ("a" 3)
    ("C.v" 1.1)
    ("x[2]" 1))
  
   ("a = 1
  b = 2
  a,b= b,a"
    ("a" 2)
    ("b" 1))
   ("a = [1,2]
  b = (3,4)
  x,y = a
  xx,yy = b"
    ("x" 1)
    ("y" 2)
    ("xx" 3)
    ("yy" 3))
   ("class C:
   a = [11,22,33]
  x,y,z = C.a"
    ("x" 11)
    ("y" 22)
    ("z" 33))
   ("a = 1
  b = 2
  c = 3
  d = a,b,c"
    ("d" [1 2 3]))
  
   "a,b = a.e.e()"
  
   "a[1:4], b[2], a.c = c"
  
   "a = b = c"
   "a = b = c.e"
   "a = b = c.e()"
   ("class x:
   a = 1
  xx = x()
  l = [1,2,3]
  a = xx.a = l[1] = b = c = 9
  z = l[2] = xx.a"
    ("a" 9)
    ("b" 9)
    ("c" 9)
    ("xx.a" 9)
    ("l[1]" 9)
    ("z" 9)
    ("l[2]" 9))
    )
#+END_SRC
** attribute
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
   (def-transform attribute pyel ()
    (lambda (value attr ctx &optional line col)
      (pyel-attribute value attr ctx line col)))
  
  (defun pyel-attribute (value attr ctx &optional line col)
    (setq ctx (cond ((context-p 'force-load) 'load)
                    ((context-p 'force-store) 'store)
                    (t (eval ctx))))
    (let ((t-value (remove-context aug-assign
                                   (remove-context method-call
                                                   (transform value))))
          (attr (read (_to- (transform attr))))
          ret obj-type obj-name)
  
      ;; ;;create slot for this attribute if it does not already exist
      ;; (when (and (context-p 'method-def)
      ;;            (not (assoc attr class-def-slots)))
      ;;   (push `(,attr :initarg ,(intern (concat ":"
      ;;                                           (symbol-name attr)))
      ;;                 :initform nil)
  
      ;;         class-def-slots))
      (if (context-p 'method-call)
          ;;ctx?
          (list 'call-method t-value attr)
  
        ;; (if (context-p 'assign-target)
        ;;          (setq assign-func 'oset))
  
        ;;check the presumption:
        (when (and (eq ctx 'store)
                   (not (context-p 'assign-target))
                   (not (context-p 'aug-assign))
                   nil);;for this to work, this function need to set context as well
          (error "`pyel-attribute': Presumption failed: ctx==store but not in assign context"))
        (when (and (eq ctx 'store)
                   (not (boundp 'assign-value))
                   (not (context-p 'aug-assign)))
          (error "`pyel-attribute': Presumption failed: ctx==store but assign-value is unbound"))
  
        (cond
         ((context-p 'aug-assign)
          (setq rhs (using-context 'return-type?
                                   (transform aug-assign-value))
                return-type (pyel-force-list return-type)
                known-types (list return-type return-type)
                ;;^assume that both types are the same
                ;;TODO: is this really a valid assumption?
                assign-val (call-transform-no-trans aug-assign-op
                                                    aug-assign-lhs
                                                    rhs))
          (list 'py-attr-aug-assign t-value attr assign-val))
  
         ((eq ctx 'store)
          (using-context 'return-type?
                         (setq ret (list 'setattr t-value attr
                                         (transform assign-value))))
                                          ;(setq return-type nil)
  
          (if (setq obj-type (pyel-env-get t-value type-env))
              (if (pyel-is-object-type obj-type)
                  (pyel-type-set-attr obj-type attr return-type)
                ;;TODO:? make a new object?
                (pyel-env-set t-value nil type-env)))
          
          ;;(list 'oset t-value attr (transform assign-value)) ;;assign target
          ret)
  
         ((eq ctx 'load) ;;assign value
          ;;(list 'oref t-value attr)
  
          (setq return-type
                (if (and (setq obj-type (pyel-env-get t-value type-env))
                         (pyel-is-object-type obj-type))
                    (pyel-type-get-attr obj-type attr)))
  
          (list 'getattr t-value attr))
         (t (error "Error in attribute-- invalid ctx")))
        )))
  
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Attribute(value, attr, ctx, lineno='nil', col_offset='nil'):
      return l_str(["attribute ", value, "\"" + attr + "\"", ctx, lineno, col_offset])
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   attribute
   ("class a:
   b = 3
  
  class b:
   x = a()
  
  class c:
   y =  b()
  o = b()
  o2 = c()"
    ("a.b" 3)
    ("b.x.b" 3)
    ("o.x.b" 3)
    ("o2.y.x.b" 3)
    ("c.y.x.b" 3)
    ("a.b = 2"
     ("a.b" 2))
    ("b.x.y = 2"
     ("b.x.y" 2))
    ("b.v = 4
  o2.y.v = b.v"
     ("o2.y.v" 4))
    ("o2.f = lambda x: x+1"
     ("o2.f(3)" 4))))
#+END_SRC
** num
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform num pyel ()
    (lambda (n &optional line col)
      (if (context-p 'return-type?)
          (setq return-type 'number))
      n))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Num(n, lineno='nil', col_offset='nil'):
      return "(num " + str(n) +" " + str(lineno) + " " + str(col_offset) + ")"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   num
   ("3" 3)
   ("4.23" 4.23)
   ("3e2" 300.0))
#+END_SRC
** name
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform name pyel ()
    (lambda (id ctx &optional line col)
      (pyel-name id ctx line col)))
  
  (defun pyel-name (id ctx &optional line col)
    (let ((new-id)
          (id (read id))
          (none '(_))
          piece code
          value orig-func-type assign-val rhs)
  
      ;;TODO: id should be string. verify?
      (setq ctx (cond ((context-p 'force-load) 'load)
                      ((context-p 'force-store) 'store)
                      (t (eval ctx))))
  
      (if (assoc id pyel-marked-ast-pieces)
          ;;this id is a marker, insert the corresponding macro
          (progn
            (setq piece (assoc id pyel-marked-ast-pieces))
            ;;'piece' has form: (marker macro-name macro-body)
            (using-context 'macro-call
                           (list (second piece)
                                 (pyel-transform-ast (third piece) :nosplice))))
  
        ;;else: normal name
        (setq id (_to- id))
        (when (and (context-p 'assign-value) ;;checking assumption
                   (not (equal ctx 'load)))
          (error (format "In transform name: context is 'assign-value' but ctx is not 'load'.
            ctx = %s" ctx)))
        (when (and (not (context-p 'function-call))
                   (not (context-p 'get-annotation))
                   (setq new-id (assoc id pyel-variable-name-translations)))
          (setq id (cadr new-id)))
  
        (when (and (eq ctx 'store)
                   (context-p 'function-def)
                   (context-p 'assign-target))
          (add-to-list 'let-arglist id))
  
        (cond
         ((context-p 'aug-assign)
          (setq return-type nil
                rhs (using-context 'return-type?
                                   (remove-context aug-assign
                                                   (transform aug-assign-value)))
                return-type (pyel-force-list return-type)
                known-types (list return-type return-type)
                ;;^assume that both types are the same
                ;;TODO: is this really a valid assumption?
                assign-val (call-transform-no-trans aug-assign-op id rhs)
                type (pyel-env-get id type-env)
                type (if (pyel-is-object-type type)
                         '(object)
                       type)
                known-types (list (pyel-force-list type) none none none))
  
          ;;the aug-assign vaurs are set in `pyel-aug-assign'
          (call-transform-no-trans 'augmented-assign-name
                                   id
                                   aug-assign-op
                                   rhs
                                   assign-val))
  
         ((eq ctx 'load)
          (setq return-type (pyel-env-get id type-env))
          id)
         ((eq ctx 'store)  (if (context-p 'for-loop-target)
                               id
                             (setq value (using-context 'return-type?
                                                        (transform assign-value))
                                   return-type
                                   (if (pyel-is-func-type return-type)
                                       (progn
                                         (setq orig-func-type return-type)
                                         (pyel-func-func-type return-type))
                                     return-type)
                                   known-types (list none (list return-type)))
                             ;;if the assign value is a function then the
                             ;;resulting type of the target is a vfunc
                             (if orig-func-type
                                 (setq return-type
                                       (pyel-change-to-vfunc-type orig-func-type)))
                             
                             (if (and (context-p 'class-def)
                                      (not (context-p 'method-def)))
                                 ;;class variable
                                 ;;`current-class-type' is set in `pyel-defclass'
                                 (pyel-type-set-attr current-class-type
                                                     id
                                                     return-type)
                               ;;else: normal variable
                               (pyel-env-set id return-type type-env))
                             (call-transform-no-trans 'set id value)))
         (t (error "name transform: invalid context"))))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Name(id, ctx, lineno='nil', col_offset='nil'):
      return l_str(["name ", "\"" + id + "\"", ctx, lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (ert-deftest pyel-test-name ()
   (should (eq (pyel "testName") 'testName)))
#+END_SRC
** list
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform list pyel ()
    (lambda (elts ctx &optional line col)
      (pyel-list elts ctx line col)))
  
  (defun pyel-list (elts ctx &optional line col)  ;;IGNORING CTX
    (let ((transformed (mapcar 'transform elts)))
      (if (context-p 'return-type?)
          (setq return-type 'list))
      (if (context-p 'macro-call)
          transformed
        (cons 'list transformed))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def List(elts, ctx, lineno='nil', col_offset='nil'):
      return "(list " + ll_str(elts) + " " + ctx + " " + str(lineno) + " " +str(col_offset) +")"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   list
   ("[]" nil)
   ("['a',1,2]" '("a" 1 2))
   ("a = [1,2,'b']
  b = [1,[1,'3',a,[],3]]"
    ("a" '(1 2 "b"))
    ("b" '(1 (1 "3" (1 2 "b") nil 3))))
   ("[[[1]]]" '(((1)))))
#+END_SRC
** dict
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (defvar pyel-dict-test 'equal "Test function for dictionaries")
  
  (def-transform dict pyel ()
    (lambda (keys values &optional line col)
      (pyel-dict keys values line col)))
  
  (defun pyel-dict (keys values line col) ;;TODO: move to lambda in template and create template vars
    (let ((ret (cons 'py-dict (mapcar* (lambda (key value)
                                         (list key value))
                                       (mapcar 'transform  keys)
                                       (mapcar 'transform  values)))))
      (if (context-p 'return-type?)
          (setq return-type 'hash))
      ret))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Dict(keys, values, lineno='nil', col_offset='nil'):
      return "(dict " + ll_str(keys) + " " + ll_str(values) + " " + str(lineno) + " " + str(col_offset) + ")"
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-dict (&rest pairs)
    (if pairs
        `(let ((__h__ (make-hash-table :test ',pyel-dict-test))) ;;default length??
           ,@(mapcar (lambda (pair)
                       `(puthash ,(car pair) ,(cadr pair) __h__))
                     pairs)
           __h__)
      `(make-hash-table :test ',pyel-dict-test)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   dict
   ("a = {'a':2, 'b':4}
  b = {2:a, 'b':4}
  c = {'a':2, 'b':4, 'c' : {'d' : 1,'e': 2,'f':{'g':3}}}
  d = {}"
    ("repr(a)" "{\"a\": 2, \"b\": 4}")
    ("repr(b)" "{2: {\"a\": 2, \"b\": 4}, \"b\": 4}")
    ("repr(c)" "{\"a\": 2, \"b\": 4, \"c\": {\"d\": 1, \"e\": 2, \"f\": {\"g\": 3}}}")
    ("repr(d)" "{}")
  
    ("repr(c['c'])" "{\"d\": 1, \"e\": 2, \"f\": {\"g\": 3}}")
    ("repr(c['c']['f'])" "{\"g\": 3}")
    ("c['c']['f']['g']" 3)))
#+END_SRC

** tuple
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform tuple pyel ()
    (lambda (elts ctx &optional line col) ;;Ignoring ctx for now
      (let ((transformed (mapcar 'transform elts)))
        (if (context-p 'return-type?)
            (setq return-type 'vector))
        (cons 'vector transformed))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Tuple(elts, ctx, lineno='nil', col_offset='nil'):
      return l_str(["tuple ", ll_str(elts), ctx, lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   Tuple
   ("()" [])
   ("a = 1
  b = 3
  c = 4"
    ("(a, 4)" [1 4])
  
    ("(a, (b, (c,4)))" [1 [3 [4 4]]])
    ("((((((((a))))))))" 'a)
    ("(a,)" [1])))
#+END_SRC
** string
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform str pyel ()
    (lambda (s &optional line col)
      ;;    (format "\"%s\"" s)
      (if (context-p 'return-type?)
          (setq return-type 'string))
      s))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Str(s, lineno='nil', col_offset='nil'):
      return "(str \"" + str(s) +"\"" + " " + str(lineno) + " " + str(col_offset) + ")"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests 
   string
   ("'a'"  "a")
   ("x = 'a'"
    ("x" "a"))
   ("['a']" '("a")))
#+END_SRC
** comparisons: >, <, ==, !=, <=, >=, is
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform compare pyel ()
    (lambda (left ops comparators &optional line col)
      ;;what if comparators has multiple items?
      (pyel-compare left ops comparators :outer line col)))
  
  ;;TODO: assign comparators to temp variables to prevent repeated evaluation
  (defun pyel-compare (left ops comparators &optional outer line col)
    ;;if outer is non-nil, then we use 'and' to combine the seporate tests
    (if (> (length ops) 1)
        (list (if outer 'and '@)
              (pyel-compare left (list (car ops)) (list (car comparators)))
              (pyel-compare (car comparators) (cdr ops) (cdr comparators)))
      
      (call-transform (read (car ops)) left (car comparators))))
  
  (pyel-dispatch-func == (l r)
                      (number number) -> (= l r)
                      (string string) -> (string= l r)
                      ;;                       (object _) -> (--eq-- l r)
                      
                      (_ _) -> (equal l r))
  
  (pyel-dispatch-func > (l r)
                      (number number) -> (> l r)
                      ;;TODO: macro for this
                      (string string) -> (pyel-string> l r)
                      (list list) -> (pyel-list-> l r)
                      (object _) -> (call-method l --gt-- r)
                      (vector vector) -> (pyel-vector-> l r)
                      )
  
  ;;TODO: other py types?
  
  ;;::Q does `string<' behave like < for strings in python?
  (pyel-dispatch-func < (l r)
                      (number number) -> (< l r)
                      (string string) -> (string< l r)
                      (list list) -> (pyel-list-< l r)
                      (object _) -> (call-method l --lt-- r)
                      (vector vector) -> (pyel-vector-< l r))
  
  (pyel-dispatch-func >= (l r)
                      (number number) -> (>= l r)
                      (string string) -> (pyel-string>= l r)
                      (list list)     ->  (pyel-list>= l r)
                      (object _) -> (call-method l --ge-- r)
                      (vector vector) -> (pyel-vector->= l r))
  
  (pyel-dispatch-func <= (l r)
                      (number number) -> (<= l r)
                      (string string) -> (pyel-string<= l r)
                      (list list)     -> (pyel-list<= l r)
                      (object _) -> (call-method l --le-- r)
                      (vector vector) -> (pyel-vector-<= l r))
  
  (pyel-dispatch-func != (l r)
                      (number number) -> (pyel-number!= l r)
                      (string string) -> (pyel-string!= l r)
                      (object _) -> (call-method l --ne-- r)
                      (_ _) -> (!equal l r))
  
  ;;this is defined as a transform because `pyel-compare' expects
  ;;all comparison functions to be transforms
  (pyel-dispatch-func is (l r)
                      (_ _) -> (eq l r))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Compare (left, ops, comparators, lineno='nil', col_offset='nil'):
        return l_str(["compare ", left, ll_str(ops), ll_str(comparators), lineno, col_offset ])

  def Gt():
        return "\">\""
  def Lt():
        return "\"<\""
  def Eq():
        return "\"==\""
  def NotEq ():
        return "\"!=\""
  def LtE():
        return "\"<=\""
  def GtE():
        return "\">=\""
  def In():
        return "\"in\""
  def NotIn():
        return "\"not-in\""
  def Is():
        return "\"is\""
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst pyel-string> (a b)
    (and (not (string< a b)) (not (string= a b))))
  
  (defsubst pyel-string<= (a b)
    (or (string< a b) (string= a b)))
  
  (defsubst pyel-string!= (a b)
    (not (string= a b)))
  
  (defsubst pyel-number!= (a b)
    (not (= a b)))
  
  (defun pyel-list-< (a b)
    "a < b"
    (let ((greator nil)
          e1 e2)
      (while (and a b (equal e1 e2))
        (setq e1 (car a)
              e2 (car b)
              a (cdr a)
              b (cdr b)))
      (pyel-< e1 e2)))
  (defun pyel-list-> (a b)
    "a > b"
    (let ((greator nil)
          e1 e2)
      (while (and a b (equal e1 e2))
        (setq e1 (car a)
              e2 (car b)
              a (cdr a)
              b (cdr b)))
      (pyel-> e1 e2)))
  
  (defsubst pyel-list>= (a b)
    (or (equal a b) (pyel-list-> a b)))
  
  (defsubst pyel-list<= (a b)
    (or (equal a b) (pyel-list-< a b)))
  
  (defun pyel-vector-> (a b)
    (let* ((greator nil)
           (len-a (length a))
           (len-b (length b))
           (len (min len-a len-b))
           (i 0)
           e1 e2)
      (while (and (< i len)
                  (equal e1 e2))
        (setq e1 (aref a i)
              e2 (aref b i)
              i (1+ i)))
      (or (pyel-> e1 e2)
          (and (= i len)
               (equal e1 e2)
               (> len-a len-b)))))
  (defun pyel-vector-< (a b)
    (let* ((greator nil)
           (len-a (length a))
           (len-b (length b))
           (len (min len-a len-b))
           (i 0)
           e1 e2)
      (while (and (< i len)
                  (equal e1 e2))
        (setq e1 (aref a i)
              e2 (aref b i)
              i (1+ i)))
      (or (pyel-< e1 e2)
          (and (= i len)
               (equal e1 e2)
               (< len-a len-b)))))
  
  (defsubst pyel-vector-<= (a b)
    (or (equal a b) (pyel-vector-< a b)))
  
  (defsubst pyel-vector->= (a b)
    (or (equal a b) (pyel-vector-> a b)))
  
  (defsubst !equal (a b)
    (not (equal a b)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   compare
   ("1 == 1" t)
   ("2 == 3" nil)
   ("a = 'a'
  b = 'b'"
    ("a == b" nil))
   ("[1,3] == [1,3]" t)
   ("[1,3] == [1,3,2]" nil)
   ("(1,2) == (1,2)" t)
   ("2 > 1" t)
   ("'a' > 'b'" nil)
   ("'z' > 'a'" t)
   ("['a'][0] >= ['z','a'][1]" t)
   ("'aaax' > 'aaaa'" t)
   ("'aaaa' > 'aaaa'" nil)
   ("'aaaa' >= 'aaaa'" t)
   ("1 != 1" nil)
   ("1 != 2" t)
   ("a = 'a'
  b = 'b'"
    ("a != b" t))
   ("['a'][0] != ['z','a'][1]" nil)
   ("(3,4) == [3,4]" nil)
   ("[9 == 1]" '(nil))
   ("((1 == 1),)" [t])
   ("1<3<8" t)
   ("def a(): return 'a'
  b = 'b'"
    ("a()<=b<'t'<='zaaaz'" t))
   ("[1] is [1]" nil)
   ("a = [1]
  b = a"
    ("a is b" t))
   ("1 is 1" t))
#+END_SRC
** if
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform if pyel ()
    (lambda (test body orelse &optional line col)
      (pyel-if test body orelse line col)))
  
  (defun pyel-if (test body orelse &optional line col)
    (let* ((tst (transform test))
           true-type false-type trans
           (true-body
            (if (> (length body) 1)
                (append (remove-context
                         ;;(tail-context return-type)
                         tail-context
                         (mapcar 'transform
                                 (or (subseq body 0 -1)
                                     (list (car body)))))
                        (using-context
                         'return-type?
                         (setq trans (list (transform (car (last body))))
                               true-type return-type)
                         trans))
              (using-context 'return-type?
                             (setq trans (mapcar 'transform body)
                                   true-type return-type)
                             trans)))
           (false-body
            (if (> (length orelse) 1)
                (append (remove-context
                         ;;(tail-context return-type)
                         tail-context
                         (mapcar 'transform
                                 (or (subseq orelse 0 -1)
                                     (list (car orelse)))))
                        (using-context
                         'return-type?
                         (setq trans (list (transform (car (last orelse))))
                               false-type return-type)
                         trans))
              (using-context 'return-type?
                             (setq trans (mapcar 'transform orelse)
                                   false-type return-type)
                             trans))) 
           (progn-code (if (> (length true-body) 1)
                           '(@ progn)
                         '@)))
      
      (setq return-type (if (equal true-type false-type)
                            true-type
                          (list true-type false-type)))
      `(if (py-bool ,tst)
           (,progn-code ,@true-body)
         ,@false-body)))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def If (test, body, orelse, lineno='nil', col_offset='nil'):
      return l_str(["if ",test, ll_str(body), ll_str(orelse), lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   if
   ("if True:
   x = 1
  else:
   x = 2"
    ("x" 1))
   ("if False:
   x = 3
  else:
   x = 4"
    ("x" 4))
   ("if len([1,2,3]) == 3:
   y = 1
   x = 5
  else:
   y = 1
   x = 6"
    ("x" 5))
  
   ("def a():
   if True:
    return 1
   else:
    return 2
  
  def b():
   if True:
    return 1
    x()
   else:
    y()
    return 2
  
  def c():
   if 1 == 2:
    return 1
    x()
   else:
    x = b() + 1
    return 2
  
  def d():
   if 1 == 2:
    return 4
   else:
    if True:
     return 1.1
     x()
    return 2
  
  def e():
   if 1 == 1:
    if True:
     if [0][0] == 0:
      return 12
     x()
   else:
    y()
    return 2"
    ("a()" 1)
    ("b()" 1)
    ("c()" 2)
    ("d()" 1.1)
    ("e()" 12))
  
   ("def test():
   if 1 == 3:
    return 12
   elif 3 == 5:
    return 123
   elif 2 == 2:
    return 234
   else:
    return 33"
    ("test()" 234))
   
   ("def test():
   if False:
    return 2
    c()
   else:
    return 5
    e()"
    ("test()" 5)))
  
#+END_SRC
** call
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (defvar pyel-obj-counter 0)
  
  (defun pyel-next-obj-name ()
    (if pyel-unique-obj-names
        (format "obj-%d" (setq pyel-obj-counter (1+ pyel-obj-counter)))
      "obj"))
  
  ;; (pyel-dispatch-func fcall (_func &rest args)
  ;;                      ($func _) -> ($func ,@args)
  ;;                      (_ _) -> (funcall $func ,@args))
  
  ;; functions set to variables override those defined with `defun'
  ;; this allows locally defined functions to override their global
  ;; counterparts without defining themselves globally
  (pyel-dispatch-func fcall (_func &rest _args)
                      ($list _) 
                      (vfunc _) -> (funcall $func ,@(pyel-sort-kwargs args))
                      ;;($function _) -> ($func ,@(pyel-sort-kwargs args))
                      (class _) -> (call-method $func --new--
                                                ,@(pyel-sort-kwargs args))
  
                      (instance _) -> (call-method $func --call--
                                                   ,@(pyel-sort-kwargs args))
                      (_ _) -> ($func ,@(pyel-sort-kwargs args))
                      )
  
  (def-transform keyword pyel ()
    (lambda (arg value &optional line col)
      (if (context-p 'keywords-alist)
          (list (_to- arg) (transform value))
        (list '@ (_to- arg)  '= (transform value)))))
  
  (def-transform call pyel ()
    ;;TODO: some cases funcall will need to be used, how to handle that?
    (lambda (func args keywords starargs kwargs &optional line col)
      (pyel-call-transform func args keywords starargs kwargs line col)))
  
  (defun pyel-call-transform (func args keywords starargs kwargs &optional line col)
    (let* ((is-method-call (eq (car func) 'attribute))
           ;;TODO: prevent repeated transformation when `is-method-call' is true
           (t-func (using-context 'return-type?
                                  (using-context 'function-call
                                                 (transform func))))
           new-func
           (name t-func);;original name before (possible) translation
           ;;check if this function name needs to be translated         
           (t-func (if (and (not is-method-call)
                            (setq new-func (assoc t-func
                                                  pyel-function-name-translations)))
                       (cadr new-func)
                     t-func))
            
           (function-type (cond ((pyel-is-func-type return-type)
                                 (pyel-func-func-type return-type))
                                ((pyel-is-class-type return-type)
                                 '(class))
                                ((pyel-is-instance-type return-type)
                                 '(instance))))
          (function-type (if (listp function-type)
                             function-type
                           (list function-type)))
          (this-return-type (if (pyel-is-func-type return-type)
                                (pyel-func-return-type return-type)))
          
          (keyword-args (using-context 'keywords-alist
                                       (mapcar (lambda (x) (transform (car x)))
                                               keywords)))
          (t-args-quoted (mapcar (lambda (x)
                                   (list 'quote (transform x)))
                                 args))
          (known-types known-types)
          new-func m-name f-name star-args kw-args ret this-return)
  
      ;; (if (member t-func pyel-defined-classes)
      ;;     ;;instantiate an object and call its initializer
      ;;     `(let ((__c (,t-func ,(pyel-next-obj-name))))
      ;;        (--init-- __c ,@(mapcar 'transform args))
      ;;        __c)
  
      (if (eq t-func pyel-type-declaration-function2)
          ;;declare variable type, return nothing
          (progn
            (assert (= (length args) 2) "type declaration function requires 2 args")
            (pyel-env-set (transform (car args))
                          (using-context 'get-annotation
                                         (transform (cadr args)))
                          type-env)
            pyel-nothing)
        ;;else: normal function/method call
        (setq known-types
              (mapcar (lambda (arg)
                        (setq type (if (symbolp arg)
                                       (pyel-env-get arg type-env)
                                     (using-context 'return-type?
                                                    (transform arg))
                                     return-type))
                        (cond ((pyel-is-func-type type)
                               (setq type (pyel-func-return-type type)))
                              ((pyel-is-class-type type)
                               (setq type '(class)))
                              ((pyel-is-instance-type type)
                               (setq type '(instance)))
                              ((null type)
                               ;;if type is not known, it could be anything
                               (setq type pyel-possible-types)))
                        ;;type for each arg must be a list
                        (if (listp type)
                            type
                          (list type)))
                      args))
  
        (setq return-type this-return-type)
        (if is-method-call 
            (cond ((and (member (setq m-name (read (caddr func)))
                                pyel-method-transforms2)
                        (setq m-name (pyel-find-method-transform-name
                                      m-name
                                      (+ (length args) (length keyword-args) 1))))
                        
                 ;;transform defined with `pyel-define-method-translation'
                 (setq ret (eval `(call-transform-no-trans
                                   ',m-name
                                   ',(transform (cadr func))
                                   ,t-args-quoted
                                   keyword-args))
                       return-type this-return-type))
  
                   ((and (member m-name pyel-method-kwarg-transforms)
                        keyword-args
                        (setq m-name (pyel-find-method-transform-name
                                      m-name
                                      (+ (length args) (length keyword-args) 1)
                                      :kwargs))
                        )
                   ;;transform defined with `pyel-method-kwarg-transform'
                   (setq ;;keyword-args keywords
                         ;;star-args starargs
                         ;;kw-args kwargs
                         ret (eval `(call-transform-no-trans
                                     ',m-name
                                     ',(transform (cadr func))
                                     ',keyword-args
                                     ,@t-args-quoted))
                         return-type this-return-type)
                   ret)
  
                  ((and (member m-name pyel-method-transforms)
                        (setq m-name (pyel-find-method-transform-name
                                      m-name
                                      (1+ (length args)))));;1+ because args does not include the object
                   ;;transform defined with `pyel-method-transform'
                   (setq known-types (cons function-type known-types))
                   ;;dynamic scoping saves the day again!
                   (setq keyword-args keywords
                         star-args starargs
                         kw-args kwargs
                         ret (eval `(call-transform-no-trans
                                     ',m-name
                                     ',(transform (cadr func))
                                     ,@t-args-quoted))
                         return-type this-return-type)
                   ret)
              ;;normal method call
              (t (setq ret (append (using-context 'method-call (transform func))
                                (mapcar 'transform args))
                    return-type this-return-type)
              ret))
  
          ;;call function transform if one was defined
          (cond ((and keyword-args
                      (member t-func pyel-func-kwarg-transforms))
                 ;;transform defined with `pyel-func-kwarg-transform'
                 (setq ret (eval `(call-transform-no-trans
                                   ',(pyel-func-transform-name t-func t)
                                   ',keyword-args
                                   ,@t-args-quoted))
                       return-type this-return-type)
                 ret)
  
                ((member t-func pyel-func-transforms)
                 ;;transform defined with `pyel-func-transform'
                 (setq ret (eval `(call-transform-no-trans
                                   ',(pyel-func-transform-name t-func)
                                   ,@t-args-quoted))
                       return-type this-return-type)
                 ret)
  
                ((member t-func pyel-func-transforms2)
                 ;;transform defined with `pyel-define-function-translation'
                 (setq ret (eval `(call-transform-no-trans
                                   ',(pyel-func-transform-name t-func)
                                   ;;,(mapcar '(lambda (x) `(quote ,x)) args)
                                   (mapcar 'transform args) ;;?
                                   keyword-args))
                       return-type this-return-type))
  
                ;;normal function call
                ;;`(,t-func ,@(mapcar 'transform args))
                ;;TODO: this is dumb, convert `call-transform' to a macro?
                (t (if (context-p 'return-type?)
                       (setq this-return (pyel-env-get name type-env)
                             this-return (cond ((pyel-is-func-type this-return)
                                                (pyel-func-return-type this-return))
                                               ((pyel-is-class-type this-return)
                                                (pyel-make-instance-type
                                                 this-return type-env))
                                               (t this-return))))
                   (setq known-types (cons function-type known-types)
                         _known-types known-types
                         ret (eval `(call-transform-no-trans
                                     'fcall ,@(cons 't-func
                                                    (append
                                                     t-args-quoted
                                                     (mapcar (lambda (x)
                                                               (list 'quote
                                                                     (transform x)))
                                                             (mapcar 'car
                                                                     keywords))))))
                         return-type this-return)
                   ret))))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Call(func, args, keywords, starargs, kwargs, lineno='nil', col_offset='nil'):
      return l_str(["call ", func, ll_str(args), ll_str(keywords), starargs or "nil" , kwargs or "nil", lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   call
   ("def a(): return 4"
    ("a()" 4)
    ("x = a()"
     ("x" 4)))
   ("def f():
   return lambda: 3"
    ("f()()" 3))
   ("def f(a):
   def g(b):
    return a + b
   return g"
   ("f(3)(4)" 7))
   ("def f(x): return x + 1
  def g(x): return x + 2"
    ("f(g(1))" 4))
   
   )
  
#+END_SRC
** while loop
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform while pyel ()
    (lambda (test body orelse &optional line col)
      (pyel-while test body orelse line col)))
  
  ;;doc: context macro-call
  (defun pyel-while (test body orelse &optional line col)
    ;;pyel-while is special. it gets to handle the macro definitions
    (let* ((tst (transform test))
           (else (mapcar 'transform orelse))
           break-code
           continue-code
           macro-name
           t-body
           t-last
           break-code
           continue-code
           wile
           ;;inter-transform variables
           continue-while
           break-while )
  
      (if (and (symbolp tst)
               (string-match (format "^%s\\([A-Za-z0-9_]+\\)$" pyel-py-macro-prefix)
                             (symbol-name tst)))
          (using-context
           'macro-call
           ;;expand as a macro call
           `(,(intern (replace-regexp-in-string "_" "-"
                                                (match-string 1 (symbol-name tst))))
             ;;TODO: if macro name is an alias, replace with actual
             ,@(mapcar 'transform body)))
  
        ;;expand as a normal while loop
        (using-context
         'while
  
         (setq code (remove-context tail-context (mapcar 'transform body))
               ;;^ no tail calls from while loop
                ;; t-body (remove-context tail-context
                ;;                        (mapcar 'transform (subseq body 0 -1)))
                ;; t-last (transform (car (last body)))
                ;; code (append t-body (list t-last))
  
  
               break-code (if break-while '(catch '__break__)
                            pyel-nothing)
               continue-code (if continue-while '(catch '__continue__)
                               pyel-nothing)
               wile `(,@break-code
                      (while
                          ,(if (equal tst []) nil tst)
                        (,@continue-code
                         ,@code)))))
        (if else
            `(@ ,wile ,@else)
          wile))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def While(test, body, orelse, lineno='nil', col_offset='nil'):
      return l_str(["while ", test, ll_str(body), ll_str(orelse), lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests 
   while
   ("x = 1
  a = 0
  while x < 10:
   a += x
   x += 1"
    ("a" 45))
  
   ;;return from tail context
   ("def f():
   x = 1;
   while x < 10:
    return x
    x+=1
   return x
  
  def g():
   x = 1;
   while x < 10:
    x+=1
    return x
   return x"
    ("f()" 1)
    ("g()" 2))
  
   ;;break
   ("x = 1
  while x < 10:
   if x == 3:
    break
   x+=1"
    ("x" 3))
  
   ;;continue
   ("x = 0
  a=0
  while x < 10:
   x+=1
   if x%2 == 0:
    continue
   a+=1"
    ("a" 5)))
#+END_SRC
** arguments
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform arguments pyel ()
    (lambda (args vararg varargannotation kwonlyargs kwarg kwargannotation
                  defaults kw_defaults)
      (pyel-arguments args vararg varargannotation kwonlyargs kwarg kwargannotation
                      defaults kw_defaults)))
  
  (defun pyel-arguments (args vararg varargannotation
                              kwonlyargs kwarg kwargannotation
                              defaults kw_defaults)
    ;;TODO: other args
  
    (let* ((_args args);;save for annotations
           (args (mapcar 'transform args))
           (positional args)
           (defaults (mapcar 'transform defaults))
           annotations)
  
      ;;&optional
      (when defaults
        (setq defaults (reverse (mapcar* (lambda (arg default)
                                           (setq arg (_to- arg))
                                           (cons arg default))
                                         (reverse args) (reverse defaults))))
        (setq args (append (subseq args 0 (- (length args) (length defaults)))
                           '(&optional)
                           defaults)))
  
      ;;&rest
      (when vararg
        (setq args (append args (list '&rest vararg)))
        (when (and (not pyel-use-list-for-varargs)
                   (context-p 'function-def))
          (push `(setq, vararg (vconcat ,vararg)) assign-defaults)))
  
      ;;&kwonly
      (when kwonlyargs
        (setq args (append args (cons '&kwonly
                                      (mapcar* 'cons
                                               (mapcar 'transform kwonlyargs)
                                               (mapcar 'transform kw_defaults))))))
      
      ;;&kwarg
      (when kwarg
        (setq args (append args (list '&kwarg kwarg))))
  
      ;;type annotations
      (when (context-p 'function-def)
        (mapcar* (lambda (arg type)
                   ;;TODO: verify correct type
                   (pyel-env-set arg type type-env)
                   (push type annotations))
                 positional
                 (using-context 'get-annotation
                                (mapcar 'transform _args)))
        ;; 'function-name', 'function-type',  and 'type-of-return' are set
        ;; by the 'return' transform.
        (setq return-type (pyel-make-func-type function-type
                                               (reverse annotations)
                                               type-of-return))
        (if function-name
            (pyel-env-set function-name 
                          return-type
                          (pyel-env-get-parent type-env))))
      args))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def arguments(args=None, vararg=None, varargannotation=None, kwonlyargs=None,
                kwarg=None, kwargannotation=None,defaults=None, kw_defaults=None):
      return ll_str(["(arguments ",
                     ll_str(args) or "nil",
                     vararg or "nil",
                     varargannotation or "nil",
                     ll_str(kwonlyargs) if kwonlyargs else "nil",
                     kwarg or "nil",
                     kwargannotation or "nil",
                     ll_str(defaults) or "nil",
                     ll_str(kw_defaults) if kw_defaults else "nil", ")"])
      #return l_str(["_arguments ",ll_str(args) or "nil",vararg or "nil",varargannotation or "nil", kwonlyargs or "nil", kwarg or "nil", kwargannotation or "nil", defaults or "nil", kw_defaults or "nil"])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (ert-deftest pyel-test-arguments ()
    (with-transform-table 'pyel
                          (and
                           (should (equal (transform '(arguments ((arg "b" nil)
                                                                  (arg "c" nil)) nil nil nil nil nil nil nil))
  
                                          '(b c)))
                           ;;other tests here
                           )))
#+END_SRC
** function def
includes 'arg' transformation
*** python ast expansion example
"def a(b,c):
  print('ok')
  return c+a"

FunctionDef(name='a',
	    args=arguments(args=[arg(arg='b', annotation=None), arg(arg='c', annotation=None)],
				vararg=None,
				varargannotation=None,
				kwonlyargs=[],
				kwarg=None,
				kwargannotation=None,
				defaults=[],
				kw_defaults=[]),

	    body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='ok')], keywords=[], starargs=None, kwargs=None)), Return(value=BinOp(left=Name(id='c', ctx=Load()), op=Add(), right=Name(id='a', ctx=Load())))],

	    decorator_list=[],
	    returns=None)

*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform arg pyel ()
    (lambda (arg annotation)
      (setq _x annotation)
      (if (context-p 'get-annotation)
          (transform annotation)
        (read arg))))
  
  (def-transform def pyel ()
    (lambda (name args body decoratorlist returns &optional line col)
      (pyel-def name args body decoratorlist returns line col)))
  
  (defun transform-last-with-context (context code)
    ;;TODO: this does not work: fix and replace code in `pyel-def
    (let*  ((last-line (using-context 'context
                                      (transform (car (last code)))))
            (first (mapcar 'transform (subseq code 0 (1- (length code))))))
      (append first (list last-line))))
  
  (defun pyel-def (name args body decoratorlist returns &optional line col)
    (let ((name (if (null name) name (read (_to- name)))))
  
      ;;when 'name' is nil, define an anonymous function
      (when (and (context-p 'function-def)
                 (not (null name)))
        (push name let-arglist)) ;;do this before the let-arglists gets overridden for this transform
  
      (let* ((inner-defun (and (context-p 'function-def)
                               (not (null name))))
             (function-name name)
             (function-type (if (or inner-defun (context-p 'lambda-def))
                                'vfunc 'func))
             (type-of-return (using-context 'get-annotation (transform returns)))
             ;;'return-type', 'function-name', and 'function-type,
             ;; are used to pass values to the 'arguments' transform
             (type-env (pyel-make-type-env type-env))
             ;;NOTE: the 'arguments' transform is responsible for setting
             ;;the type values of the parameters in this new 'type-env'
             ;;The 'arguments' transform also maps the function name to
             ;;its argument and parameter types in the parent of this 'type-env'
             
             (func 'def)
             t-body
             arglist
             first
             last-line
  
             ;;trans-template vars
             (assign-defaults (list pyel-nothing));;holds assignment code set by the arguments transform
             return-middle
             let-arglist
             global-vars
             docstring
             interactive
  
                ;;;
             (ret pyel-nothing)
             (args (_to- (using-context 'function-def (transform (car args)))))
             (arg-names (mapcar (lambda (x) (if (consp x) (car x) x)) args))
             (this-return return-type) ;;return-type set by the argument transform
             (orig-name name)
             (decorators (mapcar 'transform decoratorlist))
             setq-code first-arg-name
             )
  
        (setq _env type-env)
  
        (when (or (null name)
                  (context-p 'lambda-def)
                  (and inner-defun
                       (not (member '&kwarg args))))
  
          (setq decorators (cons 'pyel-lambda decorators)
                name nil))
  
        (when (context-p 'class-def)
          ;;this is a method. set a reference to the name of the first arg
          ;;This is used by the type declaration code
          (setq first-arg-name (car args))
          ;;map the first arg to its class type. 
          ;;`class-name' is set by the class transform
          (pyel-env-set first-arg-name (pyel-env-get class-name type-env) type-env)
          (setq decorators (cons 'pyel-lambda decorators)))
  
        (using-context
         (if (context-p 'class-def)
             '(function-def method-def)
           'function-def)
         (cond
  
          ;; ((context-p 'class-def) (using-context 'method-def
          ;;                           (setq last-line (using-context 'tail-context
          ;;                                                          (transform (car (last body))))
          ;;                                 first (mapcar 'transform (subseq body 0 (1- (length body))))
          ;;                                 t-body (append first (list last-line)))
  
          ;;                           ;;TODO: let-arlist for methods like
          ;;                           ;;      and *args ...
          ;;                           (push `(defmethod ,name
          ;;                                    ((,(car args) ,class-def-name)
          ;;                                     ,@(cdr args))
  
          ;;                                    ,@t-body)
          ;;                                 class-def-methods)))
  
          (t (setq first (subseq body 0 (1- (length body)))
                   first (if first
                             (mapcar 'transform first)
                           nil)
                   last-line (using-context 'tail-context
                                            (transform (car (last body))))
                   t-body (append first (list last-line)))
  
             ;;(setq t-body (transform-last-with-context
             ;;                'tail-context body))
  
             ;;remove variables from the let arglist that have been declared global
             (setq let-arglist (let (arglist)
                                 (mapcar (lambda (x)
                                           (unless (or (member x global-vars)
                                                       (member x arg-names))
                                             (push x arglist)))
                                         let-arglist)
                                    arglist))
             ;;      ?remove variables that are defined in emacs?
  
             (setq docstring
                   (if (stringp (car t-body))
                       (pop t-body)
                     pyel-nothing))
  
             (setq ret (if return-middle '(catch '__return__)
                         '(@)))
  
             (if let-arglist
                 (setq let-arglist (list '@ 'let let-arglist))
               (setq let-arglist '@))
  
             (if inner-defun
                 (progn
                   (if (and (member '&kwarg args)
                            (not (member 'pyel-lambda decorators)))
                       (setq decorators
                             (cons 'pyel-lambda decorators)))
                   (setq setq-code (list '@ 'setq orig-name)))
               (setq setq-code '@))
  
             (if interactive
                 (push 'interactive decorators))
  
             (setq pyel-last-func-type-env type-env)
             (setq return-type this-return)
             `(,setq-code (,func ,name ,args ,decorators
                                 ,docstring
                                 ,@assign-defaults
                                 (,let-arglist
                                  (,@ret
                                   ,@t-body
                                   ))))))))))
  
  (defun pyel-sort-kwargs (args)
    "Organize args in the format required by functions that accept keyword args
  Returns a list whose first element has an alist containing the keyword args
  in the cdr and ':kwargs' in the car position.
  and whose second element is a list of all non-keyword args
  Recognizes keyword args in the form 'arg = value'."
    ;;for use in the 'fcall' transform
    (if (member '= args)
        (let ((current args)
              kwargs
              normal)
          (when (eq (car current) '=) (error "invalid keyword arg syntax"))
          (while current
            (if (eq (car current) '=)
                (progn
                  (when (null (cdr current)) (error "invalid keyword arg syntax"))
                  (push (cons (pop normal) (cadr current)) kwargs)
                  (setq current (cddr current)))
              (push (car current) normal)
              (setq current (cdr current))))
          (list (list 'quote (cons :kwargs kwargs)) (list 'quote (reverse normal))))
      args))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-alist-to-hash (alist)
    "Turn ALIST a hash table.
  Each element in ALIST must have for form (a . b)"
    (let ((ht (make-hash-table
               :test 'equal
               :size (length alist))))
      (mapc (lambda (x)
              (puthash (car x) (cdr x) ht))
            alist)
      ht))
  
  (defmacro def (name args decorator-list &rest body)
    ;;TODO: apply decorators
  
    (if (and (= (length body) 1)
             (stringp (car body)))
        (push nil body))
  
    (using-context
     'function-def
     ;; (if (or (member '&kwarg args)
     ;;         (member '&kwonly args))
     (let* ((n -1)
            (func-name (if (member 'pyel-lambda decorator-list)
                           (progn
                             (setq decorator-list
                                   (remove 'pyel-lambda
                                           decorator-list))
                             '(lambda))
                         (list 'defun name)))
            (interactive (when (member 'interactive decorator-list)
                           (setq decorator-list
                                 (remove 'interactive
                                         decorator-list))
                           t))
           optional optional-defaults
           pos+optional rest kwarg kwonly
           npositional nargs arg-index
           kw-only-args kw-only-defaults)
  
       (when (member '&kwarg args)
         (setq kwarg (car (last args))
               args (subseq args 0 -2)
               args-without-kwarg args))
       (when (member '&kwonly args)
         (setq kwonly (pyel-split-list args '&kwonly)
               args (car kwonly)
               kwonly (cdr kwonly)
               kw-only-args (mapcar 'car kwonly)
               kw-only-defaults (mapcar 'cdr kwonly)
               args-without-kwarg args))
       (when (member '&rest args)
         (setq rest (car (last args))
               args (subseq args 0 -2)))
       (if (member '&optional args)
           (setq optional (pyel-split-list args '&optional)
                 positional (car optional)
                 optional (cdr optional)
                 optional-defaults (mapcar 'cdr optional)
                 optional (mapcar 'car optional)
                 )
         (setq positional args))
  
       (setq npositional (length positional)
             nargs (+ (length positional) (length optional))
             arg-index-alist (mapcar (lambda (x)
                                       (setq n (1+ n))
                                       (cons x n))
                                     (append positional optional)))
  
       `(,@func-name (&rest __pyel_args)
                     ,@(if interactive '((interactive)) nil)
                     (let* (,@(if rest (list rest) nil)
                            ,@(if kwarg (list kwarg) nil)
                            ,@positional
                            ,@optional
                            ,@kw-only-args)
                       
                       (if (and (listp (car __pyel_args))
                                (eq (caar __pyel_args) :kwargs))
                           ;;if this is called with keyword args they will be
                           ;;the in an alist in the car position.                         
                           (let*  ((__pyel_kwargs (cdar __pyel_args))
                                   (__pyel_args (cadr __pyel_args))
                                   (__pyel_len (length __pyel_args))
                                   (__pyel_kwarglen (length __pyel_kwargs))
                                   (__pyel_kwargs-used 0)
                                   (__pyel_values_provided (make-vector ,nargs nil))
                                   __pyel_error)
                             
                             ;;set *rest arg
                             ,(if rest
                                  `(if (> __pyel_len ,nargs)
                                       (setq ,rest (subseq __pyel_args ,nargs))))
                             
                             ;;set positional arg values
                             (setq __pyel_tmp __pyel_args)
                             
                             ,(if positional
                                   `(setq 
                                     ,@(reduce 'append (mapcar (lambda (arg)
                                                                `(,arg (car __pyel_tmp)
                                                                       __pyel_tmp (cdr __pyel_tmp)))
                                                              positional))))
                             ,(if optional
                                   `(setq 
                                     ,@(reduce 'append (mapcar* (lambda (arg default)
                                                                 `(,arg (or (car __pyel_tmp) ,default)
                                                                        __pyel_tmp (cdr __pyel_tmp)))
                                                               optional optional-defaults))))
  
                             ;;set positional and optional arg values that are provided as keywords
                             ;;(optional arg default values are set elsewhere)
                             ,@(let* ((i 0)
                                      (args (append positional optional))
                                      code arg)
                                 (while args
                                   (setq arg (car args)
                                         args (cdr args)
                                         code `((if (= __pyel_kwarglen 0)
                                                    nil
                                                  (if (setq __pyel_val (assoc (quote ,arg) __pyel_kwargs))
                                                      (if (or (<= ,(setq i (1+ i)) __pyel_len)
                                                              (aref __pyel_values_provided ,(1- i)))
                                                          ;;the value for this arg has already been provided
                                                          (setq __pyel_error (quote ,arg))
                                                        ;;else:
                                                        (aset __pyel_values_provided ,(1- i) t)
                                                        (setq ,arg (cdr __pyel_val)
                                                              __pyel_kwargs (remove __pyel_val __pyel_kwargs)
                                                              __pyel_kwargs-used (1+ __pyel_kwargs-used)
                                                              __pyel_kwarglen (1- __pyel_kwarglen))))
                                                  ,@code))))
                                 code)
  
  
                             (if __pyel_error
                                 (signal 'TypeError
                                         (format ,(format "%s() got multiple values for keyword argument '%%s'"
                                                          name)
                                                 __pyel_error)))
  
                             ;;set kwonly arg values
                             ,@(mapcar* (lambda (arg default)
                                          `(if (setq __pyel_val (assoc (quote ,arg) __pyel_kwargs))
                                               (progn
                                                 (setq ,arg (cdr __pyel_val))
                                                 (setq __pyel_kwargs (remove __pyel_val __pyel_kwargs))
                                                 )
                                             (setq ,arg ,default)))
                                        kw-only-args kw-only-defaults)
  
                             ;;verify that minimum required arguments have been passed
                             ,(if (or rest optional)
                                  `(if (< (+ __pyel_kwargs-used __pyel_len) ,npositional)
                                       (signal 'TypeError (format ,(format "%s() takes at least %s arguments (%%s given)"
                                                                           name npositional)
                                                                  (+ __pyel_kwargs-used __pyel_len))))
                                `(if (not (= (+ __pyel_kwargs-used __pyel_len) ,npositional))
                                     (signal 'TypeError (format ,(format "%s() takes exactly %s arguments (%%s given)"
                                                                         name npositional)
                                                                (+ __pyel_kwargs-used __pyel_len)))))
                             
                             ,(if kwarg
                                  `(setq ,kwarg (pyel-alist-to-hash __pyel_kwargs)))
                             ;;NOTE: dont have to worry about repeated keywords because that is a syntax error
                             )
                         
                         ;;else: called without keyword args
                         ,(if (or rest optional)
                              `(if (< (length __pyel_args) ,npositional)
                                   (signal 'TypeError (format ,(format "%s() takes at least %s arguments (%%s given)"
                                                                       name npositional)
                                                              (length __pyel_args))))
                            `(if (not (= (length __pyel_args) ,npositional))
                                 (signal 'TypeError (format ,(format "%s() takes exactly %s arguments (%%s given)"
                                                                     name npositional)
                                                            (length __pyel_args)))))
                         
                         ,@(mapcar (lambda (arg-value) ;;set kwarg defaults
                                     (list 'setq (car arg-value)
                                           (cdr arg-value)))
                                   kwonly)
  
                         ,(if (or positional optional) ;;set arg values
                              `(setq 
                                ,@(append (reduce 'append (mapcar (lambda (arg)
                                                            `(,arg (car __pyel_args)
                                                                   __pyel_args (cdr __pyel_args)))
                                                           positional))
                                          (reduce 'append (mapcar* (lambda (arg default)
                                                                    `(,arg (or (car __pyel_args) ,default)
                                                                           __pyel_args (cdr __pyel_args)))
                                                                   optional optional-defaults)))
                                          ))
                         
                         ,(if rest ;;set *rest arg
                              `(setq ,rest __pyel_args)
                            ;;TODO: verify that __pyel_args is null here (else case)
                            )
  
                         ,(if kwarg `(setq ,kwarg (make-hash-table :size 0))))
                       
                       ,@body)))))
  
  
  ;;tmp fix (default values for kwonly args are not getting translated)
  (setq None nil)
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def FunctionDef(name, args, body, decorator_list, returns, lineno='nil', col_offset='nil'):
      return " ".join(map(str, ["(def \"", name,"\"", ll_str(args), ll_str(body), ll_str(decorator_list) or "nil",returns or "nil", lineno, col_offset, ")"]))
  
  def arg(arg, annotation):
      return "(arg \"" + arg + "\" " + " " + (annotation or "nil") + ")"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   def
   ("def a():
    return 0
  
  def b(a):
   return a
  
  def c(a,b,c):
   return a+b+c
  
  def d(a):
   if True:
    return a
   return 5
   somethingelse()
  
  def e(a=1.1):
   return a
  def f(*a):
   return a
  "
    ("a()" 0)
    ("b(1)" 1)
    ("c(1,2,3)" 6)
    ("d(2)" 2)
    ("e()" 1.1)
    ("e(22)" 22)
    ("f(1,3,5,6,8)" [1, 3, 5, 6, 8]))
   
   ;;test interactively
   ("def a():
   'docstring'
   interactive()
   x = 1
   return 'hi'
  
  def b():
   return 2"
    ("commandp(quote(a))" t)
    ("commandp(quote(b))" nil)))
  
  (pyel-create-tests
   function-arguments
   ("def func(__a,__b,c=1,d='two',*rest,**kwargs):
   return [__a,__b,c,d,rest,kwargs]"
    ("repr(func(1,2))"
     "[1, 2, 1, \"two\", [], {}]")
    ("repr(func(1,2,3))"
     "[1, 2, 3, \"two\", [], {}]")
    ("repr(func(1,2,3,4))"
     "[1, 2, 3, 4, [], {}]")
    ("repr(func(1,2,3,4,5))"
     "[1, 2, 3, 4, [5], {}]")
    ("repr(func(1,2,3,4,5,6))"
     "[1, 2, 3, 4, [5, 6], {}]")
    ("repr(func(1,2,3,4,5,6,x = 's'))"
     "[1, 2, 3, 4, [5, 6], {x: \"s\"}]")
    ("repr(func(1,2,3,4,5,6,x = 's',y = 23))"
     "[1, 2, 3, 4, [5, 6], {y: 23, x: \"s\"}]")
    ("repr(func(x = 's',__b = 324,__a = 'n',))"
     "[\"n\", 324, 1, \"two\", [], {x: \"s\"}]")
    ("repr(func(x = 's',__b = 324,__a = 'n',d = 2))"
     "[\"n\", 324, 1, 2, [], {x: \"s\"}]"))
   ;;tests with kwonlyargs
   ("def test(a, *b, c=1, d, **e):
   return [a, b, c, d, e]"
    ("repr(test('x',1,2,3,_d=1.1,_xx=2.2,_yy=3.3))"
     "[\"x\", [1, 2, 3], 1, nil, {-yy: 3.3, -xx: 2.2, -d: 1.1}]")
  
    ("repr(test('x',d=1.1))"
     "[\"x\", nil, 1, 1.1, {}]")
  
    ("repr(test('x'))"
     "[\"x\", nil, 1, nil, {}]")
  
    ("repr(test(d=1,c=2,a='x',e=4))"
     "[\"x\", (), 2, 1, {e: 4}]")
  
    ("repr(test(1,2,3,4,5,6))"
     "[1, (2, 3, 4, 5, 6), 1, nil, {}]"))
  
   ("def test(a,b):
       return [a,b]"
    ("test(1,2)" '(1 2))
    ("test(b=4,a='s')" '("s" 4)))
  
   ;;check that default values for optional args get set with or without kwargs
   ("def test(a,b,c,d=1,dd=2,ddd=4,*restst, x=1,xx=32,xxx=43,**kwargs_):
   return [a,b,c,d,dd,ddd,restst,x,xx,xxx,kwargs_]"
    ("repr(test(1,2,3,999,888,777,1,2,3,43,4,5,x=3))"
     "[1, 2, 3, 999, 888, 777, [1, 2, 3, 43, 4, 5], 3, 32, 43, {}]")
    ("repr(test(1,2,3,999,888,777,1,2,3,43,4,5))"
     "[1, 2, 3, 999, 888, 777, [1, 2, 3, 43, 4, 5], 1, 32, 43, {}]"))
   )
  
  (ert-deftest pyel-test-sort-kwargs ()
    (equal (pyel-sort-kwargs '(a b = 1 5 12 x = 1 3))
           '((a 5 12 3) ((x . 1) (b . 1))))
    (equal (pyel-sort-kwargs '(b = 1 x = 1))
           '(nil ((x . 1) (b . 1))))
    (equal (pyel-sort-kwargs '(b = 1))
           '(nil ((b . 1))))
    (equal (pyel-sort-kwargs '(a b c d))
           '((a b c d) nil))
    (equal (pyel-sort-kwargs '(a b = (b 1 2) 5 12 x = 1 (+ 1 3 ) y = "s"))
           '((a 5 12 (+ 1 3)) ((y . "s") (x . 1) (b b 1 2)))))
#+END_SRC
** bin-op, +, -, *, /, **, |, &
::Q can an overloaded + operator obj be used like:  3+obj
    these transforms assume you can
TODO: FIX: most of these are wrong.
      if one arg is a number, then the other may still be an object
overloadable operators: http://rgruet.free.fr/PQR26/PQR2.6.html#SpecialMethods

emacs bitwise operators: http://www.gnu.org/software/emacs/manual/html_node/elisp/Bitwise-Operations.html

*** transform
TODO: tests!
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform bin-op pyel ()
    (lambda (left op right &optional line col)
      (call-transform op left right)))
  
  (pyel-dispatch-func * (l r)
                      (number number) -> (* l r)
                      (string number)
                      (number string)  -> (pyel-num*str l r)
                      (object _)  -> (call-method l --mul-- r))
  
  (pyel-dispatch-func + (lhs rhs)
                      (number number) -> (+ lhs rhs)
                      (string string) -> (concat lhs rhs)
                      (list list)   -> (append lhs rhs)
                      (object _) -> (call-method lhs --add-- rhs)
                      (vector vector) -> (vconcat lhs rhs))
  
  (pyel-dispatch-func - (l r)
                      (number number) -> (- l r)
                      (object _) -> (call-method l --sub-- r))
  
  (pyel-dispatch-func ** (l r) ;;pow
                      (number number) -> (expt l r)
                      (object _) -> (call-method l --pow-- r))
  
  (pyel-dispatch-func / (l r)
                      (number number) -> (pyel-div l r)
                      (object _) -> (call-method l --truediv-- r))
  
  (pyel-dispatch-func // (l r) ;;floored (normal) division
                      (number number) -> (/ l r)
                      (object _) -> (call-method l --floordiv-- r))
  
  (pyel-dispatch-func ^ (l r) ;;bit xor
                      (number number) -> (logxor l r)
                      (object _) -> (call-method l --xor-- r))
  
  (pyel-dispatch-func & (l r) ;;bit and
                      (number number) -> (logand l r)
                      (object _) -> (call-method l --and-- r))
  
  (pyel-dispatch-func | (l r) ;;bit or
                      (number number) -> (logior l r)
                      (object _) -> (call-method l --or-- r))
  
  (pyel-dispatch-func % (l r)
                      (number number) -> (% l r)
                      (object _) -> (call-method l --mod-- r))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def BinOp(left, op, right, lineno='nil', col_offset='nil'):
      return l_str(["bin-op ", left, op, right, lineno, col_offset])
  
  def Add():
      return "+"
  def Mult():
      return "*"
  def Sub():
      return "-"
  def Div():
      return "/"
  def FloorDiv():
      return "//"
  def Pow():
      return "**"
  def BitXor():
      return "^"
  def BitOr():
      return "|"
  def BitAnd():
      return "&"
  def Mod():
      return "%"
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-num*str (left right)
    (let ((c 0)
          (ret "")
          num str)
      (if (numberp left)
          (setq num left
                str right)
        (setq num right
              str left))
      (while (< c num)
        (setq ret (concat ret str)
              c (1+ c)))
      ret))
  
  (defmacro pyel-div (l r)
    (cond ((or (floatp l)
               (floatp r))
           `(/ ,l ,r))
          ((integerp l)
           `(/ ,(* 1.0 l) ,r))
          ((integerp r)
           `(/ ,l ,(* 1.0 r)))
          (t `(/ (* ,l 1.0) ,r))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   add-op
   ("1 + 2" 3)
   ("'a' + 'b'" "ab")
   ("n1 = 2
  n2 = 5
  s1 = 'asd'
  s2 = 'df'
  l1 = [1]
  l2 = [3,'a']
  v1 = (1,2)
  v2 = (3,)"
    ("n1 + n2" 10)
    ("s1 + s2" "asddf")
    ("l1 + l2" '(1 3 "a"))
    ("v1 + v2" [1 2 3]))
   
   ("class test:
   def __init__(self, n):
    message('init')
    self.n = n*2
   def __add__(self, o):
    message('adding')
    return self.n + o.n
  x = test(5)
  y = test(2)"
    ("x + y" 14)))
  
  (pyel-create-tests
   sub-op
   ("3 - 2" 1)
   ("n1 = 5
  n2 = 3"
    ("n1 - n2" 2)))
  
  (pyel-create-tests
   mult-op
   ("3 * 4" 12)
   ("'a' * 3" "aaa")
   ("4*'b'" "bbbb")
   ("n1 = 2
  n2 = 4
  s = 's'"
    ("n1 * n2" 8)
    ("s * n1" "ss")
    ("n1 * s" "ss"))
   )
  
  (pyel-create-tests
   pow-op
   ("3 ** 4" 81)
   ("n1 = 2
  n2 = 4"
    ("n1 ** n2" 16)))
  
  (pyel-create-tests
   div-op
   ("9 / 4" 2.25)
   ("9 // 4" 2)
   ("a = 9
  b = 4"
    ("a / b" 2.25)
    ("a // b" 2))
   )
  
  (pyel-create-tests
   bin-ops
   ("3 & 5" 0)
   ("3 | 5" 7)
   ("3 ^ 5" 6)
   ("a = 3
  b = 5"
    ("a & b" 0)
    ("a | b" 7)
    ("a ^ b" 6)))
  
  (pyel-create-tests
   mod-op
   ("5 % 3" 2)
   ("a = 3
  b = 5"
    ("b % a" 2)))
#+END_SRC
** subscript,index,slice
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform index pyel ()
    (lambda (value &optional line col)
      (transform value)))
  
  (def-transform slice pyel ()
    (lambda (lower upper step)
      (call-method slice
                   --new-- 
                   (transform lower)
                   (transform upper)
                   (transform step))))
  
  (def-transform subscript pyel ()
    (lambda (value _slice ctx &optional line col)
      (pyel-subscript value _slice ctx line col)))
  
  (pyel-dispatch-func sc-load-i (name value)
                      (list _) -> (nth value name)
                      (object _) -> (call-method name --getitem-- value)
                      (vector _) -> (aref name value)
                      (string _) -> (char-to-string (aref name value))
                      (hash _) -> (gethash value name))
  
  (pyel-dispatch-func sc-load-s (name start end step)
                      (object _ _ _) -> (call-method name --getitem--
                                                     (call-method slice
                                                                  --new--
                                                                  start
                                                                  end
                                                                  step))
                      ;;TODO implement step
                      (_ _ _ _) -> (subseq name start end))
  
  (pyel-dispatch-func sc-store-s (name start end step assign)
                      (object _ _ _) -> (call-method name --setitem--
                                                     (call-method slice
                                                                  --new--
                                                                  start
                                                                  end
                                                                  step)
                                                     assign)
  
                      ;;TODO implement step
                      (_ _ _ _) -> (setf (subseq name start end) assign))
  
  (pyel-dispatch-func sc-store-i (name value assign)
                      (list _) -> (setf (nth value name) assign)
                      (object _) -> (call-method name --setitem-- value assign)
                      (vector _) -> (setf (aref name value) assign)
                      (hash _) -> (puthash value assign name))
  ;;                  (string _) -> not supported in python
  
  (defun pyel-subscript (value _slice ctx &optional line col)
    (let* (;(value (transform value))
           (t-slice (using-context 'return-type?
                                   (transform _slice)))
           (slice-type return-type)
           (ctx (cond ((context-p 'force-load) 'load)
                      ((context-p 'force-store) 'store)
                      (t (eval ctx))))
           (t-value (remove-context aug-assign
                                    (using-context 'return-type?
                                                   (transform value))))
           (_value-type (cond ((pyel-is-class-type return-type)
                               '(class))
                              ((pyel-is-instance-type return-type)
                               '(instance))
                              (t return-type)))
           (none '(_))
           start stop step ret rhs assign-val)
      
      (setq slice-type (if (null slice-type) nil (pyel-force-list slice-type))
            value-type (if (null _value-type) nil (pyel-force-list _value-type)))
  
      (when (py-object-p t-slice)
        (setq start (getattr t-slice start)
              stop (getattr t-slice stop)
              step (getattr t-slice step)))
  
      (setq ret
            (if (context-p 'aug-assign)
                (progn
                  (if (py-object-p t-slice)
                      (progn
                        (setq known-types (list value-type none none none))
                        (call-transform-no-trans 'augmented-assign-slice
                                                 t-value start stop step))
  
                    (setq rhs (using-context 'return-type?
                                             (remove-context
                                              aug-assign
                                              (transform aug-assign-value)))
                          return-type (pyel-force-list return-type)
                          known-types (list return-type return-type)
                          ;;^assume that both types are the same
                          ;;TODO: is this really a valid assumption?
                          assign-val (call-transform-no-trans aug-assign-op
                                                              aug-assign-lhs
                                                              rhs))
                    (setq known-types (list value-type none none))
                    (call-transform-no-trans 'augmented-assign-index
                                             t-value t-slice
                                             assign-val)))
              ;;else: normal index or slice
              
              (if (eq ctx 'load)
                  (if (py-object-p t-slice)
                      (progn
                        (setq known-types (list value-type none none none))
                        (call-transform-no-trans 'sc-load-s
                                                 t-value start stop step)) ;;load slice
                    (setq known-types (list value-type slice-type))
                    (call-transform-no-trans 'sc-load-i
                                             t-value t-slice)) ;;load index
                ;;else: store
                (setq t-assign-value (using-context 'return-type?
                                                    (transform assign-value))
                      assign-value-type return-type)
                (if (py-object-p t-slice)
                    (progn
                      (setq known-types (list value-type none none none none))
                      (call-transform-no-trans 'sc-store-s
                                               t-value start stop step t-assign-value))
                  (setq known-types (list value-type none none))
                  (call-transform-no-trans 'sc-store-i
                                           t-value t-slice t-assign-value))))) ;;store index
      (setq return-type nil) ;;return type unknown
      ret))
  
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Slice(lower, upper, step):
      step = step or "nil"
      lower = lower or 0
      upper = upper or "nil"
      return l_str(["slice", lower, upper, step])

  def Index(value, lineno='nil', col_offset='nil'):
      return "(index " + value + " " + str(lineno) + " " + str(col_offset) + ")"

  def Subscript(value, slice, ctx, lineno='nil', col_offset='nil'):
      return ll_str(["subscript", value, slice, ctx, lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   slice-object
   ("o1 = slice(5)
  o2 = slice(1, 5)
  o3 = slice(2, 6, 7)"
    ("o1.start" 0)
    ("o1.stop" 5)
    ("o1.step" nil)
    ("o2.start" 1)
    ("o2.stop" 5)
    ("o2.step" nil)
    ("o3.start" 2)
    ("o3.stop" 6)
    ("o3.step" 7)))
  
  (pyel-create-tests
   subscript
   ;;load index====
   ;;string
   ("a = '1X23'"
    ("a[1]" "X"))
   ;;list
   ("a = [1,2,3,4]"
    ("a[1]" "2"))
   ;;vector
   ("a = (1,2,3,4)"
    ("a[1]" "2"))
   ;;object
   ("class a:
   def __getitem__ (self, value):
    return value + 4
  x = a()"
    ("x[1]" "5"))
   ;;=load slice====
   ;;vector
   ("a = (1,2,3,4,5)"
    ("a[1:4]" "(2,3,4)")
    ("a[:4]" "(1,2,3,4)")
    ("a[2:]" "(3,4,5)")
    ("a[:]" "(1,2,3,4,5)"))
   ;;list
   ("a = [1,2,3,4,5]"
    ("a[1:4]"  "[2,3,4]")
    ("a[:4]"  "[1,2,3,4]")
    ("a[2:]"  "[3,4,5]")
    ("a[:]"  "[1,2,3,4,5]"))
   ;;strings
   ("a = '012345678'"
    ("a[1:4]" "123")
    ("a[:4]" "0123")
    ("a[2:]" "2345678")
    ("a[:]" "012345678"))
   ;;object
   ("class a:
   def __getitem__ (self, value):
    return value.start + value.stop
  x = a()"
    ("x[1:2]" "3")
    ("x[5:7]"  "12"))
  
   ;;store index
   ;; list
   ("def __add(a,b):
   return a+b
  a = [1,2,3,4]
  a[0] = __add(a[1],a[2])
  a[2] = 'str'"
    ("a[0]"  5)
    ("a[2]"  "str"))
   ;;vector
   ("a = (1,2,3,4)
  a[0] = a[1] + a[2]
  a[2] = 'str'"
    ("a[0]"  5)
    ("a[2]"  "str"))
   ;;object
   ("class a:
   def __setitem__ (self, index, value):
    self.index = index
    self.value = value
  x = a()
  x[3] = 5"
    ("x.index" 3)
    ("x.value" 5))
  
   ;;store slice
   ;;list
   ("a = [1,2,3,4,5,6]"
    ("a[1:4] = [5,4,'f']"
     ("a"  '(1 5 4 "f" 5 6)))
    ("a[:3] = ['a',4,2.2]"
     ("a" '("a" 4 2.2 4 5 6)))
    ("a[3:] = [3,3]"
     ("a" '(1 2 3 3 3 6)))) ;;TODO: should == '(1 2 3 3 3 6)
  
   ;;vector
   ("a = (1,2,3,4,5,6)
  a[1:4] = (5,4,'f')"
    ("a" [1 5 4 "f" 5 6]))
   ("a = (1,2,3,4,5,6)
  a[:3] = ('a',4,2.2)"
    ("a" ["a" 4 2.2 4 5 6]))
   ("a = (1,2,3,4,5,6)
  a[3:] = (3,3)"
    ("a" [1 2 3 3 3 6])) ;; TODO: should == [1, 2, 3, 3, 3]
  
   ;;string
   ("a = '123456'
  a[1:4] = '54f'"
    ("a" "154f56"))
  
   ("a = '123456'
  a[:3] = 'a42'"
    ("a" "a42f56"))
  
   ("a = '123456'
  a[3:] = '33'"
    ("a" "'a42336'")) ;;TODO: should == '12333'"
  
   ;;object
   ("class a:
   def __setitem__ (self, index, value):
    self.start = index.start
    self.stop = index.stop
    self.step = index.step
    self.value = value
  x = a()
  x[2:3] = [1,2,3]"
    ("x.start" "2")
    ("x.stop" "3")
    ("x.value" "[1,2,3]"))
   ("x.start" "2")
   ("x.stop" "3")
   ("x.value" "[1,2,3]")
  
   ("a = [1,2,3,4,5,6]
  a[2] += 3"
    ("a" '(1 2 6 4 5 6)))
  
   ("a = [1,2,3,4,5,6]
  b = [1,2,3,4,5,6]
  a[2] += b[3]"
    ("a" '(1 2 7 4 5 6)))
  
   ("[2,3,3][2]" 3)
   ("[1,2,(3,2,8)][2][2]" 8))
#+END_SRC

** class
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform classdef pyel ()
    (lambda (name bases keywords starargs kwargs
                  body decorator_list &optional line col)
      (pyel-defclass name bases keywords starargs kwargs
                     body decorator_list line col)))
  
  (defun pyel-defclass (name bases keywords starargs kwargs
                             body decorator_list &optional line col)
    (let* ((class-name (_to- (transform name)))
          (t-bases (mapcar 'transform bases))
          (outer-env type-env)
          (type-env (pyel-make-type-env type-env))
          ;;`class-type' is used by other transforms
          (current-class-type (pyel-make-class-type class-name outer-env)))
      
      ;;set the type of this class in the outer type-env
      (pyel-env-set class-name current-class-type outer-env)
      
      (when (context-p 'function-def)
        (push class-name let-arglist)
        (push '__defined-in-function-body t-bases))
      
      (add-to-list 'pyel-defined-classes name)
      (setq pyel-last-class-type-env type-env)
      (remove-context
       function-def
       (using-context 'class-def
                      `(define-class ,class-name ,t-bases
                         ,@(mapcar 'transform body)
                         )))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def ClassDef (name, bases, keywords, starargs, kwargs, body, decorator_list, lineno='nil', col_offset='nil'):
    return ll_str(["classdef",
                   name,
                   ll_str(bases),
                   ll_str(keywords),
                   starargs or "nil",
                   kwargs or "nil",
                   ll_str(body),
                   ll_str(decorator_list),
                   lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   objects
   ("class tclass():
   '''a test class'''
   cvar = 12
   def __init__(self, x):
    self.a = x + 10
   def get(self):
    return self.a
   def set(self,n):
    self.a = n
  x = tclass(4)"
    ("tclass.__name__" "tclass")
    ("tclass.get" (lambda (self) (getattr self a)))
    ("tclass.set" (lambda (self n) (setattr self a n)))
    ("tclass.cvar" 12)
    ("tclass.v = 'hi'" ("tclass.v" "hi"))
    ("tclass.set(tclass, 23)" ("tclass.a" 23))
    ("tclass.set(tclass, 19)" ("tclass.get(tclass)" 19))
    ("tclass.add5 = lambda self: self.cvar + 5" ("tclass.add5" (lambda (self) nil (pyel-+ (getattr self cvar) 5))))
    ("repr(x)" "<class 'object'>")
    ("x.__class__ == tclass" t)
    ("x.__class__.__name__" "tclass")
    ("x.a" 14)
    ("x.a = 2" ("x.a" 2))
    ("x.cvar = 4" (" x.cvar, tclass.cvar" [4 12]))
    ("x.set(10); y = x.get" ("y()" 10))
    ("tclass.sixmore = lambda self: self.a + 6
  y = x.sixmore
  x.a = 2"
     ("y()" 8)))

   ("class one:
   def __init__(self,x):
    self.n = x
   def m(self):
    return self.n + 1
  class two:
   def __init__(self):
    self.other = one(5)
  x = two()"
    ("x.other.n" 5)
    ("x.other.m()" 6)))

  (pyel-create-tests
   special-method-lookup
   ( "class adder:
   def __init__(self, n):
    self.x = n
   def __call__ (self, n):
    return self.x + n
  c = adder(10)
  d = adder(10)
  d.__call__ = lambda : 'hi'"
     ("c(6)" 16)
     ("repr(c.__call__)"
      "<bound method adder.__call__ of adder object at 0x18b071>")
     ("d.__call__" (lambda nil nil "hi"))))
#+END_SRC
** assert
*** transform
;;TODO: have custom option for t/nil value of assert's 2nd parameter

;;TODO: fix this case:
  (pyel "assert (True,1), 'message'")
   -> (assert (vector True 1) t "message")
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform assert pyel ()
    (lambda (test msg &optional line col)
      (pyel-assert test msg line col)))
  
  (defun pyel-assert (test msg &optional line col)
    `(assert ,(transform test) t ,(transform msg)))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Assert(test, msg, lineno='nil', col_offset='nil'):
      return ll_str(["assert ", test , msg or "nil", lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests assert
                     "assert sldk()"
                     "assert adk,'messsage'")
#+END_SRC
** for loop
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform for pyel ()
    (lambda (target iter body orelse &optional line col)
      (pyel-for target iter body orelse line col)))
  
  ;;TODO: for a,y in thing: ...
  ;;TODO: check if iter is an object, then do the iterator thing
  
  (defun pyel-for (target iter body orelse &optional line col)
    (let ((target (using-context 'for-loop-target
                                 (if (eq (car target) 'tuple)
                                     (mapcar 'transform (cadr target))
                                   (list (transform target)))))
          (body (using-context 'for (mapcar 'transform body)))
          (else (mapcar 'transform orelse))) ;;break/continue for else?
      (when else
        (setq body (append body (cons 'else else))))
  
      `(py-for ,@target in ,(transform iter) ,@body)))
  
  ;; (if (eq (car target) 'tuple)
  ;;     "TODO: var unpacking"
  ;;   ;;create a temp target variable
  ;;   ;;in body, unpack that into the provided target variables
  
  ;;   (let* ((continue-for nil)
  ;;          (break-for nil)
  ;;          (code (using-context 'for (mapcar 'transform body)))
  ;;          (break-code (if break-for '(catch '__break__)
  ;;                        pyel-nothing))
  ;;          (continue-code (if continue-for '(catch '__continue__)
  ;;                           pyel-nothing)))
  ;;     (setq _x break-for)
  ;;     `(,@break-code
  ;;       (loop for ,(using-context 'for-loop-target
  ;;                                 (transform target))
  ;;             in (py-list ,(transform iter))
  ;;             do (,@continue-code
  ;;                 ,@(mapcar 'transform body)))
  ;;       ,@(mapcar 'transform orelse)))))
  
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def For(target, iter, body, orelse, lineno='nil', col_offset='nil'):
      return l_str(["for ", target, iter, ll_str(body), ll_str(orelse), lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (setq pyel-for-loop-code-fns '((cons . pyel-for-loop-list-code)
                                 (vector . pyel-for-loop-vector-code)
                                 (string . pyel-for-loop-string-code)
                                 (object . pyel-for-loop-object-code)))
  
  (defsubst pyel-type-of (obj)
    (if (py-object-p obj)
        'object
      (type-of obj)))
  
  (defmacro py-for (&rest args)
    "(for <targets> in <iter> <body> else <body>)
  else is optional"
    
    ;;TODO: error checking for correct form
    (let* ((targets (pyel-split-list args 'in))
           (args (cdr targets))
           (targets (car targets))
           (iter (pop args)) ;;TODO: must iter be only one form?
           
           (body (pyel-split-list args 'else))
           (else-body (cdr body))
           (body (car body))
           
           (target (cond ((symbolp targets) ;;nil when there are multiple targets
                          targets)
                         ((= (length targets) 1)
                          (car targets))
                         (t nil)))
           
           (unpack-code (unless target
                          (let (ret)
                            (dotimes (i (length targets) (reverse ret))
                              (push `(setq ,(nth i targets)
                                           (nth ,i __target__))
                                    ret)))))
           (unpack-code (cons '(setq __target__ (nth __idx __tmp-list)) unpack-code))
           
           (current-transform-table (get-transform-table 'for-macro))
           __for-continue ;;these are set by the for-macro transforms
           __for-break
           )
      ;;TODO: when multiple targets, check that all lists are the same size
      (setq body (mapcar 'transform body))
      
      ;;TODO: expand to simpler cases when types are known
      
      `(let* ((__iter ,iter)
              (func (cdr (assoc (setq _t (pyel-type-of __iter)) pyel-for-loop-code-fns))))
         (if (null func)
             (error "invalid type")
           (eval (funcall func ',targets __iter ',body ',else-body
                          ,__for-break ,__for-continue) :lexical)))))
  
  
  (defun pyel-for-loop-list-code (targets iter body else-body
                                          &optional break continue)
    (let* ((loop-body `(,@(if (= (length targets) 1)
                              `((setq ,(car targets) (nth __idx __tmp-lst)))
                            ;;unpack-code
                            (cons '(setq __target__ (nth __idx __tmp-lst))
                                  (let (ret)
                                    (dotimes (i (length targets) (reverse ret))
                                      (push `(setq ,(nth i targets)
                                                   (pyel-nth ,i __target__))
                                            ret)))))
                        (setq __idx (1+ __idx))
                        
                        ,@body))
           (loop-body (if continue
                          `((catch '__continue__ ,@loop-body))
                        loop-body))
           
           (while-loop `(while (< __idx __len)
                          ,@loop-body))
           
           (while-loop (if break
                           `(catch '__break__ ,while-loop)
                         while-loop)))
      
      `(let* ((__tmp-lst ',iter)
              ;;      ,@iter-lets
              ;;      ,@next-function-lets
              (__len (length __tmp-lst))
              (__idx 0))
         ,while-loop
         ,@else-body)))
  
  
  (defun pyel-for-loop-string-code (target iter body else-body
                                           &optional break continue)
    (let* ((loop-body `(,@(if (= (length target) 1)
                              `((setq ,(car target)
                                      (pyel-string-nth __tmp-str __idx)))
                            (error "ValueError: need more than 1 value to unpack"))
                        (setq __idx (1+ __idx))
                        ,@body))
           (loop-body (if continue
                          `((catch '__continue__ ,@loop-body))
                        loop-body))
           
           (while-loop `(while (< __idx __len)
                          ,@loop-body))
           
           (while-loop (if break
                           `(catch '__break__ ,while-loop)
                         while-loop)))
      
      `(let* ((__tmp-str ',iter)
              (__len (length __tmp-str))
              (__idx 0))
         ,while-loop
         ,@else-body)))
  
  ;;yes, I'm aware how repetitive it's getting
  
  (defun pyel-for-loop-vector-code (targets iter body else-body
                                            &optional break continue)
    (let* ((loop-body `(,@(if (= (length targets) 1)
                              `((setq ,(car targets) (aref __tmp-lst __idx )))
                            ;;unpack-code
                            (cons '(setq __target__ (aref __tmp-lst __idx))
                                  (let (ret)
                                    (dotimes (i (length targets) (reverse ret))
                                      (push `(setq ,(nth i targets)
                                                   (pyel-nth ,i __target__))
                                            ret)))))
                        (setq __idx (1+ __idx))
  
                        ,@body))
           (loop-body (if continue
                          `((catch '__continue__ ,@loop-body))
                        loop-body))
  
           (while-loop `(while (< __idx __len)
                          ,@loop-body))
  
           (while-loop (if break
                           `(catch '__break__ ,while-loop)
                         while-loop)))
  
      `(let* ((__tmp-lst ',iter)
              ;;      ,@iter-lets
              ;;      ,@next-function-lets
              (__len (length __tmp-lst))
              (__idx 0))
         ,while-loop
         ,@else-body)))
  
  
  (defun pyel-for-loop-object-code (targets object body else-body
                                            &optional break continue)
    (let* ((loop-body `(,@(if (= (length targets) 1)
                              `((setq ,(car targets) (call-method __iter__ --next--)))
                            ;;unpack-code
                            (cons '(setq __target__ (call-method __iter__ --next--))
                                  (let (ret)
                                    (dotimes (i (length targets) (reverse ret))
                                      (push `(setq ,(nth i targets)
                                                   (pyel-nth ,i __target__))
                                            ret)))))
                        ,@body))
           (loop-body (if continue
                          `((catch '__continue__ ,@loop-body))
                        loop-body))
  
           (while-loop `(condition-case nil
                            (while t
                              ,@loop-body)
                          (StopIteration nil)))
  
           (while-loop (if break
                           `(catch '__break__ ,while-loop)
                         while-loop)))
  
      ;;variable __iter is set by calling function
      `(let* (;;(__iter ,object)
              (__iter__ (condition-case nil
                            (call-method __iter --iter--)
                          (AttributeError
                           (py-raise (TypeError (format
                                                 "'%s' object is not iterable"
                                                 (getattr __iter --name--))))))))
         ,while-loop
         ,@else-body)))
  
  
  
  (make-transform-table 'for-macro)
  
  (def-transform break for-macro ()
    (lambda () (setq __for-break t)
      '(throw '__break__ nil)))
  
  (def-transform continue for-macro ()
    (lambda () (setq __for-continue t)
      '(throw '__continue__ nil)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   for-loop
  
   ("x = []
  for a in range(5):
   x.append(a)"
    ("x" '(0 1 2 3 4)))
  
   ;;lists
   ("x = []
  for a,b in [[1,2],'34',(5,6)]:
   x.append([a,b])"
    ("x" '((1 2) ("3" "4") (5 6))))
  
   ("x = []
  for a,b,c,d in [[1,2,1,1],'34xa',(5,6,'a',1)]:
   x.append([a,b,c,d,a])"
    ("x" '((1 2 1 1 1) ("3" "4" "x" "a" "3") (5 6 "a" 1 5)))) 
  
   ("n = 0
  for a in range(5):
   for b in range(5):
    n = n + a + b"
    ("n" 100))
   
   ("x = []
  for a in range(100):
   if (a % 2 == 0):
    continue
   if a > 10:
    break
   x.append(a)"
    ("x" '(1 3 5 7 9)))
  
   ;;vectors
   ("x = []
  for a in (1,2,3,4):
   x.append(2*a)"
    ("x" '(2 4 6 8)))
  
   ("x = []
  for a,b in ([1,2],'34',(5,6)):
   x.append([a,b])"
    ("x" '((1 2) ("3" "4") (5 6))))
   ("tup = make_vector(20,0)
  for i in range(10):
   tup[i] = i
  x = []
  tup = make_vector(20,0)
  for i in range(20):
   tup[i] = i;
  for a in tup:
   if (a % 2 == 0):
    continue
   if a > 10:
    break
   x.append(a)"
    ("x" '(1 3 5 7 9)))
  
   ("x = []
  for a in 'string':
   x.append(a)"
    ("x" '("s" "t" "r" "i" "n" "g")))
  
   ;;strings
   ("x = []
  c = 0
  def getstr():
   global c
   c+=1
   return 'qwerty'
  for a in getstr():
   x.append(a)"
    ("x" '("q" "w" "e" "r" "t" "y"))
    ("c" 1))
  
   ;;objects
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()
  x = []
  for n in obj:
   x.append(n)"
    ("x" '("5" "4" "3" "2" "1"))))
  
#+END_SRC
** global
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform global pyel ()
    (lambda (names &optional line col)
      (if (context-p 'function-def)
          (progn (mapc (lambda (x) (add-to-list 'global-vars (_to- x))) names)
                 pyel-nothing)
        (pyel-not-implemented "'global' calls outside of function definitions"))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Global(names, lineno='nil', col_offset='nil'):
    return l_str(['global', l_str(names), lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   global
   ("x = 1
  y = 1
  def f():
   global x
   x = 3
   y = 2
  f()"
    ("x" 3)
    ("y" 1)))
#+END_SRC
** nonlocal
TODO: 'nonlocal' is currently equivalent to 'global'
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el

 #+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Nonlocal(names, lineno='nil', col_offset='nil'):
    return l_str(['global', l_str(names), lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** lambda
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform lambda pyel ()
    (lambda (args body &optional line col)
      (using-context 'lambda-def
                     (pyel-def nil args body nil nil line col))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Lambda(args, body, lineno='nil', col_offset='nil'):
      return ll_str(["lambda", ll_str(args),'('+ll_str(body)+')', lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   lambda
   ("f = lambda x,y: x+y
  f2 = lambda x,*rest: [x,rest]
  f3 = lambda x, *rest, **k : [x, rest, k]"
    ("f(1,2.3)" 3.3)
    ("f2(1,2,3,4,'asd')" '(1 (2 3 4 "asd")))
    ("repr(f3(1,2,3,4,5,a__=1,b__=2))"
     "[1, [2, 3, 4, 5], {b--: 2, a--: 1}]"))
   ("reduce(lambda a,b:a+b, range(2, 9))" 35))
  
#+END_SRC
** unary op, not, usub
TODO: tests!
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform unary-op pyel ()
    (lambda (op operand &optional line col)
      (call-transform op operand)))
  
  (pyel-dispatch-func not (obj)
                      (number) -> (eq obj 0)
                      (string) -> (eq obj "")
                      (list)   -> (null obj)
                      (object) -> (pyel-object-bool obj)
                      (vector) -> (eq obj [])
                      (hash)   -> (= (hash-table-count obj) 0)
                      (_)      -> (not obj))
  
  (pyel-dispatch-func usub (x)
                      (number) -> (- x)
                      (object) -> (--usub-- x) ;;?
                      )
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def UnaryOp(op, operand, lineno='nil', col_offset='nil'):
      return l_str(["unary-op ", op, operand, lineno, col_offset])

  def Not():
      return "not"

  def USub():
      return "usub"
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst pyel-object-bool (obj)
    (condition-case nil
        (call-method obj --bool--)
      (AttributeError
       (condition-case nil
           (= (call-method obj --len--) 0)
         (AttributeError t)))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   not
   ("class c:
   def __init__(self, n):
    self.length = n
   def __len__(self):
    return self.length
  x = c(0)
  y = c(1)"
    ("not x" t)
    ("not x" nil))
   ("not 's'" nil)
   ("not ''" t)
   ("not []" t)
   ("not ()" t)
   ("not {}" t)
   ("not (2,)" nil))
  
  (pyel-create-tests
   usub
   ("-1" -1)
   ("a = 2"
    ("-a" -1)))
#+END_SRC
** augmented assignment
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-dispatch-func augmented-assign-index (target index _assign-val)
                      (list _ _ _) -> (py-list-aug-assign target index $assign-val)
                      (hash _ _ _) -> (py-hash-aug-assign target index $assign-val))
  
  (pyel-dispatch-func augmented-assign-name (target _op value _assign-value)
                      (object _ _ _) -> (py-obj-aug-assign target $op value)
                      (_ _ _ _)      -> (setq $target $assign-value))
  
  (def-transform aug-assign pyel ()
    (lambda (target op value &optional line col)
      (pyel-aug-assign target op value line col)))
  
  (defun pyel-aug-assign (target op value &optional line col)
    (let* (;;inter-transform variables:
           (aug-assign-value value) 
           (aug-assign-op op))
  
      (using-context 'aug-assign
                     (transform target))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def AugAssign(target, op, value, lineno='nil', col_offset='nil'):
      return l_str(['aug-assign', target, op, value, lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;;To prevent repeated evaluation and allow the augmented assignment operation
  ;;to be fully expanded on known types, some trickiness is employed.
  ;;The intermediate variable `aug-assign-lhs' is created for the left-hand side,
  ;;The operation is expanded with it and then in augmented assign macros
  ;;such as `py-list-aug-assign' the `aug-assign-lhs' variable is given a value
  
  (defconst aug-assign-lhs '_lhs)
  
  (defmacro py-list-aug-assign (target index assign-val)
    (let ((aug-assign-lhs-val '(car __cell__)))
      `(let* ((__cell__ (nthcdr ,index ,target))
              (,aug-assign-lhs (car __cell__)))
         (setcar __cell__ ,assign-val))))
  
  (defmacro py-hash-aug-assign (table key assign-val)
    `(let* ((__tbl__ ,table)
            (__key__ ,key)
            (,aug-assign-lhs (gethash __key__ __tbl__)))
       (puthash __key__ ,assign-val __tbl__)))
  
  (defmacro py-obj-aug-assign (object op value)
    (let ((method (cadr (assoc op '((+ --iadd--)
                                    (& --iand--)
                                    (// --ifloordiv--)
                                    (<< --ilshift--)
                                    (% --imod--)
                                    (* --imul--)
                                    (| --ior--)
                                    (** --ipow--)
                                    (>> --irshift--)
                                    (- --isub--)
                                    (/ --itruediv--)
                                    (^ --ixor--))))))
      (list 'call-method object method value)))
  
  (defmacro py-attr-aug-assign (obj attr value)
    `(let* ((__obj__ ,obj)
            (,aug-assign-lhs (getattr __obj__ ,attr)))
       (setattr __obj__ ,attr ,value)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   aug-assign
   ("x = 2"
    ("x += 3" ("x" 5))
    ("x *= 3" ("x" 6))
    ("x -= 1" ("x" 1))
    ("x /= 4" ("x" 0.5)))
  
   ("x = [2,'s']"
    ("x[0] += 3" ("x[0]" 5))
    ("x[0] *= 3" ("x[0]" 6))
    ("x[0] -= 1" ("x[0]" 1))
    ("x[0] /= 4" ("x[0]" 0.5))
    ("x[1] += 's'" ("x[1]" "ss")))
  
   ("a = {1:4, 2:4, 3:4}
  a[1] += 3
  a[2] *= 4
  a[3] -= 4"
    ("a[1]" 7)
    ("a[2]" 4)
    ("a[3]" 0))
  
   ("a = [1,3]
  a += [4,2]"
   ("a" '(1 3 4 2)))
   
  ("v = (1,2)
  v += (3,3)"
   ("v" [1 2 3 3]))
  
  ("s = 'st'
  s += 'ring'"
   ("s" "string"))
   
   ("class a:
   x = 2"
    ("a.x += 3" ("a.x" 5))
    ("a.x *= 3" ("a.x" 6))
    ("a.x -= 1" ("a.x" 1))
    ("a.x /= 4" ("a.x" 0.5)))
  
   ("class tst:
   x = 3
   def __iadd__(self, n):
    self.x = self.x + n
    return self
  o = tst()
  o += 2"
    ("o.x" 5)))
  
#+END_SRC
** return
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform return pyel ()
    (lambda (value &optional line col)
  
      (if (context-p 'tail-context)
          (transform value)
        (setq return-middle t)
        `(throw '__return__ ,(transform value)))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Return(value, lineno='nil', col_offset='nil'):
      return "(return " + str(value) + " " + str(lineno) + " " + str(col_offset)+ ")"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** break and continue
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform break pyel ()
    (lambda (&optional line col)
      ;;TODO verify that it is ok to just use one inter-template var for this
      (context-switch
       (while (setq break-while t)
         '(throw '__break__ nil))
       (for (setq break-for t)
            '(break)))))
  
  (def-transform continue pyel ()
    (lambda (&optional line col)
      ;;TODO verify that it is ok to just use one inter-template var for this
      (context-switch
       (while (setq continue-while t)
         '(throw '__continue__ nil))
       (for (setq continue-for t)
            '(continue)))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Break(lineno=None, col_offset=None):
      return l_str(["break", lineno, col_offset])

  def Continue(lineno=None, col_offset=None):
      return l_str(["continue", lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   break
   ("x = 0
  while x < 10:
   x = x + 1
   if x == 3:
    break"
    ("x" 3))
  
   ("x = [0]
  c = 0
  while c < 3:
   c = c + 1
   y = 0
   while y < 10:
    y = y + 1
    x.append(y)
    if y == 3:
     x.append('b')
     break"
    ("x" '(0 1 2 3 "b" 1 2 3 "b" 1 2 3 "b"))))
  
  (pyel-create-tests 
   continue
   ("l = [0]
  y = 8;
  while y > 0:
   y = y -1
   if y % 2 == 0:
    continue
   l.append(y)"
    ("l"  '(0 7 5 3 1)))
  
   ("x = [0]
  c = 0
  while c < 3:
   c = c + 1
   y = 0
   while y < 5:
    y = y + 1
    if y % 2 == 0:
     x.append('c')
     continue
    x.append(y)"
    ("x" '(0 1 "c" 3 "c" 5 1 "c" 3 "c" 5 1 "c" 3 "c" 5))))
#+END_SRC
** exception handler
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform except-handler pyel (type name body)
    ;;TODO: name?
    (lambda (type name body &optional line col)
      `(,(or (transform type) 'error)
        ,@(mapcar 'transform body))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def ExceptHandler(type, name, body, lineno='nil', col_offset='nil'):
      return l_str(["except-handler", type or 'nil', name or 'nil', l_str(body), lineno, col_offset])
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

** try except
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform try pyel (body handlers orelse)
    ;;TODO: orelse
    (lambda (body handlers orelse &optional line col)
      (let ((body (mapcar 'transform body)))
        (if (> (length body) 1)
            (setq body (cons 'progn body))
          (setq body (car body)))
        `(condition-case nil
             ,body
             ,@(mapcar 'transform handlers)))))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def TryExcept(body, handlers, orelse, lineno='nil', col_offset='nil'):
      return l_str(["try", l_str(body), l_str(handlers), l_str(orelse), lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   try
   ("a = ''
  try:
   1 / 0
   a = 'yes'
  except:
   a = 'no'
  
  b = ''
  try:
   b = 'a'
   [1,3][23]
  except:
   try:
    1/0
   except:
    b = 'ok'
  
  c = ''
  try:
   c = 'y'
  except:
   c = 'n'
  
  try:
   raise IndexError
   d = 2
  except IndexError:
   d = 2233
  except:
   d = 'no'
  
  "
    ("a" "no")
    ("b" "ok")
    ("c" "y")
    ("d" 2233)))
#+END_SRC
** in, not in
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  ;;a not in b
  ;; function:    is_not(a, b)  (in the operator module)
  (pyel-dispatch-func not-in (l r)
                      (_ list) -> (not (py-list-member l r))
                      (_ string) -> (not (py-string-member l r))
                      (_ object) -> (not (py-object-member l r))
                      (_ vector) -> (not (py-vector-member l r)))
  
  ;;a in b
  ;; function:    is_(a, b)
  (pyel-dispatch-func in (l r)
                      (_ list) -> (py-list-member l r)
                      (_ string) -> (py-string-member l r)
                      (_ object) -> (py-object-member l r)
                      (_ vector) -> (py-vector-member l r))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-vector-member (elt vector)
    "Return non-nil if ELT is an element of VECTOR. Comparison done with `equal'."
    (let ((i 0)
          (len (length vector))
          found)
      (while (and (not found)
                  (< i len))
        (if (equal (elt vector i) elt)
            (setq found t)
          (setq i (1+ i))))
      found))
  
  (defsubst py-list-member (elt list)
    (if (member elt list) t nil))
  
  (defun py-string-member (s string)
    "Return non-nil if S is a substring of STRING"
    (and (stringp s) (if (string-match (regexp-quote s) string) t nil)))
  
  (defun py-object-member (elt obj)
    ;;https://docs.python.org/3/reference/expressions.html#membership-test-details
    (if (py-object-p obj)
        (if (obj-hasattr obj --contains--)
            (call-method obj --contains-- elt)
  
          ;;else: try iteration via __iter__
          (let ((iter (condition-case nil
                          (call-method obj --iter--)
                        (AttributeError nil)))
                (not-found t)
                (i -1) val)
            (if iter
                (condition-case nil
                    (while not-found
                      (setq val (call-method iter --next--))
                      (if (or (eq val elt) (equal val elt))
                          (setq not-found nil)))
                  (StopIteration nil))
              ;;else: try old-style iteration protocol if __getitem__ is defined
              (if (obj-hasattr obj --getitem--)
                  (condition-case nil
                      (while not-found
                        (setq val (call-method obj --getitem-- (setq i (1+ i))))
                        (if (or (eq val elt) (equal val elt))
                            (setq not-found nil))
  
                        )
                    (IndexError nil))
                ;;else: not iterable
                (py-raise (TypeError
                           (format "argument of type '%s' is not iterable" (py-type obj))))))
            (not not-found)))
      ;;else: not an object
      (error (format "py-object-member: invalid type for OBJ parameter (%s)"
                     (type-of obj)))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   in
   ;;lists
   ("3 in range(3)" t)
   ("'s' in range(3)" nil)
   ("'s' in [1,2,'s',3]" t)
   ;;vectors
   ("1 in [3,1,2]" t)
   ("'s' in (4,2,'s','x')" t)
   ("'s' in (4,2,'ss','x')" nil)
   ("[1,2,3][1] in [1,3,[3,2,1]][2]" t)
   ;;strings
   ("'s' in 'string'" t)
   ("'q' in 'string'" nil)
   ("'tri' in 'string'" t)
   ;;objects
   ;;with __contains__
   ("class tst():
   x = [1,2,3,4]
   def __contains__(self,e):
    return e in self.x
  x = tst()"
    ("3 in x" t)
    ("3 not in x" nil)
    ("2 in [x][0]" t))
   ;;with __iter__
   ("class tst2:
   x = [1,2,3,4]
   def __iter__(self):
    self.i = 0
    self.max = len(self.x)
    return self
   def __next__(self):
    i = self.i
    __i = i
    __m = self.max
    if i < self.max:
      ret = self.x[i]
      self.i = i+1
      return ret
    else:
     raise StopIteration
  o = tst2()"
    ("3 in o" t)
    ("33 not in o" t)
    ("3 not in o" nil)
    ("33 in o" nil))
   ;;with __getitem__
   ("class tst3:
   x = [5,6,7,8]
   def __getitem__(self, index):
    if type(index) == int and index >= 0 and index < len(self.x):
     return self.x[index];
    else:
     raise IndexError
  o = tst3()
  x = []
  for i in range(10):
   if i in o:
    x.append(i)"
    ("3 in o" nil)
    ("7 in o" t)
    ("7 not in o" nil)
    ("12 not in o" t)
    ("x" '(5 6 7 8)))
   )
  
  (pyel-create-tests
   not-in
   ;;lists
   ("3 not in range(3)" nil)
   ("'s' not in range(3)" t)
   ("'s' not in [1,2,'s',3]" nil)
   ;;vectors
   ("1 not in [3,1,2]" nil)
   ("'s' not in (4,2,'s','x')" nil)
   ("'s' not in (4,2,'ss','x')" t)
   ("[1,2,3][1] in [1,3,[3,2,1]][2]" nil)
   ;;strings
   ("'s' not in 'string'" nil)
   ("'q' not in 'string'" t)
   ("'tri' not in 'string'" nil)
   ;;objects tests are part of the 'in' tests
   )
  
#+END_SRC
** comprehensions
DOC: generator expressions are translated to list comprehensions
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform comprehension pyel (target iter ifs)
    (lambda (target iter ifs) (pyel-comprehension target iter ifs)))
  
  (defun pyel-comprehension (target iter ifs)
    (cons 'for (cons (using-context 'for-loop-target
                                    (transform target))
                     (cons 'in (cons (transform iter)
                                     (reduce 'append
                                             (mapcar (lambda (x)
                                                       (list 'if (transform x)))
                                                     ifs)))))))
  (def-transform list-comp pyel (elt generators)
    (lambda (elt generators &optional line col)
      (pyel-list-comp elt generators line col)))
  
  
  (defun pyel-list-comp (elt generators &optional line col)
    (let ((ret (cons 'py-list-comp
                     (cons (transform elt)
                           (reduce 'append (mapcar 'transform generators))))))
      (setq return-type 'list)
      ret))
  
  (def-transform dict-comp pyel (key value generators)
    (lambda (key value generators &optional line col)
      (pyel-dict-comp key value generators line col)))
  
  (defun pyel-dict-comp (key value generators &optional line col)
    (let ((ret (append (list 'py-dict-comp (transform key) ': (transform value))
                       (reduce 'append (mapcar 'transform generators)))))
      (setq return-type 'hash)
      ret))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def comprehension(target, iter, ifs):
      return l_str(["comprehension", target, iter, l_str(ifs)])

  def GeneratorExp(elt, generators, lineno='nil', col_offset='nil'):
      return l_str(["list-comp", elt, l_str(generators), lineno, col_offset])

  def ListComp(elt, generators, lineno='nil', col_offset='nil'):
      return l_str(["list-comp", elt, l_str(generators), lineno, col_offset])

  def DictComp(key, value, generators, lineno='nil', col_offset='nil'):
      return l_str(["dict-comp", key, value, l_str(generators), lineno, col_offset])

  def SetComp(elt, generators, lineno='nil', col_offset='nil'):
      return "(unimplemented \"set comprehension\")"
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-parse-comprehension-forms (forms)
    (let (generators target iter ifs e)
      (while forms
        (setq e (car forms)
              forms (cdr forms))
        (cond ((eq e 'for)
               (when target
                 (push (list target iter ifs) generators)
                 (setq target nil
                       iter nil
                       ifs nil))
  
               (setq target (car forms)
                     forms (cdr forms)
                     _in (car forms)
                     forms (cdr forms)
                     iter (car forms)
                     forms (cdr forms))
               (assert (eq _in 'in) "invalid comprehension. expected 'in'")
               (assert (not (null iter)) "invalid comprehension. bad iter "))
              
              ((eq e 'in)
               (error "invalid list comprehension. unexpected 'in'"))
              ((eq e 'if)
               (assert (car forms) "invalid list comprehension. bad condition")
               (push (car forms) ifs)
               (setq forms (cdr forms)))))
      (if target
          (cons (list target iter ifs) generators)
        generators)))
  
  (defmacro py-list-comp (elt &rest body)
    "elements of BODY have the structure:
     for <var> in <iterator>
  or:
     if <conditions>
  with the same semantics as python list comprehensions"
    (let ((generators (py-parse-comprehension-forms body))
          code)
      (setq code `(push ,elt __pyel_lst__))
      (while generators
        (setq ifs (car generators)
              target (nth 0 ifs)
              iter (nth 1 ifs)
              ifs (nth 2 ifs)
              generators (cdr generators))
        (when ifs
          (setq code `(if ,(if (> (length ifs) 1)
                               (cons 'and ifs)
                             (car ifs))
                          ,code)))
        (setq code
              `(let ((__tmp_lst__ (py-list ,iter)))
                 (while __tmp_lst__
                   (setq ,target (car __tmp_lst__)
                         __tmp_lst__ (cdr __tmp_lst__))
                   ,code))))
      `(let (__pyel_lst__)
         ,code
         (reverse __pyel_lst__))))
  
  
  (defmacro py-dict-comp (key : val &rest body)
    (let ((generators (py-parse-comprehension-forms body))
          code)
      (setq code `(puthash ,key ,val __pyel_dict__))
      (while generators
        (setq ifs (car generators)
              target (nth 0 ifs)
              iter (nth 1 ifs)
              ifs (nth 2 ifs)
              generators (cdr generators))
        (when ifs
          (setq code `(if ,(if (> (length ifs) 1)
                               (cons 'and ifs)
                             (car ifs))
                          ,code)))
        (setq code
              `(let ((__tmp_lst__ (py-list ,iter)))
                 (while __tmp_lst__
                   (setq ,target (car __tmp_lst__)
                         __tmp_lst__ (cdr __tmp_lst__))
                   ,code))))
      `(let ((__pyel_dict__ (make-hash-table :test 'equal)))
         ,code
         __pyel_dict__)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   list-comprehensions
   ("[x*x for x in range(10)]" '(0 1 4 9 16 25 36 49 64 81))
   ("[x*x for x in range(10) if x > 5]" '(36 49 64 81))
   ("[x*x for x in range(10) if x > 5 if x < 8]" '(36 49))
  
   ("[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]" '([1 3] [1 4] [2 3] [2 1] [2 4] [3 1] [3 4]))
  
   ("matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],]
  transposed = []
  for i in range(4):
   transposed.append([row[i] for row in matrix])"
    
    ("[[row[i] for row in matrix] for i in range(4)]"
     '((1 5 9) (2 6 10) (3 7 11) (4 8 12)))
    ("transposed" '((1 5 9) (2 6 10) (3 7 11) (4 8 12))))
  
   ("[x for x in range(30) if x %2 == 0]"
    '(0 2 4 6 8 10 12 14 16 18 20 22 24 26 28))
   ("[[x,y] for x in range(5) for y in range(3)]" '((0 0) (0 1) (0 2) (1 0) (1 1) (1 2) (2 0) (2 1) (2 2) (3 0) (3 1) (3 2) (4 0) (4 1) (4 2)))
   ("[[x,y,z] for x in range(3) for y in range(3) for z in range(2)]"
    '((0 0 0) (0 0 1) (0 1 0) (0 1 1) (0 2 0) (0 2 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1) (1 2 0) (1 2 1) (2 0 0) (2 0 1) (2 1 0) (2 1 1) (2 2 0) (2 2 1)))
   ("[[x,y,z] for x in range(5) if x == 3 for y in range(3) for z in range(2)]"
    '((3 0 0) (3 0 1) (3 1 0) (3 1 1) (3 2 0) (3 2 1)))
   ("[[x,y,z] for x in range(5) for y in range(3) for z in range(2) if (x+y+z)%2 == 0]" '((0 0 0) (0 1 1) (0 2 0) (1 0 1) (1 1 0) (1 2 1) (2 0 0) (2 1 1) (2 2 0) (3 0 1) (3 1 0) (3 2 1) (4 0 0) (4 1 1) (4 2 0)))
   ("[x+y for x in range(7) for y in range(9) if x%2==0 if y > 6]"
    '(7 8 9 10 11 12 13 14)))
  
  
  (pyel-create-tests
   dict-comprehensions
   ("str({x:x*x for x in range(5)})" "{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}")
   ("x = {x: [y*y for y in range(x)] for x in range(20)}"
    ("hash_table_count(x)" 20)
    ("x[3],x[5],x[10]" [(0 1 4) (0 1 4 9 16) (0 1 4 9 16 25 36 49 64 81)])))
#+END_SRC

** boolop, and, or
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform boolop pyel (op values)
    (lambda (op values &optional line col)
      (cons op (mapcar 'transform values))))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-or (&rest conditions)
    (cons 'or (mapcar (lambda (x)
                        `(let ((__val ,x))
                           (if (py-bool __val) __val)))
                      conditions)))
  
  (defmacro py-and (&rest conditions)
    (cons 'and (mapcar (lambda (x)
                         `(let ((__val ,x))
                            (if (py-bool __val) __val)))
                       conditions)))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def BoolOp(op, values, lineno='nil', col_offset='nil'):
      return l_str(["boolop", op, l_str(values), lineno, col_offset])
  def Or():
      return "py-or"
  def And():
      return "py-and"
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   boolop
   ("a = True
  b = False"
    ("a or b" t)
    ("False or b or True" t)  
    ("a and b" nil)
    ("False or 'a'" "a"))
   
   ("False or False" nil)
   ("3 and 's'" "s")
   ("1 and 2 and 3" 3)
   ("False or []" nil)
   ("False or ()" [])
   ("'' or 's'" "s")
   ("{} or 3" 3)
   ("d and ''" nil))
#+END_SRC
** pass
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform pass pyel ()
    (lambda (&optional line col) nil))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Pass(lineno=None, col_offset=None):
      return l_str(["pass", lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** unimplemented
This will help notify the user when some syntax has not been implemented
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform unimplemented pyel (name)
    (lambda (name)
      (pyel-notify-error "Set comprehensions have not been implemented")
      pyel-error-string))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** conditional expressions
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform if-exp pyel ()
    (lambda (test body orelse &optional line col)
      (pyel-if test (list body) (list orelse) line col)))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def IfExp (test, body, orelse, lineno='nil', col_offset='nil'):
      return l_str(["if-exp", test, body, orelse, lineno, col_offset])
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   conditional-expressions
   ("1 if True else 0" 1)
   ("len((2,3)) if 's' else 3" 2)
   ("2 if '' else 4" 4))
#+END_SRC
** raise
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (def-transform raise pyel ()
    (lambda (exc cause &optional line col)
      `(py-raise ,(transform exc)))) ;;TODO: ignoring cause
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Raise(exc, cause, lineno='nil', col_offset='nil'):
      return l_str(["raise", exc, cause or "nil", lineno, col_offset])
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-raise (exc &optional cause)
    "signal an error with the name of EXC, an class/object
  EXC must be derived from BaseException"
    (if (py-object-p exc)
        ;;TODO: after `issubclass' is implemented
        ;; (or (and (py-class-p exc)
        ;;          (issubclass exc BaseException))
        ;;     (and (py-instance-p exc)
        ;;          (issubclass (py-type exc) BaseException)))
        (let ((name (getattr exc --name--))
              (args (getattr exc args)))
          (signal (intern name) (format "%s: %s" name (if (= (length args) 1)
                                                          (aref args 0)
                                                        args))))
      (signal 'TypeError "TypeError: exceptions must derive from BaseException")))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** transform template
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
;;
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py

#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

* function transforms
** simple name changes
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-translate-function-name 'map 'mapcar)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

** len
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'len 'number)
  (pyel-func-transform len (thing)
                       (hash)   -> (hash-table-count thing)
                       (object) -> (call-method thing --len--)
                       (_)      -> (length thing))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   len-function
   ("a = [1,2,3,'5']
  b = []
  c = 'str'
  d = (1,2,3,4)"
    ("len(a)" 4)
    ("len(b)" 0)
    ("len(c)" 3)
    ("len(d)" 4))
   ("len('')" 0)
   ("len([3,4])" 2)
   ("len({1:'one', 2:'two'})" 2)
   )
#+END_SRC
** range, xrange
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'range 'list)
  (pyel-declare-el-func-fn 'xrange 'list)
  (pyel-translate-function-name 'range 'py-range)
  (pyel-translate-function-name 'xrange 'py-range)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-range (start &optional end step)
   (unless end
    (setq end start
     start 0))
   (number-sequence start (1- end) step))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   range-function
   ("range(5)" '(0 1 2 3 4))
   ("range(2,7)" '(2 3 4 5 6))
   ("range(2,20,3)" '(2 5 8 11 14 17))
   ("xrange(2,20,3)" '(2 5 8 11 14 17)))
#+END_SRC
** input
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'input 'string)
  (pyel-translate-function-name 'input 'read-string)
#+END_SRC
** list
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'list 'list)
  
  (defalias 'py-list 'pyel-list-function31)
  (pyel-func-transform list (object)
                       (string) -> (py-string-to-list object)
                       (object) -> (py-object-to-list object)
                       (vector) -> (py-vector-to-list object)
                       (hash)   -> (py-hash-to-list object)
                       (list)   -> (py-copy-list object))
  
  (let ((current-transform-table (get-transform-table 'pyel))
        (type-env (pyel-make-type-env pyel-global-type-env)))
    (call-transform (pyel-func-transform-name 'list) nil))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst py-string-to-list (string)
    (mapcar 'byte-to-string string))
  
  (defsubst py-vector-to-list (vector)
    (mapcar 'identity vector))
  
  (defsubst py-copy-list (list)
    (copy-list list))
  
  (defun py-object-to-list (object)
    "assumes that py-object(OBJECT) == t"
    (let ((iter (condition-case nil
                    (call-method object --iter--)
                  (AttributeError
                   (py-raise (TypeError (format
                                         "'%s' object is not iterable"
                                         (getattr object --name--)))))))
          list)
      (condition-case nil
          (while t
            (setq list (cons (call-method iter --next--) list)))
        (StopIteration (reverse list)))))
  
  (defun py-hash-to-list (hash)
    (let (keys)
      (maphash (lambda (key value)
                 (setq keys (cons key keys))) hash)
      keys))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   list-function
   ("list('string')" '("s" "t" "r" "i" "n" "g"))
   ("list([1,2,'3',(2,)])" '(1 2 "3" [2]))
   ("a = [1]
  b = [a,1]
  c = list(b)"
    ("c is b" nil)
    ("c == b" t)
    ("c[0] is a" t))
  
   ("a = [1]
  b = (a, 1)
  c = list(b)"
    ("c" '((1) 1))
    ("c[0] is a" t))
  
   ("list({1:'one', 2:'two', 3:'three'})" '(3 2 1))
  
   ("list((1,2,3))" '(1 2 3))
   
   ("s = '123'
  l = [1,2,3]
  tu = (1,2,3,)
  d = {1:'1',2:'2',3:'3'}"
    ("list(s)" '("1" "2" "3"))
    ("list(l)" '(1 2 3))
    ("list(tu)" '(1 2 3))
    ("list(d)" '(3 2 1)))
   
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()"
    ("list(obj)" '("5" "4" "3" "2" "1"))))
#+END_SRC
** tuple
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'tuple 'vector)
  (pyel-func-transform tuple (object)
                       (string) -> (py-string-to-vector object)
                       (list)   -> (py-list-to-vector object)
                       (vector) -> (py-copy-vector object)
                       (hash)   -> (py-hash-to-vector object)
                       (object) -> (py-object-to-vector object))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;; (defun py-string-to-vector (string)
  ;;   "Return a vector of characters in STRING."
  ;;   (vconcat (mapcar 'byte-to-string string)))
  (defun py-string-to-vector (string)
    "Return a vector of characters in STRING."
    (let* ((v (make-vector (length string) 0))
           (i -1))
      (mapc (lambda (x)
              (aset v (setq i (1+ i)) (byte-to-string x)))
            string)))
  
  (defsubst py-list-to-vector (list)
    "return a vector v such that (aref v i) = (nth i LIST)"
    (vconcat list))
  
  (defsubst py-copy-vector (vec)
    (vconcat vec))
  
  (defun py-hash-to-vector (ht)
    (let ((v (make-vector (hash-table-count ht) 0))
          (i -1))
      (maphash (lambda (key value)
                 (aset v (setq i (1+ i)) key))
               ht)
      v))
  
  (defsubst py-object-to-vector (object)
    (vconcat (py-object-to-list (object))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   tuple-function
   ("tuple('string')" ["s" "t" "r" "i" "n" "g"])
   ("tuple([1,2,'3',(2,)])" [1 2 "3" [2]])
   ("a = [1]
  b = [a,1]
  c = list(b)"
    ("c is b" nil)
    ("c == b" nil)
    ("c[0] is a" t))
   
   ("a = [1]
  b = (a, 1)
  c = tuple(b)"
    ("c" '((1) 1))
    ("c[0] is a" t))
  
   ("a = (1,)
  b = [a, 1]
  c = tuple(b)"
    ("c" '[[1] 1])
    ("c[0] is a" t))
   
   ("tuple({1:'one', 2:'two', 3:'three'})" '[3 2 1])
   
   ("tuple((1,2,3))" [1 2 3])
   
   ("s = '123'
  l = [1,2,3]
  tu = (1,2,3,)
  d = {1:'1',2:'2',3:'3'}"
    ("tuple(s)" ["1" "2" "3"])
    ("tuple(l)" [1 2 3])
    ("tuple(tu)" [1 2 3])
    ("tuple(d)" [3 2 1]))
   
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()"
    ("list(obj)" '["5" "4" "3" "2" "1"])))
#+END_SRC
** hasattr
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'hasattr 'bool)
  (pyel-translate-function-name 'hasattr 'obj-hasattr)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** isinstance
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'isinstance 'bool)
  (pyel-translate-function-name 'isinstance 'obj-isinstance)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
** str
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-func-transform str (thing)
                       ;; if any of these cases are added/removed,
                       ;; the 'pyel-str-function' alias will
                       ;; need to be updated 
                       (number) -> (number-to-string thing)
                       (string) -> (format "\"%s\"" thing)
                       (function) -> (py-function-str thing)
                       (list) -> (py-list-str thing)
                       (object) -> (call-method thing --str--)
                       (vector) -> (py-vector-str thing)
                       (hash) -> (py-hash-str thing)
                       (symbol) -> (symbol-name thing))
  
  (pyel-translate-function-name 'str 'pyel-str)
  (pyel-declare-el-func-fn 'str 'string)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun _py-str-sequence (seq)
    "convert SEQ to a string of its python representation
      does not include starting/ending parens or brackets"
    (if (vectorp seq)
        (setq seq (mapcar 'identity seq)))
    (if (listp seq)
        (if (listp (cdr seq))
            (mapconcat (lambda (x) (pyel-str-function x)) seq ", ")
          ;;cons cell
          ;;this should not happend from normal python
          ;;but maybe when interacting with lisp code from python
          (concat (pyel-str-function (car seq))
                  " . "
                  (pyel-str-function (cdr seq))))
      (error "invalid type")))
  
  (defun py-list-str (thing)
    (concat "[" (_py-str-sequence thing) "]"))
  (defun py-vector-str (thing)
    (concat "(" (_py-str-sequence thing) ")"))
  
  (defun py-function-str (func)
    "return a string representation of function FUNC"
    (let* ((obj (bound-method-p func))
           (obj-name (if obj (-to_ (getattr obj --name--)) nil)))
      (if obj
          (format "<bound method %s.%s of %s object at 0x18b071>"
                  obj-name
                  (-to_ (bound-method-name func))
                  obj-name)
        (format "<function %s at 0x18b071>" (if (and (listp func)
                                                     (or (eq (car func) 'lambda)
                                                         (eq (car func) 'closure)))
                                                "<lambda>"
                                              (symbol-name func))))))
  
  (defun py-hash-str (ht)
    (let (str)
      (maphash (lambda (key value)
                 (push (concat (pyel-str-function key)
                               ": "
                               (pyel-str-function value))
                       str))
               ht)
  
      (concat "{" (mapconcat 'identity (reverse str) ", ") "}")))
  
  
  
  ;;temp fix for 'str' transform
  ;;The problem:
  ;; cannot pass a function name to the str transform because the tranform
  ;; treats it like a variable and it may not be bound as a variable
  ;;*This should be temporary because the 'str' transform cannot
  ;;expand this way to take advantage of known types
  (defmacro pyel-str (thing)
    (if (symbolp thing)
        (if (boundp thing)
            (list 'pyel-str-function thing)
          (if (functionp thing)
              `(py-function-str (quote ,thing))
            thing;;error
            ))
      (list 'pyel-str-function thing)))
  
  ;;Currently, the repr and str transforms are not not called directly
  ;;so they never have the chance to expand.
  ;;The expanded functions are used so we force expansion here.
  ;;required functions are `pyel-str-function' and `pyel-repr-function'
  ;;;NOTE: this is also done in `pyel-run-tests'
  (let ((current-transform-table (get-transform-table 'pyel))
        (type-env (pyel-make-type-env pyel-global-type-env)))
    (call-transform (pyel-func-transform-name 'repr) nil)
    (call-transform (pyel-func-transform-name 'str) nil))
  
  ;;The transform now outputs macros unique to the known types
  ;;if the type cases are changed in the 'str' or 'repr' transforms
  ;;these aliases will need to be updated
  (defalias 'pyel-str-function 'pyel-str-function255)
  (defalias 'pyel-repr-function 'pyel-repr-function255)
  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   str
   ("str('somestring')" "\"somestring\"")
   ("str(\"'dstring'\")" "\"'dstring'\"")
   ("str(342)" "342")
   ("x = [1,2,'hi']
  str(x)"
    "[1, 2, \"hi\"]")
   ("x = (1,'two',3)
  str(x)"
    "(1, \"two\", 3)")
   ("x = {1: 'one', 5: 'five', 12: 'telve'};
  str(x)"
    "{1: \"one\", 5: \"five\", 12: \"telve\"}")
   ("f = lambda : False
  str(f)"
    "<function <lambda> at 0x18b071>")
   ("def __ff_(): pass
  str(__ff_)"
    "<function --ff- at 0x18b071>")
  
   ("class strtest:
   def __init__ (self, n):
    self.x = n
   def __str__(self):
    return 'str' + str(self.x)
  obj = strtest(4)"
   ("str(obj)" "str4")))
#+END_SRC
** repr
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-func-transform repr (thing)
                       ;; if any of these cases are added/removed,
                       ;; the 'pyel-repr-function' alias will
                       ;; need to be updated (in str transform section)
                       (number) -> (number-to-string thing)
                       (string) -> (py-repr-string thing)
                       (function) -> (py-function-str thing)
                       (symbol) -> (pyel-symbol-str thing)
                       (list) -> (py-list-repr thing)
                       (object) -> (call-method thing --repr--)
                       (vector) -> (py-vector-str thing)
                       (hash) -> (py-hash-str thing))
  
  (pyel-translate-function-name 'repr 'pyel-repr)
  (pyel-declare-el-func-fn 'repr 'string)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;;temp function. see `pyel-str' for details
  (defmacro pyel-repr (thing)
    (if (stringp thing)
        (py-repr-string (py-repr-string thing))
      (if (symbolp thing)
          (if (boundp thing)
              (list 'pyel-repr-function thing)
            (if (functionp thing)
                `(py-function-str (quote ,thing))
              thing;;error
              ))
        (list 'pyel-repr-function thing))))
  
  (defsubst py-repr-string (thing)
    (prin1-to-string thing))
  
  (defun _py-repr-sequence (seq)
    (mapconcat (lambda (x) (pyel-repr-function x)) seq ", "))
  
  (defun py-list-repr (thing)
    (concat "[" (_py-repr-sequence thing) "]"))
  
  (defun py-vector-repr (thing)
    (concat "(" (_py-repr-sequence thing) ")"))
  
  (defun pyel-symbol-str (sym)
    (if (eq sym t)
        "True"
      (case sym
        (nil "False")
        (integer "<class 'int'>")
        (float "<class 'float'>")
        (string "<class 'str'>")
        (cons "<class 'list'>")
        (vector "<class 'tuple'>")
        (hash-table "<class 'dict'>")
        (t (symbol-name sym)))))
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   repr
   ("repr('somestring')" "\"\\\"somestring\\\"\"")
   ("repr(342)" "342")
   ("x = [1,2,'hi']
  repr(x)"
    "[1, 2, \"hi\"]")
   ("x = (1,'two',3)
  repr(x)"
    "(1, \"two\", 3)")
   ("x = {1: 'one', 5: 'five', 12: 'telve'}
  repr(x)"
    "{1: \"one\", 5: \"five\", 12: \"telve\"}")
   ("f = lambda : False
  repr(f)"
    "<function <lambda> at 0x18b071>")
   ("def __ff_(): pass
  repr(__ff_)"
    "<function --ff- at 0x18b071>"))
#+END_SRC
** hex
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'hex 'string)
  (pyel-translate-function-name 'hex 'py-hex)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-hex (n)
    (format "0x%x" n))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   hex-function
   ("hex(23)" "0x17")
   ("hex(123232332)" "0x758604c"))
#+END_SRC
** bin
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'bin 'string)
  (pyel-translate-function-name 'bin 'py-bin)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-bin (n) ;;Is there really no built in way to do this???
    (let (bin)
      (while (not (= n 0))
        (push (number-to-string (% n 2)) bin)
        (setq n (/ n 2)))
      (mapconcat 'identity (cons "0b" bin) "")))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   bin-function
   ("bin(123)" "0b1111011")
   ("bin(3456312)" "0b1101001011110100111000"))
#+END_SRC
** print
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  ;;(pyel-translate-function-name 'print 'py-print)
  (pyel-declare-el-func-fn 'print 'bool)
  (pyel-define-function-translation
   print
   `(py-print ,(cadr (assoc 'sep kwargs))
              ,(cadr (assoc 'end kwargs))
              nil ;;TODO: file=sys.stdout
              ,@args))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defvar pyel-print-function 'prin1
    "function that is used for printing by `py-print'")

  (defun py-print (sep end file &rest args)
    (let ((sep (or sep " "))
          (end (or end "\n")))
      (progn (mapc (lambda (x)
                     (funcall pyel-print-function x)
                     (funcall pyel-print-function sep))
                   (butlast args))
             (funcall pyel-print-function (car (last args)))
             (funcall pyel-print-function end)
             nil)))
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py
  def Print(dest, values, nl, lineno='nil', col_offset='nil'):
      print("Error: using python2, upgrade to python3")
      exit()
        
#+END_SRC  
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

** pow
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'pow 'number)
  (pyel-define-function-translation
   pow
   (case (length args)
     (3 (list 'mod (list 'expt (car args) (cadr args)) (caddr args)))
     (2 (list 'expt (car args) (cadr args)))
     (t "ERROR") ;;TODO
     ))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   pow-function
   ("pow(2,5,5)" 2)
   ("pow(3,7,20)" 7)
   ("pow(3,7)" 2187)
   ("pow(2,2)" 4)
   ("pow(10,-2)" 0.01))
#+END_SRC
** eval
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'eval 'list)
  (pyel-translate-function-name 'eval 'py-eval)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-eval (source &rest others)
    (cond (others
           (error "Only the first arg in `py-eval' is implemented"))
          ((stringp source)
           (pyel-eval source))
          ((listp source)
           (let ((lexical-binding t))
             (eval source)))
      (t (py-raise (TypeError "eval() arg 1 must be a string, bytes or code object)")))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests 
   eval
   ("x = 23
  a = 1
  b = 4
  s = 'a+b'"
    ("eval('x')" 23)
    ("eval(s)" 5)))
#+END_SRC
** type
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  ;;(pyel-declare-el-func-fn 'type 'symbol)
  (pyel-translate-function-name 'type 'py-type)
  
  (pyel-translate-variable-name 'int 'py-int)
  (pyel-translate-variable-name 'float 'py-float)
  (pyel-translate-variable-name 'tuple 'py-tuple)
  (pyel-translate-variable-name 'dict 'py-dict)
  (pyel-translate-variable-name 'list 'py-list)
  (pyel-translate-variable-name 'string 'py-string)
  (pyel-translate-variable-name 'bool 'py-bool)
  (pyel-translate-variable-name 'type 'py-type)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;;TODO: when the built in type classes are finished and `py-type'
  ;;      returns them, the special cases for the types in
  ;;      `pyel-symbol-str' should be removed
  
  (defun py-type (object)
    (cond ((integerp object)
           py-int)
          ((floatp object)
           py-float)
          ((listp object)
           py-list)
          ((stringp object)
           py-string)
          ((py-instance-p object)
           (aref object obj-class-index)) ;;?
          ((py-class-p object)
           py-type)
          ((vectorp object)
           py-tuple)
          ((hash-table-p object)
           py-dict)
          ((or (eq object t)
               (eq object nil))
           py-bool)
          (t (type-of object))))
  
  ;;symbols that represent built in python types
  (defconst py-int 'int)
  (defconst py-float 'int)
  (defconst py-tuple 'tuple)
  (defconst py-dict 'dict)
  (defconst py-list 'list)
  (defconst py-str  'string)
  (defconst py-string 'string)
  (defconst py-bool 'bool)
  (defconst py-type 'type)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   type
   ("type(t)" "<class 'bool'>")
   ("type(3)" py-int)
   ("type(3.3)" py-float)
   ("type('3')" py-string)
   ("type([3])" py-list)
   ("type((3,))" py-tuple)
   ("type({3:'3'})" py-list)
   ("type('s') == str" t)
   ("type((3,)) == list" nil)
   ("class testc: pass
  x = testc()
  y = type(x)"
    ("repr(type(x))" "<class 'testc'>")
    ("y is testc" t)))
#+END_SRC
** abs
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'abs 'number)
                           
  (pyel-func-transform abs (object)
                       (number) -> (abs object)
                       (object) -> (call-method object --abs--))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   abs-function
   ("abs(3)" 3)
   ("abs(-3)" 3)
   ("class C:
   def __abs__(self):
    'doc'
    return 'hi'
  obj = C()"
    ("abs(obj)" "hi")))
#+END_SRC  
** chr
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'chr 'string)
  (pyel-translate-function-name 'chr 'py-chr)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst py-chr (i)
    (byte-to-string i))
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   chr-function
   ("chr(70)" "F")
   ("chr(50)" "2"))
#+END_SRC

** ord
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'ord 'number)
  (pyel-translate-function-name 'ord 'py-ord)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst py-ord (i)
    (string-to-char i))
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   ord-function
   ("ord('F')" 70)
   ("ord('2')" 50))
#+END_SRC
** exit
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'chr nil)
  (pyel-translate-function-name 'exit 'pyel-exit)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
(defun pyel-exit ()
  (if pyel-interactive
      (progn (kill-buffer)
	     (message "you killed ipyel")
	     (throw 'ipyel-quit nil))
    ;;(kill-emacs)
    (save-buffers-kill-terminal)))
#+END_SRC
** int
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'int 'number)
  (pyel-func-transform int (object)
                       (string) -> (py-str-to-int object)
                       (number) -> (py-number-to-int object)
                       (object) -> (call-method object --int--))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-number-to-int (num)
    (if (numberp num)
        (floor num)
      (list 'floor num)))
  
  (defmacro py-str-to-int (str)
    (if (stringp str)
        (floor (string-to-int str))
      `(floor (string-to-int ,str))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   int-function
   ("int('34')" 34)
   ("int('3.3')" 3)
   ("int(2)" 2)
   ("int(23.2)" 23)
   ("x = '3'
  y = ['4']
  z = 2
  a = 3.3"
    ("int(x)" 3)
    ("int(y[0])" 4)
    ("int(z)" 2)
    ("int(a)" 3))
   ("class test:
   def __int__(self):
    return 342
  o = test()"
    ("int(o)" 342)))
#+END_SRC

** float
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'float 'number)
  (pyel-func-transform float (object)
                       (string) -> (py-str-to-float object)
                       (number) -> (py-number-to-float object)
                       (object) -> (call-method object --float--))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-number-to-float (num)
    (if (numberp num)
        (* 1.0 num)
      (list '* 1.0 num)))
  
  (defmacro py-str-to-float (str)
    (if (stringp str)
        (* 1.0 (string-to-int str))
      (list '* 1.0 (list 'string-to-int str))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   float-function
   ("float('34')" 34.0)
   ("float('3.3')" 3.3)
   ("float(2)" 2.0)
   ("float(23.2)" 23.2)
   ("x = '3.1'
  y = ['4']
  z = 2
  a = 3.3"
    ("float(x)" 3.1)
    ("float(y[0])" 4.0)
    ("float(z)" 2.0)
    ("float(a)" 3.3))
   ("class test:
   def __float__(self):
    return 342.1
  o = test()"
    ("float(o)" 342.1)))
#+END_SRC

** dict
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'dict 'hash)
  
  (pyel-func-kwarg-transform dict (kwargs)
                             (_)-> (pyel-alist-to-hash2 'kwargs))
  
  (pyel-func-transform dict (&optional object)
                       (list)   -> (pyel-list-to-dict object)
                       (object) -> (pyel-object-to-dict object)
                       (vector) -> (pyel-vector-to-dict object)
                       (hash)   -> (copy-hash-table object))
  
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-alist-to-hash2 (alist)
    "like `pyel-alist-to-hash' but elements have the form (a b)"
    (let ((ht (make-hash-table
               :test 'equal
               :size (length alist))))
      (mapc (lambda (x)
              (puthash (car x) (cadr x) ht))
            alist)
      ht))
  
  (defun pyel-list-to-dict (list)
    (let ((h (make-hash-table :test pyel-dict-test)) ;;default length??
          key-val)
      (mapcar (lambda (elem)
                (setq key-val (pyel-get-key-val elem))
                (puthash (car key-val) (cdr key-val) h))
              list)
      h))
  
  (defun pyel-vector-to-dict (vec)
    (let ((h (make-hash-table :test pyel-dict-test)) ;;default length??
          key-val)
      (dotimes (i (length vec))
        (setq key-val (pyel-get-key-val (aref vec i)))
        (puthash (car key-val) (cdr key-val) h))
      h))
  
  (defun pyel-object-to-dict (object)
    (let ((iter (condition-case nil
                    (call-method object --iter--)
                  (AttributeError
                   (py-raise (TypeError (format
                                         "'%s' object is not iterable"
                                         (getattr object --name--)))))))
          (ht (make-hash-table :test pyel-dict-test))
          key-val)
      (condition-case nil
          (while t
            (setq key-val (pyel-get-key-val (call-method iter --next--)))
            (puthash (car key-val) (cdr key-val) ht))
        (StopIteration ht))))
  
  (defun pyel-get-key-val (obj)
    "(k . v) -> (k . v)
  \(k v) -> (k . v)
  \[k v] -> (k . v)
  'kv' -> ('k' . 'v')"
    (cond ((listp obj)
           (if (listp (cdr obj))
               (if (= (length obj) 2)
                   (cons (car obj) (cadr obj))
                 (error "invalid length"))
             obj))
          ((vectorp obj)
           (if (= (length obj) 2)
               (cons (aref obj 0) (aref obj 1))
             (error "invalid length")))
          ((stringp obj)
           (if (= (length obj) 2)
               (cons (subseq obj 0 1) (subseq obj 1 2))
             (error "invalid length")))
          (t (error "invalid type"))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   dict-function
   ("repr(dict())" "{}")
   ("repr(dict(__a = 1,__b = 2,__c = 4))" "{--a: 1, --b: 2, --c: 4}")
   ("repr(dict([('a',3),('b', 5),('c',8)]))" "{\"a\": 3, \"b\": 5, \"c\": 8}")
   ("repr(dict((('a',3),('b', 5),('c',8))))" "{\"a\": 3, \"b\": 5, \"c\": 8}")
   ("a = [('ab'),['b', 5],('c',8)]
  x = dict(a)"
    ("repr(x)" "{\"a\": \"b\", \"b\": 5, \"c\": 8}"))
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = self.x
     self.x -= 1
     return ret, ret**2
    raise StopIteration
  o = a()
  x = dict(o)"
    ("repr((x))" "{5: 25, 4: 16, 3: 9, 2: 4, 1: 1}")))
#+END_SRC

** round
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'round 'number)
  (pyel-translate-function-name 'round 'py-round)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-round (number &optional ndigits)
    (if ndigits
        (round-float number ndigits)
      (round number)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   round-function
   ("round(342.234)" 342)
   ("round(342.834)" 343)
   ("round(342.834,1)" 342.8)
   ("round(342.834,2)" 342.83))
#+END_SRC
** enumerate
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'enumerate 'list)
  
  (pyel-func-transform enumerate (obj &optional start)
                       (list)   -> (py-enumerate-list obj start)
                       (string) -> (py-enumerate-string obj start)
                       (object) -> (py-enumerate-object obj start)
                       (vector) -> (py-enumerate-vector obj start)
                       (hash)   -> (py-enumerate-hash obj start))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-enumerate-list (list &optional start)
    (let ((ret nil)
          (c (or start 0)))
      (while list
        (setq ret (cons (list c (car list)) ret)
              list (cdr list)
              c (1+ c)))
      (reverse ret)))
  
  (defsubst py-enumerate-vector (vec &optional start)
    (py-enumerate-list (mapcar 'identity vec) start))
  
  (defsubst py-enumerate-hash (hash &optional start)
    (py-enumerate-list (py-hash-to-list hash) start))
  
  (defsubst py-enumerate-string (str &optional start)
    (py-enumerate-list (split-string str "" :omit-nulls) start))
  
  (defsubst py-enumerate-object (obj &optional start)
    (py-enumerate-list (py-object-to-list obj) start))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   enumerate-function
   ("enumerate(['a','b','c'])" '((0 "a") (1 "b") (2 "c")))
   ("enumerate(('a','b','c'))" '((0 "a") (1 "b") (2 "c")))
   ("enumerate('abc')" '((0 "a") (1 "b") (2 "c")))
   ("enumerate(['a','b','c'],10)" '((10 "a") (11 "b") (12 "c")))
   ("enumerate(('a','b','c'),10)" '((10 "a") (11 "b") (12 "c")))
   ("enumerate('abc',10)" '((10 "a") (11 "b") (12 "c")))
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()"
    ("enumerate(obj)" '((0 "5") (1 "4") (2 "3") (3 "2") (4 "1")))))
#+END_SRC

** interactive
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'interactive nil)
  
  (pyel-define-function-translation
   interactive
   (setq interactive t))
#+END_SRC
** divmod
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'divmod 'vector)
  
  (pyel-func-transform divmod (x y)
                       (_ float)
                       (_ float) -> (py-divmod-f x y)
                       (int int) -> (py-divmod-i x y)
                       (_ _)     -> (py-divmod x y))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-divmod (x y)
    "(divmod X Y) -> [div mod]
  
  Return the vector [(X-X%Y)/Y, X%Y].  Invariant: div*Y + mod == X."
    (cond ((and (numberp x)
                (numberp y))
           (vector (if (or (floatp x)
                           (floatp y))
                       (ffloor (/ x y)) (floor (/ x y)))
                   (mod x y)))
          ((or (floatp x) (floatp y))
           `(vector (ffloor (/ ,x ,y)) (mod ,x ,y)))
          (t `(vector (if (or (floatp ,x)
                              (floatp ,y))
                          (ffloor (/ ,x ,y))
                        (floor (/ ,x ,y)))
                      (mod ,x ,y)))))
  
  (defsubst py-divmod-f (x y)
    (vector (ffloor (/ x y)) (mod x y)))
  
  (defsubst py-divmod-i (x y)
    (vector (floor (/ x y)) (mod x y)))
#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   divmod-function
   ("divmod(6, 2)" [3 0])
   ("divmod(6, 4)" [1 2])
   ("divmod(6.7, 4)" [1.0 2.7])
   ("divmod(6.712, 4.1)" [1.0 2.612])
   ("divmod(4.2, 2.1)" [2.0 0.0])
   ("divmod(4, 2.1)" [1.0 1.9])
   ("a = 412
  b = 13"
    ("divmod(a, b)" [31 9])))
#+END_SRC

** bool
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'bool 'bool)
  
  (pyel-func-transform bool (object)
                       (object) -> (py-object-bool object)
                       (_) -> (py-bool object))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defsubst py-bool (thing)
    "convert a non-object THING into a boolean value.
  For objects, use `py-object-bool'"
    ;;TODO: this should handled in the type dispatch
    (if (and thing
             (not (or (eq thing 0)
                      (eq thing [])
                      (eq thing "")
                      (and (hash-table-p thing)
                           (= (hash-table-count thing) 0))
                      )))
        t))
  
  (defun py-object-bool (object)
    "return t if an object is considered true, else nil.
  If the object defines the --bool-- method, that is called.
  Otherwise the --len-- method is tried, the result is true
  if the length is greator then one.
  If neither the --bool-- or the --len-- methods are defined,
  all instances are considered true"
    (condition-case nil
        (call-method object --bool--)
      (AttributeError
       (condition-case nil
           (> (call-method object --len--) 0)
         (AttributeError
          t)))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   bool-function
   ("bool(2)" t)
   ("bool(0)" nil)
   ("bool([2,2])" t)
   ("bool([])" nil)
   ("bool((2,))" t)
   ("bool(())" nil)
   ("bool('s')" t)
   ("bool('')" nil)
   ("bool({})" nil)
   
   ;;objects
   ("class a():
   def bool(self):
    return False
  o1 = a()
  class b():
   def __bool__(self):
    return False
  o2 = b()
  class c():
   def __init__(self,n):
    self.len = n
   def __len__(self):
    return self.len
  o3 = c(0)
  o4 = c(3)"
    ("'y' if bool(o1) else 'n'" "y")
    ("'y' if bool(o2) else 'n'" "n")
    ("'y' if bool(o3) else 'n'" "n")
    ("'y' if bool(o4) else 'n'" "y")))
#+END_SRC

** iter
Iterators for built in types are defined in py-iter.py
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'iter 'object)
  
  (pyel-func-transform iter (obj)
                       (list)   -> (py-list-iter obj)
                       (string) -> (py-string-iter obj)
                       (object) -> (py-object-iter obj)
                       (vector) -> (py-vector-iter obj)
                       (hash)   -> (py-hash-iter obj))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-object-iter (object)
    "if OBJECT defines the --iter-- method, call it
  otherwise raise a TypeError."
    (condition-case nil
        (call-method object --iter--)
      (AttributeError
       (py-raise (TypeError (format
                             "'%s' object is not iterable"
                             (getattr object --name--)))))))
  
  (defalias 'py-vector-iter 'py-tuple-iter)
  (defalias 'py-hash-iter 'py-dict-iter)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   iter-function
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()
  it = iter(a)
  x = it.__next__()
  x2 = it.__next__()"
    ("x" "5")
    ("x2" "4"))
   
   ;;built-in types
   ("list_i = iter([1,2,3])
  tuple_i = iter(('1','2','3'))
  string_i = iter('str')
  dict_i = iter({1:'1', '5':5})
  s = 's2'
  string_i2 = iter(s)
  tuple_i2 = iter([[(2,4)]][0][0])
  string_i3 = iter('string')
  tuple_i3 = iter(('1','2','3'))"
    ("next(list_i)" 1)
    ("next(list_i)" 2)
    ("next(tuple_i)" "1")
    ("next(string_i)" "s")
    ("next(string_i)" "t")
    ("next(dict_i)"  "5")
    ("next(string_i2)" "s")
    ("next(tuple_i2)" 2)
    ("[x for x in 'string']" '("s" "t" "r" "i" "n" "g"))
    ("[x for x in {1:'1', '5':5,(2,3):3}]" '([2 3] "5" 1))))
  
#+END_SRC
** next
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'next nil)
  
  (pyel-translate-function-name 'next 'py-next)
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-next (object)
    (let (ret err)
      (if (py-object-p object)
          (condition-case nil
              (setq ret (call-method object --next--))
            (AttributeError (setq err t)))
        (setq err t))
      (if err
          (py-raise (TypeError (format "'%s' object is not an iterator"
                                       (getattr object --name--))))
        ret)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   next-function
   ("class c:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = c()
  it = iter(obj)
  a = next(it)
  b = next(it)"
    ("a" "5")
    ("b" "4")))
#+END_SRC
** all
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'all 'bool)
  
  (pyel-func-transform all (obj)
                       (list)   -> (py-list-all obj)
                       (string) -> (py-string-all obj)
                       (object) -> (py-object-all obj)
                       (vector) -> (py-vector-all obj)
                       (hash)   -> (py-hash-all obj))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-all (list)
    (let ((all t))
      (while list
        (if (not (py-bool (car list)))
            (setq list nil
                  all nil)
          (setq list (cdr list))))
      all))
  
  (defun py-vector-all (vector)
    (let ((all t)
          (len (length vector))
          (i 0))
      (while (< i len)
        (if (not (py-bool (aref vector i)))
            (setq i len
                  all nil)
          (setq i (1+ i))))
      all))
  
  (defsubst py-string-all (obj)
    t) ;;TODO: is this ever not the case?
  
  (defun py-hash-all (ht)
    (catch 'return
      (maphash (lambda (k v)
                 (if (not (py-bool k))
                     (throw 'return nil)))
               ht)
      t))
  
  (defun py-object-all (object)
    (let ((iter (py-object-iter object))
          (all t))
      (condition-case nil
          (while all
            (if (not (py-bool (call-method iter --next--)))
                (setq all nil)))
        (StopIteration nil))
      all))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   all-function
   ;;list
   ("all([1,'s',2,4])" t)
   ("all([1,'s','',4])" nil)
   ("all([])" t)
   ;;vector
   ("all((1,'s',2,4))" t)
   ("all((1,'s',(),4))" nil)
   ("all(())" t)
   ;;string
   ("all('s')" t)
   ("all('')" t)
   ;;hash table
   ("all({2:4, 4:'', 1:'s'})" t)
   ("all({2:4, 4:'', 1:'s', (): 2})" nil)
   ("all({})" t)
   ;;objects
   ("class a:
   x = 5
   def __init__(self, s, e):
    self.start = s
    self.end = e
   def __iter__(self):
    self.current = self.start
    return self
   def __next__(self):
    if self.current < self.end:
     ret = self.current
     self.current += 1
     return ret
    raise StopIteration
  o = a(1,9)
  o2 = a(-3,3)"
    ("all(o)" t)
    ("all(o2)" nil)))
#+END_SRC
** any
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'any 'bool)
  
  (pyel-func-transform any (obj)
                       (list)   -> (py-list-any obj)
                       (string) -> (py-string-any obj)
                       (object) -> (py-object-any obj)
                       (vector) -> (py-vector-any obj)
                       (hash)   -> (py-hash-any obj))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-any (list)
    (let ((not-found t))
      (while list
        (if (py-bool (car list))
            (setq list nil
                  not-found nil)
          (setq list (cdr list))))
      (not not-found)))
  
  (defun py-vector-any (vector)
    (let ((found nil)
          (len (length vector))
          (i 0))
      (while (< i len)
        (if (py-bool (aref vector i))
            (setq i len
                  found t)
          (setq i (1+ i))))
      found))
  
  (defsubst py-string-any (obj)
    (not (eq obj ""))) ;;TODO: is this ever not the case?
  
  (defun py-hash-any (ht)
    (catch 'return
      (maphash (lambda (k v)
                 (if (py-bool k)
                     (throw 'return t)))
               ht)
      nil))
  
  (defun py-object-any (object)
    (let ((iter (py-object-iter object))
          (not-found t))
      (condition-case nil
          (while not-found
            (if (py-bool (call-method iter --next--))
                (setq not-found nil)))
        (StopIteration nil))
      (not not-found)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   any-function
   ;;list
   ("any([[],False, 's',2])" t)
   ("any([[],False, '',0])" nil)
   ("any([])" nil)
   ;;vector
   ("any(([],False, 's',2))" t)
   ("any(([],False, '',0))" nil)
   ("any(())" nil)
   ;;string
   ("any('s')" t)
   ("any('')" nil)
   ;;hash table
   ("any({0:4, 4:'', 1:'s'})" t)
   ("any({0:4, '':3, (): 2})" nil)
   ("any({})" nil)
   ;;objects
   ("class a:
   x = 5
   def __init__(self, s, e):
    self.start = s
    self.end = e
   def __iter__(self):
    self.current = self.start
    return self
   def __next__(self):
    if self.current < self.end:
     ret = self.current
     self.current += 1
     return ret
    raise StopIteration
  o = a(0,9)
  o2 = a(0,0)"
    ("any(o)" t)
    ("any(o2)" nil)))
#+END_SRC
** sum
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'sum 'number)
  
  (pyel-func-transform sum (obj)
                       (list)   -> (py-list-sum obj)
                       (object) -> (py-object-sum obj)
                       (vector) -> (py-vector-sum obj)
                       (hash)   -> (py-hash-sum obj))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-sum (list)
    (let ((not-found t)
          (sum 0))
      (while list
        (setq sum (+ sum (car list))
              list (cdr list)))
      sum))
  
  (defun py-vector-sum (vector)
    (let ((found nil)
          (len (length vector))
          (i 0)
          (sum 0))
      (while (< i len)
        (setq sum (+ sum (aref vector i))
              i (1+ i)))
      sum))
  
  (defun py-hash-sum (ht)
    (let ((sum 0))
      (maphash (lambda (k v)
                 (setq sum (+ sum k)))
               ht)
      sum))
  
  (defun py-object-sum (object)
    (let ((iter (py-object-iter object))
          (sum 0))
      (condition-case nil
          (while t
            (setq sum (+ sum (call-method iter --next--))))
        (StopIteration nil))
      sum))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   sum-function
   ("sum([1,2,3])" 6)
   ("sum([])" 0)
   ("sum((1,2,3))" 6)
   ("sum(())" 0)
   ("sum({1:'3',2:'32'})" 3)
   ("sum({})" 0)
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret =self.x
     self.x -= 1
     return ret
    raise StopIteration
  o = a()"
    ("sum(o)" 15)))
#+END_SRC
** hash
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-declare-el-func-fn 'hash 'number)
  (pyel-func-transform hash (obj)
                       (object) -> (call-method obj --hash--)
                       (_) -> (sxhash obj))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
   (pyel-create-tests
  hash
  ("hash(3)" 3)
  ("hash('3')" 63)
  ("class a:
   x = 5
   def __hash__(self):
    return 1234
  o = a()"
   ("hash(o)" 1234)))
  
#+END_SRC
** transform template
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
;;
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py

#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

* method transforms
** doc  TODO: move or tangle
*Defining method transforms for build in types*
In python, primitive types such as the list and string are objects. We need to
define the way in which these method calls will be transformed to function
calls in Emacs Lisp. The macro `pyel-method-transform' is used for this.

Definition form is just like normal type transforms, with a few extra requirement
- The first argument is the potential object
- these transforms are responsible for creating the normal
  method call if obj is actually an object and not a primative type
  this must always be last with the type selector (_ _ ... _ ) ->
Example:
#+Begin_SRC emacs-lisp
  (pyel-method-transform append (obj thing)
                         (list _) -> (py-list-append obj thing)
                         (_ _)    -> (call-method obj append thing))
#+End_Src
** append
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform append (obj thing)
                         (list _) -> (py-append $obj thing)
                         (_ _)    -> (call-method obj append thing))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-append (list thing)
    "add THING to the end of LIST"
    (while (not (null (cdr list)))
      (setq list (cdr list)))
    (setcdr list (list thing)))
  
  (defmacro py-append (list thing)
    "add THING to the end of LIST"
    `(cond ((null ,list) (setq ,list (list ,thing)))
           (t (py-list-append ,list ,thing))))
  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   append
   ("a = [1,2,3]
  c = ['a','a']
  b = a
  a.append('hi')
  e = []
  e.append(3)"
    ("a" '(1 2 3 "hi"))
    ("a is b" t)
    ("a.append(c)" ("a is b" t))
    ("a.append(c)" ("a[3] is c" t))
    ("e" '(3))))
#+END_SRC
** insert
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform insert (obj i x)
                         (list _) -> (py-insert obj i x)
                         (_ _) -> (call-method obj insert i x))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
(defun py-insert (list index object)
  "insert OBJECT into LIST at INDEX"
  (let ((i 0)
	(rest list)
	first)
    (while (< i index)
      (setq first rest)
      (setq rest (cdr rest))
      (setq i (1+ i)))
    (setcdr first (cons object rest)))
  nil)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
(pyel-create-tests
 insert
 ("x = [1,2,3]
y = x
x.insert(1,'hi')"
  ("x" '(1 "hi" 2 3))
  ("x is y" t)))
#+END_SRC
** find
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform find (obj sub &optional start end)
                         (string _ _ _) -> (py-find obj sub start end)
                         (_ _ _ _)      -> (call-method obj find sub start end))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-find (str sub &optional start end)
    "Return the lowest index in S where substring sub is found,
  such that sub is contained within S[start:end].  Optional
  arguments start and end are interpreted as in slice notation.
  
  Return -1 on failure."
    (let* ((start (or start 0))
          (index (string-match (regexp-quote sub) (substring str start end))))
      (if index
          (+ index start)
        -1)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   find-method
   ("'aaaxaaa'.find('x',3)"  3)
   ("'aaaxaaa'.find('x',4)"  -1)
   ("'aaaxaaa'.find('x',2, 4)"  3)
   ("'aaaxaaa'.find('x',1, 3)"  -1)
   ("x = 'asdf'"
    ("x.find('sd')" 1)))
#+END_SRC
** index
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform index (obj elem)
                         (list _) -> (list-index elem obj)
                         (string _) -> (py-string-index obj elem)
                         (object _) -> (call-method obj index elem)
                         (vector _) -> (vector-index elem obj))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun list-index (elem list)
    "return the index of ELEM in LIST"
    (let ((m (member elem list)))
      (when m
        (- (length list) (length m)))))
  
  (defun vector-index (elem vector)
    "return the index of ELEM in VECTOR"
    (let ((i 0)
          (len (length vector))
          found)
  
      (while (and (< i len)
                  (not found))
        (if (equal (aref vector i) elem)
            (setq found i)
          (setq i (1+ i))))
      found))
  
  (defun py-string-index (str sub)
    "Like STR.find() but raise ValueError when the substring SUB is not found."
    (let ((ret (py-find str sub)))
      (if (= ret -1)
          (py-raise (ValueError "substring not found"))
        ret)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   index-method
   ;;lists
   ("x = [1,(1,2),'5']"
    ("x.index(1)" 0 )
    ("x.index((1,2))" 1)
    ("x.index('5')" 2))
   ;;strings
   ("x = 'importantstring'"
    ("x.index('t')" 5)
    ("x.index('or')" 3)
    ("x.index('g')" 14)
    ("x.index(x)" 0))
    ("x = 'str.ing'"
     ("x.index('.')" 3))
   ;;arrays
   ("x = (1,2,'tree',(3,))"
    ("x.index(1)" 0)
    ("x.index('tree')" 2)
    ("x.index((3,))" 3)))
#+END_SRC
** remove
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform remove (obj x)
                         (list _) -> (py-list-remove obj x)
                         (_ _) -> (call-method obj remove x))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-remove (list item)
    "remove ITEM from LIST"
    (when (equal (car list) item)
      (setcar list (cadr list))
      (setcdr list (cddr list)))
    (let ((rest list)
          first)
      (while rest
        (if (equal (car rest) item)
            (progn (setcdr first (cdr rest))
                   (setq rest nil))
          (setq first rest
                rest (cdr rest))))
      nil))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   remove-method
   ("x = [1,'2','2',(1,)]
  y = x
  x.remove('2')"
    ("x is y" t))
   
   ("x = [1,'2','2',(1,)]
  x.remove('2')"
    ("x" '(1 "2" [1])))
   
   ("x = [1,'2','2',(1,)]
  x.remove(1)"
    ("x" '("2" "2" [1])))
   
   ("x = [1,'2','2',(1,)]
  x.remove((1,))"
    ("x" '(1 "2" "2"))))
#+END_SRC
** count
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform count (obj elem)
                         (string _) -> (count-str-matches obj elem)
                         (list _) -> (count-elems-list obj elem)
                         (object _)  -> (call-method obj count elem)
                         (vector _) -> (count-elems-vector obj elem))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  ;;;; this one is slower
  ;;;; maybe faster with a non-regexp search function?
  ;; (defun count-str-matches (string substr)
  ;;   "count number of occurrences of SUBSTR in STRING"
  ;;   (let ((quoted (regexp-quote substr))
  ;;      (start 0)
  ;;      (strlen (length string))
  ;;      (sublen (length substr))
  ;;      (count 0))
  ;;     (while (< start strlen)
  ;;       (setq start (string-match quoted string start))
  ;;       (if start (setq count (1+ count)
  ;;                    start (+ start sublen))
  ;;      (setq start strlen)))
  ;;     count))
  
  (defun count-str-matches (string substr)
    "count number of occurrences of SUBSTR in STRING"
    (with-temp-buffer
      (insert string)
      (goto-char 1)
      (how-many (regexp-quote substr))))
  
  (defun count-elems-list (list elem)
    "return how many times ELEM occurs in LIST"
    (let ((c 0))
      (dolist (x list)
        (if (equal x elem)
            (setq c (1+ c))))
      c))
  
  (defun count-elems-vector (vector elem)
    "return how many times ELEM occurs in VECTOR"
    (let ((c 0)
          (i 0)
          (len (length vector)))
      (while (< i len)
        (if (equal (aref vector i) elem)
            (setq c (1+ c)))
        (setq i (1+ i)))
      c))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
(pyel-create-tests
 count-method
 ;;strings
 ("'xxxxx'.count('x')" 5)
 ("'xxxx'.count('xx')" 2)
 ("'xxxx'.count('xxxx')" 1)
 ("'x.xx'.count('.')" 1)
 ;;lists
 ("x = [1,2,3,3,[2],'s']"
  ("x.count(3)" 2)
  ("x.count(2)" 1)
  ("x.count([3,4])" 1)
  ("x.count('s')" 1))
 ;;vector
 ("x = (1,2,3,3,[2],'s')"
  ("x.count(3)" 2)
  ("x.count(2)" 1)
  ("x.count([3,4])" 1)
  ("x.count('s')" 1))
 ("(1,1,1).count(1)" 3))
#+END_SRC
** join
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform join (obj elem)
                         (string _) ->  (mapconcat 'identity elem obj)
                         (_ _)      -> (call-method obj join thing))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    (pyel-create-tests
     join-method
     ("'X'.join(('f','g'))" "fXg")
     ("' '.join([str(x) for x in range(3)])" "0 1 2")
     ("''.join(['a','b']))" "ab"))
#+END_SRC
** extend
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform extend (obj elem)
                         (list _) ->  (py-list-extend obj elem)
                         (_ _)    ->  (call-method obj extend elem))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-extend (list iterable)
    "Extend LIST by appending elements from the ITERABLE"
    (let ((rest list)
          prev)
  
      (while rest
        (setq prev rest
              rest (cdr rest)))
      
      (cond ((py-object-p iterable)
             (_py-list-extend-with-object prev iterable))
            ((listp iterable)
             (_py-list-extend-with-list prev iterable))
            ((stringp iterable)
             (_py-list-extend-with-string prev iterable))
            ((vectorp iterable)
             (_py-list-extend-with-vector prev iterable))
            (t (py-raise (TypeError (format "TypeError: '%s' object is not iterable"
                                            (type-of iterable))))))))
  
  (defun _py-list-extend-with-list (list extendlist)
    (while extendlist
      (setcdr list (list (car extendlist)))
      (setq list (cdr list)
            extendlist (cdr extendlist))))
  
  (defun _py-list-extend-with-vector (list vector)
    (let ((len (length vector))
          (index 0))
      (while (< index len)
        (setcdr list (list (aref vector index)))
        (setq list (cdr list)
              index (1+ index)))
      nil))
  
  (defun _py-list-extend-with-string (list string)
    (let ((len (length string))
          (index 0))
      (while (< index len)
        (setcdr list (list (char-to-string (aref string index))))
        (setq list (cdr list)
              index (1+ index)))
      nil))
  
  (defun _py-list-extend-with-object (list object)
    "assumes that py-object(OBJECT) == t"
    (let ((iter (condition-case nil
                    (call-method object --iter--)
                  (AttributeError
                   (py-raise (TypeError (format
                                         "'%s' object is not iterable"
                                         (getattr object --name--))))))))
      (condition-case nil
          (while t
            (setcdr list (list (call-method iter --next--)))
            (setq list (cdr list)))
        (StopIteration nil))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   extend-method
   ;;extend with list
   ("x = [1]
  y = x
  x.extend([1,'2',(3,)])"
    ("x is y" t)
    ("x" '(1 1 "2" [3])))
   ;;extend with vector
   ("x = [1]
  y = x
  x.extend((1,'2',(3,)))"
    ("x is y" t)
    ("x" '(1 1 "2" [3])))
   ;;extend with string
   ("x = [1]
  y = x
  x.extend('extended')"
    ("x is y" t)
    ("x" '(1 "e" "x" "t" "e" "n" "d" "e" "d")))
   ;;extend with object
   ("class a:
   x = 5
   def __iter__(self):
    return self
   def __next__(self):
    if self.x > 0:
     ret = str(self.x)
     self.x -= 1
     return ret
    raise StopIteration
  obj = a()
  x = [1]
  y = x
  x.extend(obj)"
    ("y is x" t)
    ("x" '(1 "5" "4" "3" "2" "1"))))
#+END_SRC
** pop
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform pop (obj &optional a)
                         (list _) -> (py-list-pop obj a)
                         (hash _) -> (py-hash-pop obj a)
                         (_ _)    -> (call-method obj pop a))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-list-pop (list &optional index)
    "remove and return item at INDEX from LIST (default last)"
    (let ((rest list)
          (i 0)
          last ret)
      (if index
          (if (= index 0)
              (progn
                (setq ret (car list))
                (setcar list (cadr list))
                (setcdr list (cddr list))
                ret)
            ;; 0 < index < len(list)
            (while (< i index)
              (setq last rest
                    rest (cdr rest)
                    i (1+ i)))
            (setq ret (car rest))
            (setcdr last (cdr rest))
            ret)
        ;;pop last element
        (while (cdr rest)
          (setq prev rest
                rest (cdr rest)))
        (setq ret (car rest))
        (setcdr prev nil))
      ret))
  
  (defun py-hash-pop (ht key)
    (let ((ret (gethash key ht)))
      (remhash key ht)
      ret))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   pop-method
   ("x = [[1],'s',(2,), 1, 4]
  y = x
  a = x.pop()
  b = x.pop(0)
  c = x.pop(2)
   "
    ("a" 4)
    ("b" '(1))
    ("c" 1)
    ("x is y" t))
  
   ("x = {1:'one',2:'two',3:'three'}
  y = x.pop(2)"
    ("y" "two") 
    ("repr(x)" "{1: \"one\", 3: \"three\"}"))
   )
#+END_SRC
** reverse
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform reverse (obj)
                         (list) ->  (pyel-list-reverse obj)
                         (_)    ->  (call-method obj reverse))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-list-reverse (list)
    "reverse LIST *IN PLACE*"
    (let ((reversed (reverse list))
          (rest list))
      (while reversed
        (setcar rest (car reversed))
        (setq reversed (cdr reversed)
              rest (cdr rest)))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   reverse-method
   ("x = [1,2,3]
  y = x
  x.reverse()"
    ("x" '(3 2 1))
    ("x is y" t)))
#+END_SRC
** lower
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform lower (obj)
                         (string) ->  (downcase obj)
                         (_)    ->  (call-method obj lower))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   lower-method
   ("x = 'aB'
  y = x
  y = x.lower()"
    ("y" "ab")
    ("x" "aB")))
#+END_SRC
** upper
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform upper (obj)
                         (string) ->  (upcase obj)
                         (_)    ->  (call-method obj upper))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   upper-method
   ("x = 'aB'
  y = x
  y = x.upper()"
    ("y" "AB")
    ("x" "aB")))
#+END_SRC
** split
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform split (obj &optional sep maxsplit)
                         (string) ->  (pyel-split obj sep maxsplit)
                         (_)    ->  (call-method obj split sep maxsplit))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-split (string &optional sep maxsplit)
    
    (let ((sep (or (and sep (regexp-quote sep)) "[ \f\t\n\r\v]+")) ;;correct default?
          (ret (split-string string sep t)))
      (if maxsplit
          ret ;;TODO
        ret)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   split-method
   ("x = 'a b c'
  y = x.split()"
    ("y" '("a" "b" "c")))
   ("'a b c'.split()" '("a" "b" "c"))
   ("y = 'a x b x d x'.split()"
    ("y" '("a" "x" "b" "x" "d" "x"))
    ("len(y)" 6)))
#+END_SRC
** strip
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform strip (obj &optional chars)
                         (string) ->  (pyel-strip obj chars)
                         (_)    ->  (call-method obj strip chars))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-strip-left (s &optional chars)
    "Remove whitespace or CHARS at the beginning of S."
    (let ((chars (if chars (format "\\`[%s]+" chars)
                   "\\`[ \t\n\r]+")))
      (if (string-match chars s)
          (replace-match "" t t s)
        s)))
  
  (defun pyel-strip-right (s &optional chars)
    "Remove whitespace or CHARS at the end of S."
    (let ((chars (if chars (format "[%s]+\\'" chars)
                   "[ \t\n\r]+\\'")))
      (if (string-match chars s)
          (replace-match "" t t s)
        s)))
  
  (defun pyel-strip (s &optional chars)
    "Remove whitespace or CHARS at the beginning and end of S."
    (pyel-strip-left (pyel-strip-right s chars) chars))
  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   strip-method
   ("'\r\nhello \t'.strip('heo')" "hello")
   ("'hello'.strip('heo')" "ll")
   ("x = 'hello'"
    ("x.strip('hlo')" "e")))
#+END_SRC
** get
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform get (obj key &optional default)
                         (hash) ->  (gethash key obj default)
                         (_)    ->  (call-method-with-defaults obj get
                                                               (key) (default)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   get-method
   ("x = {1:'one',2:'two',3:'three'}"
    ("x[1]" "one")
    ("x[1] == x.get(1)" t)
    ("x.get(3, 'd')" "three")
    ("x.get(4, 'd')" "d")))
#+END_SRC
** items
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform items (obj)
                           (hash) -> (pyel-dict-items obj)
                           (_)    -> (call-method obj items))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-dict-items (dict)
    (let (ret)
      (maphash (lambda (key value)
                 (push (list key value) ret))
               dict)
      ret))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   items-method
   ("x = {1:'one',2:'two',3:'three'}
  y = {8 : 88}
  z = {}"
    ("x.items()" '((3 "three") (2 "two") (1 "one")))
    ("y.items()" '((8 88)))
    ("z.items()" nil)))
#+END_SRC
** keys
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform keys (obj)
                             (hash) -> (py-dict-keys obj)
                             (_)    -> (call-method obj key))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-dict-keys (dict)
    (let (ret)
      (maphash (lambda (key value)
                 (push key ret))
               dict)
      ret))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   keys-method
   ("x = {1:'one',2:'two',3:'three'}
  y = {8 : 88}
  z = {}"
    ("x.keys()" '(3 2 1))
    ("y.keys()" '((8)))
    ("z.keys()" nil)))
#+END_SRC
** values
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform values (obj)
                         (hash) -> (pyel-dict-values obj)
                         (_)    -> (call-method obj values))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-dict-values (dict)
    (let (ret)
      (maphash (lambda (key value)
                 (push value ret))
               dict)
      ret))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   values-method
   ("x = {1:'one',2:'two',3:'three'}
  y = {8 : 88}
  z = {}"
    ("x.values()" '("three" "two" "one"))
    ("y.values()" '(88))
    ("z.values()" nil)))
#+END_SRC
** popitem
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform popitem (obj)
                         (hash) -> (pyel-hash-popitem obj)
                         (_)    -> (call-method obj popitem))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-hash-popitem (ht)
    (let* ((key (catch '_pop_
                  (maphash (lambda (k v)
                             (throw '_pop_ k))
                           ht)))
           (ret (list key (gethash key ht))))
      (remhash key ht)
      ret))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   popitem-method
   ("x = {1:'one',2:'two',3:'three'}
  y = x.popitem()"
    ("y" '(1 "one"))
    ("repr(x)" "{2: \"two\", 3: \"three\"}")))
#+END_SRC
** copy
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform copy (obj)
                         (hash) -> (copy-hash-table obj)
                         (_)    -> (call-method obj popitem))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   copy-method
   ("x = {1:['one'],2:'two',3:'three'}
  y = x
  z = x.copy()"
    ("x is z" nil)
    ("x[1] is z[1]" t)))
#+END_SRC
** islower
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform islower (obj)
                         (string) -> (py-islower obj)
                         (_)      -> (call-method obj islower))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-gen-match-code (regex str)
    "return code that tests if REGEX matches STR
  if STR is constant, return t if it matches, else nil"
    (let ((case-fold-search nil))
      (if (stringp str)
          (numberp (string-match regex str))
        `(let ((case-fold-search nil))
           (numberp (string-match ,regex ,str))))))
  
  
  (defmacro py-islower (s)
    "S.islower() -> bool
  
  Return t if all cased characters in S are lowercase and there is
  at least one cased character in S, False otherwise."
    (pyel-gen-match-code "^[^A-Z]*[a-z]+[^A-Z]*$" s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   islower-method
   ("a = 'A'
  b = 'a'
  c = 'Aa'"
    ("a.islower()" nil)
    ("b.islower()" t)
    ("c.islower()" nil))
   ("'A1'.islower()" nil)
   ("'a1'.islower()" t)
   ("'11'.islower()" nil))
#+END_SRC

** isupper
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform isupper (obj)
                         (string) -> (py-isupper obj)
                         (_)      -> (call-method obj isupper))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-isupper (s)
    "S.isupper() -> bool
  
  Return True if all cased characters in S are uppercase and there is
  at least one cased character in S, False otherwise."
  (pyel-gen-match-code "^[^a-z]*[A-Z]+[^a-z]*$" s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   isupper-method
   ("a = 'A'
  b = 'a'
  c = 'Aa'"
    ("a.isupper()" t)
    ("b.isupper()" nil)
    ("c.isupper()" nil))
   ("'A1'.isupper()" t)
   ("'a1'.isupper()" nil)
   ("'11'.isupper()" nil))
#+END_SRC
** istitle
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform istitle (obj)
                         (string) -> (py-istitle obj)
                         (_)      -> (call-method obj istitle))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-istitle (s)
  "  S.istitle() -> bool
  
      Return True if S is a titlecased string and there is at least one
      character in S, i.e. upper- and titlecase characters may only
      follow uncased characters and lowercase characters only cased ones.
      Return False otherwise.  "
  
    (pyel-gen-match-code "^[^a-zA-Z]*[A-Z][^A-Z]*$" s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   istitle-method
   ("a = 'sldk'
  b = 'Dsldk'
  c = 'aDsldk'"
    ("a.istitle()" nil)
    ("b.istitle()" t)
    ("c.istitle()" nil))
   ("'2Dsldk'.istitle()" t)
   ("'DDsldk'.istitle()" nil)
   ("'LDKJ'.istitle()" nil)
   ("''.istitle()" nil))
#+END_SRC
** isalpha
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform isalpha (obj)
                         (string) -> (py-isalpha obj)
                         (_)      -> (call-method obj isalpha))  
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-isalpha (s)
      "S.isalpha() -> bool
    
    Return True if all characters in S are alphabetic
    and there is at least one character in S, False otherwise."
    
      (pyel-gen-match-code "^[a-zA-Z]+$" s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
(pyel-create-tests
 isalpha-method
 ("'a'.isalpha()" t)
 ("'aBc'.isalpha()" t)
 ("'2'.isalpha()" nil)
 ("'a2B'.isalpha()" nil)
 ("''.isalpha()" nil)
 ("x = 'asd'"
  ("x.isalpha()" t)))  
#+END_SRC
** isalnum
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform isalnum (obj)
                         (string) -> (py-isalnum obj)
                         (_)      -> (call-method obj isalnum))

#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defmacro py-isalnum (s)
    "S.isalnum() -> bool
  
      Return True if all characters in S are alphanumeric
      and there is at least one character in S, False otherwise."
  
    (pyel-gen-match-code "^[0-9]+$" s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   isalnum-method
   ("'0'isalnum()" t)
   ("'0'isalnum()"  t)
   ("'0s'.isalnum()" nil)
   ("''.isalnum()" nil)
   ("'0.1'.isalnum()" nil)
   ("x = '23'"
    ("x.isalnum()" t)))
  
#+END_SRC
** zfill
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform zfill (obj width)
                         (string) -> (py-zfill obj width)
                         (_)      -> (call-method obj zfill width))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-zfill (str width)
    " S.zfill(width) -> str
  
  Pad a numeric string S with zeros on the left, to fill a field
  of the specified width. The string S is never truncated."
    (let ((diff (- width (length str))))
      (if (> diff 0)
          (concat (make-string diff ?0) str)
        str)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   zfill-method
   ("a = 'asdf'"
    ("a.zfill(10)" "000000asdf"))
   ("'34'.zfill(5)" "00034")
   ("'234789'.zfill(5)" "234789")
   ("''.zfill(5)" "00000"))  
#+END_SRC
** title
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform title (obj)
                         (string) -> (py-title obj)
                         (_)      -> (call-method obj title))  
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-title (s)
    "Return a titlecased version of S, i.e. words start with title case
  characters, all remaining cased characters have lower case."
    (let ((case-fold-search nil))
      (if (eq s "")
          ""
        (if (string-match "^\\([^A-Za-z]*\\)\\([A-Za-z]\\)\\(.*\\)$" s)
            (concat (match-string 1 s)
                    (upcase (match-string 2 s))
                    (downcase (match-string 3 s)))
          s))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   title-method
   ("'sldk'.title()" "Sldk")
   ("'s'.title()" "S")
   ("''.title()" "")
   ("'2dd'.title()" "2Dd")
   ("'2ddlkDd'.title()" "2Ddlkdd")
   ("'23(23aaaaa'.title()" "23(23Aaaaa")
   ("'343'.title()" "343")
   ("x = '2dd'"
    ("x.title()" "2dd")))
#+END_SRC
** swapcase
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform swapcase (obj)
                         (string) -> (py-swapcase obj)
                         (_)      -> (call-method obj swapcase))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-swapcase (s)
    "swapcase(...)
  S.swapcase() -> str
  
  Return a copy of S with uppercase characters converted to lowercase
  and vice versa."
    (let ((i (length s)))
      (while (> i 0)
        (setq i (1- i))
        (aset s i (let ((char (aref s i)))
                    (if (eq (downcase char) char)
                        (upcase char)
                      (downcase char)))))
      s))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   swapcase-method
   ("'ab'.swapcase()"  "AB")
   ("'aB'.swapcase()"  "Ab")
   ("'aB1'.swapcase()"  "Ab1")
   ("'11'.swapcase()" "11")
   ("''.swapcase()"  "")
   ("x = 'aaBB1'"
    ("x.swapcase()" "AAbb1")))  
#+END_SRC
** startswith
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform startswith (obj prefix &optional start end)
                         (string _ _ _) -> (py-startswith obj prefix start end)
                         (_ _ _ _)            -> (call-method obj startswith prefix start end))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-startswith (s prefix &optional start end)
    "S.startswith(prefix[, start[, end]]) -> bool
  
  Return True if S starts with the specified prefix, False otherwise.
  With optional start, test S beginning at that position.
  With optional end, stop comparing S at that position.
  prefix can also be a tuple of strings to try."  
    (setq start (or start 0))
    (cond ((stringp prefix)
           (if (string-match (concat "^" (regexp-quote prefix)) (substring s start end))
               t nil))
          ((vectorp prefix)
           (let ((found nil)
                 (i 0)
                 (len (length prefix)))
             (while (< i len)
               (if (py-startswith s (aref prefix i) start end)
                   (setq found t
                         i len)
                 (setq i (1+ i))))
             found))
          (t (py-raise (TypeError (format "startswith first arg must be str or a tuple of str, not %s" (py-type prefix)))))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   startswith-method
   ("'abcde'.startswith('bcd', 1, 2)" nil)
   ("'abcde'.startswith('bcd', 1, 3)" nil)
   ("'abcde'.startswith('bcd', 1, 4)" t)
   ("'abcde'.startswith('bcd', 1)" t)
   ("'abcde'.startswith('x', 1)" nil)
   ("'abcde'.startswith('abc')" t)
   ("'$abcde'.startswith('$abc')" t)
   ("'abcde'.startswith('.')" nil)
   ("x = 'abcde'"
    ("x.startswith('.')" nil))
   ("'abcde'.startswith(('.', 'b'))" nil)
   ("'abcde'.startswith(('.', 'b','a'))" t))
#+END_SRC
** splitlines
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform splitlines (obj &optional keepends)
                         (string) -> (py-splitlines obj keepends)
                         (_)      -> (call-method obj splitlines keepends))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-splitlines (string &optional keepends)
    ;;based off code from `split-string'
    (let ((start 0)
          notfirst
          (list nil)
          (len (length string))
          str)
  
      (while (and (string-match "\n" string
                                (if (and notfirst
                                         (= start (match-beginning 0))
                                         (< start len))
                                    (1+ start) start))
                  (< start len))
        (setq notfirst t
              str (substring string start (match-beginning 0)))
        (if keepends (setq str (concat str "\n")))
        (setq list (cons str list)
              start (match-end 0)))
  
      (setq str (substring string start))
      (unless (eq str "")
        (if keepends (setq str (concat str "\n")))
        (setq list (cons str list)))
      (nreverse list)))  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   splitlines-method
   ("'''a
  b
  c
  
  '''.splitlines()" '("a" "b" "c" ""))
   ("x =  '''a
  
  b
  c
  
  '''.splitlines()" '("a" "" "" "b" "c" ""))
   ("''.splitlines()" nil)
   ("'asdf'.splitlines()" '("asdf")))
#+END_SRC
** rstrip
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rstrip (obj &optional chars)
                         (string) -> (py-rstrip obj chars)
                         (_)      -> (call-method obj rstrip chars))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-rstrip (s &optional chars)
    "S.rstrip([chars]) -> str
  
  Return a copy of the string S with trailing whitespace removed.
  If chars is given and not None, remove characters in chars instead."
    (pyel-strip-right s chars))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   rstrip-method
   ("'hello'.rstrip('heo')" "hell")
   ("'\thello\t  '.rstrip()" "    hello")
   ("x = 'hello'"
    ("x.rstrip('hlo')" "he")))  
#+END_SRC
** lstrip
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform lstrip (obj &optional chars)
                         (string) -> (py-lstrip obj chars)
                         (_)      -> (call-method obj lstrip chars))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-lstrip (s &optional chars)
    "S.lstrip([chars]) -> str
  
  Return a copy of the string S with leading whitespace removed.
  If chars is given and not None, remove characters in chars instead."
    (pyel-strip-left s chars))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   lstrip-method
   ("'hello'.lstrip('heo')" "llo")
   ("'\thello\t  '.lstrip()" "hello         ")
   ("x = 'hello'"
    ("x.lstrip('hlo')" "ello")))
#+END_SRC
** rsplit
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rsplit (obj &optional sep maxsplit)
                         (string) ->  (pyel-rsplit obj sep maxsplit)
                         (_)    ->  (call-method obj rsplit sep maxsplit))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-rsplit (string &optional sep maxsplit)
    (pyel-split string sep maxsplit)) ;;TODO: properly implement this
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   rsplit-method
   ("x = 'a b c'
  y = x.rsplit()"
    ("y" '("a" "b" "c")))
   ("'a b c'.rsplit()" '("a" "b" "c"))
   ("y = 'a x b x d x'.rsplit()"
    ("y" '("a" "x" "b" "x" "d" "x"))
    ("len(y)" 6)))
#+END_SRC
** partition
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform partition (obj sep)
                         (string) ->  (py-partition obj sep)
                         (_)    ->  (call-method obj partition sep))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-partition (s sep)
    "S.partition(sep) -> (head, sep, tail)
  
  Search for the separator sep in S, and return the part before it,
  the separator itself, and the part after it.  If the separator is not
  found, return S and two empty strings."
    (if (string-match (format "^\\(.*\\)%s\\(.*\\)$" (regexp-quote sep))  s)
        (vector (match-string 1 s) sep (match-string 2 s))
      (vector s "" "")))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   partition-method
   ("'abcdefghi'.partition('c')" ["ab" "c" "defghi"])
   ("'abcdefghi'.partition('cde')" ["ab" "cde" "fghi"])
   ("'abcdefghi'.partition('x')" ["abcdefghi" "" ""])
   ("'x'.partition('x')" ["" "x" ""])
   ("x = 'abcdefghi'"
    ("x.partition('c')" ["ab" "c" "defghi"])))
#+END_SRC
** rpartition
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rpartition (obj sep)
                         (string) ->  (py-rpartition obj sep)
                         (_)      ->  (call-method obj rpartition sep))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defalias 'py-rpartition 'py-partition)
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   rpartition-method
   ("'abcdefghi'.rpartition('c')" ["ab" "c" "defghi"])
   ("'abcdefghi'.rpartition('cde')" ["ab" "cde" "fghi"])
   ("'abcdefghi'.rpartition('x')" ["abcdefghi" "" ""])
   ("'x'.rpartition('x')" ["" "x" ""])
   ("x = 'abcdefghi'"
    ("x.rpartition('c')" ["ab" "c" "defghi"])))
#+END_SRC
** rjust
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rjust (obj width &optional fillchar)
                         (string) -> (py-rjust obj width fillchar)
                         (_)      -> (call-method obj rjust width fillchar))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-rjust (str width &optional fillchar)
    "S.rjust(width[, fillchar]) -> str
  
  Return S right-justified in a string of length width. Padding is
  done using the specified fill character (default is a space)."
    (if (> (length fillchar) 1)
        (py-raise (TypeError
                   "The fill character must be exactly one character long")))
    (let ((diff (- width (length str)))
          (fillchar (or fillchar " ")))
      (if (> diff 0)
          (concat (make-string diff (string-to-char fillchar)) str)
        str)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   rjust-method
   ("'hi'.rjust(10)"  "        hi")
   ("'hi'.rjust(7, '_')" "_____hi")
   ("'hi'.rjust(10)" "        hi")
   ("'hi'.rjust(3, '_')" "_hi")
   ("'hi'.rjust(7, '_')" "_____hi")
   ("'hisldkjf'.rjust(3, '_')" "hisldkjf")
   ("x = 'ab'"
    ("x.rjust(10)" "        ab")))  
#+END_SRC
** ljust
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform ljust (obj width &optional fillchar)
                         (string) -> (py-ljust obj width fillchar)
                         (_)      -> (call-method obj ljust width fillchar))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-ljust (str width &optional fillchar)
  "S.ljust(width[, fillchar]) -> str
  
  Return S left-justified in a Unicode string of length width. Padding is
  done using the specified fill character (default is a space)."
    (if (> (length fillchar) 1)
        (py-raise (TypeError
                   "The fill character must be exactly one character long")))
    (let ((diff (- width (length str)))
          (fillchar (or fillchar " ")))
      (if (> diff 0)
          (concat str (make-string diff (string-to-char fillchar)))
        str)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   ljust-method
   ("'hi'.ljust(10)"  "hi        ")
   ("'hi'.ljust(7, '_')" "hi_____")
   ("'hi'.ljust(10)" "hi        ")
   ("'hi'.ljust(3, '_')" "hi_")
   ("'hi'.ljust(7, '_')" "hi_____")
   ("'hisldkjf'.ljust(3, '_')" "hisldkjf")
   ("x = 'ab'"
    ("x.ljust(10)" "ab        ")))    
#+END_SRC
** rfind
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rfind (obj sub &optional start end)
                         (string) -> (py-rfind obj sub start end)
                         (_)      -> (call-method obj rfind sub start end))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun pyel-reverse-string (string)
    (let ((start 0)
          (end (1- (length string)))
          tmp)
      (while (< start end)
        (setq tmp (aref string start))
        (aset string start (aref string end))
        (aset string end tmp)
        (setq start (1+ start)
              end (1- end)))
      string))
  
  (defun py-rfind(str sub &optional start end)
    "S.rfind(sub[, start[, end]]) -> int
  
  Return the highest index in S where substring sub is found,
  such that sub is contained within S[start:end].  Optional
  arguments start and end are interpreted as in slice notation.
  
  Return -1 on failure."
    (let* ((start (or start 0))
           (index (string-match
                   (pyel-reverse-string (regexp-quote sub))
                   (pyel-reverse-string (substring str start end)))))
      (if index
          ;;(+ index start)
          (- (length str)  index (length sub))
        -1)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  (pyel-create-tests
   rfind-method
   ("'aaaxaaa'.rfind('x',3)"  3)
   ("'aaaxaaa'.rfind('x',4)"  -1)
   ("'aaaxaaa'.rfind('x',2, 4)"  3)
   ("'aaaxaaa'.rfind('x',1, 3)"  -1)
   ("'abcxebdxebdexed'.rfind('xe')" 12)
   ("x = 'asdf'
  y = 'abxabxab'"
    ("x.rfind('sd')" 1)
    ("y.rfind('ab')" 6)))
#+END_SRC
** rindex
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform rindex (obj sub &optional start end)
                         (string) -> (py-rindex obj sub start end)
                         (_)      -> (call-method obj rindex sub start end))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-rindex (str sub &optional start end)
    "S.rindex(sub[, start[, end]]) -> int
   
   Like S.rfind() but raise ValueError when the substring is not found.  "
    (let ((ret (py-rfind str sub start end)))
      (if (= ret -1)
          (py-raise (ValueError "substring not found"))
        ret)))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    
#+END_SRC
** replace
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform replace (obj old new &optional count)
                         (string) -> (py-string-replace obj old new count)
                         (_)      -> (call-method obj replace old new count))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-string-replace (string old new &optional count)
    "Return a copy of STRING with all occurrences of substring
  OLD replaced by NEW.  If the optional argument COUNT is
  given, only the first COUNT occurrences are replaced."
    ;;based on `replace-regexp-in-string'
  
    ;; To avoid excessive consing from multiple matches in long strings,
    ;; don't just call `replace-match' continually.  Walk down the
    ;; string looking for matches of REGEXP and building up a (reversed)
    ;; list MATCHES.  This comprises segments of STRING which weren't
    ;; matched interspersed with replacements for segments that were.
    ;; [For a `large' number of replacements it's more efficient to
    ;; operate in a temporary buffer; we can't tell from the function's
    ;; args whether to choose the buffer-based implementation, though it
    ;; might be reasonable to do so for long enough STRING.]
    (let ((case-fold-search nil)
          (replaced 0)
          (l (length string))
          (start 0)
          matches str mb me)
      (save-match-data
        (while (and
                (or (null count) (< replaced count))
                (< start l)
                (string-match old string start))
          (setq replaced (1+ replaced)
                mb (match-beginning 0)
                me (match-end 0))
          ;; If we matched the empty string, make sure we advance by one char
          (when (= me mb) (setq me (min l (1+ mb))))
          ;; Generate a replacement for the matched substring.
          ;; Operate only on the substring to minimize string consing.
          ;; Set up match data for the substring for replacement;
          ;; presumably this is likely to be faster than munging the
          ;; match data directly in Lisp.
          (string-match old (setq str (substring string mb me)))
          (setq matches
                (cons (replace-match new
                                     nil nil str)
                      (cons (substring string start mb) ; unmatched prefix
                            matches)))
          (setq start me))
        ;; Reconstruct a string from the pieces.
        (setq matches (cons (substring string start l) matches)) ; leftover
        (apply #'concat (nreverse matches)))))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    
#+END_SRC
** clear
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform clear (obj)
                         (hash) -> (py-hash-table-clear obj)
                         (_)      -> (call-method obj clear))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-hash-table-clear (ht)
    "Remove all items from hash table HT"
    (maphash (lambda (key value)
               (remhash key ht))
             ht))
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    
#+END_SRC
** fromkeys
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform fromkeys(obj keys &optional value)
                           (hash _ _) -> (py-fromkeys keys value)
                           (_)      -> (call-method obj fromkeys keys value))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defun py-fromkeys (keys &optional value)
    (let* ((len (length keys))
           (ht (make-hash-table :size len :test 'equal))
           (type (type-of keys))
           (i 0))
      (cond ((eq type 'cons)
             (while keys
               (puthash (car keys) value ht)
               (setq keys (cdr keys))))
            ((eq type 'string)
             (while (< i len)
               (puthash (substring keys i (1+ i)) value ht)
               (setq i (1+ i))))
            ((eq type 'vector)
             (while (< i len)
               (puthash (aref keys i) value ht)
               (setq i (1+ i)))
             )
            (t (error "py-fromkeys: invalid type for parameter 'keys'")))
      ht))  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    
#+END_SRC
** isprintable
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
  (pyel-method-transform isprintable (obj)
                         (string) -> (py-isprintable obj)
                         (_)      -> (call-method obj isprintable))
#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
  (defconst py-printable "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c")
  
  (setq py-printable-ht (make-hash-table :test 'equal
                                         :size (* (length py-printable) 2)))
  (py-for c in py-printable
          (puthash c t py-printable-ht))
  
  ;;TODO: perhaps examining ascii values will be faster
  (defun py-isprintable(s)
    "S.isprintable() -> bool
  
  Return True if all characters in S are considered
  printable in repr() or S is empty, False otherwise."
    (let ((good t)
          (len (length s))
          (i 0))
      (while (and good (< i len))
        (if (not (gethash (pyel-string-nth s i) py-printable-ht))
            (setq good nil))
        (setq i (1+ i)))
      good))
  
#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
    
#+END_SRC
** transform template
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
;;
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py

#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC
* macro transforms
macros and macroish primitives
** cond
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  ;;;;errors with these
  ;; (pyel-create-tests cond
  ;;                    "x = cond([1 > 2, 'first']
  ;;    [2 == 2, 'second']
  ;;    [5 == 7, 'third']
  ;;    [True, error('wtf')])
  ;; assert x == 'second'"
  ;;                    )
#+END_SRC
** lambda
*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
  ;;;;Errors with these
  ;; (pyel-create-tests lambda
  ;;                    "x = [2,3,4]
  ;; square = lambda([x]
  ;;  x*x)
  ;; y = mapcar(square,x)
  ;; assert y == [4,9,16]
  ;; "
  ;;                    "f = lambda([x,y]
  ;; if x > y:
  ;;  'x'
  ;; else:
  ;;  'y')
  ;; x=cl_mapcar(f, [1, 2, 3, 4, 5], [4, 2, 1, 6, 3])
  ;; assert x == ['y', 'y', 'x', 'y', 'x']")
#+END_SRC
** transform template
*** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
;;
#+END_SRC
*** python ast
#+Begin_SRC python :tangle py-ast.py

#+END_SRC
*** python el lib
#+Begin_SRC emacs-lisp:tangle py-lib.el

#+END_SRC

*** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC


* ---------------------
* misc
#+Begin_SRC emacs-lisp :tangle pyel.el
  
  (defmacro pyel-string-nth (string index)
    (list 'substring string index
          (if (numberp index) (1+ index) (list '1+ index))))
  
  (defsubst pyel-nth (n seq)
    (let ((tp (type-of seq)))
      (cond ((eq tp 'cons)
             (nth n seq))
            ((eq tp 'vector)
             (aref seq n))
            ((eq tp 'string)
             (substring seq n (1+ n))))))
  
  (defun char-split-string (string)
    "split a string into its charaters"
    (cdr (butlast (split-string string ""))))
  
  
  (defun strip-end (string &optional char)
    "if CHAR occurs at the end of STRING, remove it"
    (let ((split (char-split-string string))
          (char (or char " ")))
      
      (while (string= char (car (last split)))
        (setq split (butlast split)))
      (mapconcat 'identity split "")))
  
  (defun strip-start (string &optional char)
    "if CHAR occurs at the beginning of STRING, remove all occurrences"
    (let ((split (char-split-string string))
          (char (or char " ")))
      
      (while (string= char (car split))
        (setq split (cdr split)))
      (mapconcat 'identity split "")))
  
  ;;built-in equivalent?
  (defun file-path-concat (&rest dirs)
    "concatenate strings representing file paths
  prevents multiple/none '/' seporating file names"
    (let* ((first (strip-end (car dirs) "/"))
           (last (strip-start (car (last dirs)) "/"))
           (dirs (append (list first)
                         (mapcar '(lambda (x)  (strip-start (strip-end x "/") "/"))
                                 (cdr (butlast dirs)))
                         (list last))))
      (mapconcat 'identity dirs "/")))
  
#+END_SRC

* EOF
** main
#+Begin_SRC emacs-lisp :tangle pyel.el
  (require 'transformer)
  (require 'pyel-transforms)
  (require 'pyel-tests-generated)
  (require 'pyel-preprocessor)
  
  (require 'py-objects)
  (require 'pyel-mode)
  (require 'py-lib)
  (require 'py-classes)
  
  (require 'pyel-testing)
  
  (pyel "this_fixes_a_bug") ;;prevents errors the first time pyel-run-tests is run
  ;;...I'm so sorry
  
  (load "pyel.py")
  (load "py-iter.py")
  (provide 'pyel)
  ;;pyel.el ends here
#+END_SRC
** transforms
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
(provide 'pyel-transforms)
;;pyel-transforms.el ends here
#+END_SRC
** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
(provide 'pyel-tests)
;;pyel-tests.el ends here
#+END_SRC
** py-lib
#+Begin_SRC emacs-lisp :tangle py-lib.el
(provide 'py-lib)
;;py-lib.el ends here
#+END_SRC
** python ast
#+Begin_SRC python :tangle py-ast.py
  print(eval(ast.dump(ast.parse(open(options[0],'r').read()),include_attributes=True)))
  
  #py-ast.py ends here
#+END_SRC

* ------------------
* transform template
** transform
#+Begin_SRC emacs-lisp :tangle pyel-transforms.el
;;
#+END_SRC
** python ast
#+Begin_SRC python :tangle py-ast.py

#+END_SRC
** python el lib
#+Begin_SRC emacs-lisp :tangle py-lib.el

#+END_SRC

** tests
#+Begin_SRC emacs-lisp :tangle pyel-tests.el
;;
#+END_SRC

* .emacs config (move)
#+Begin_SRC emacs-lisp :tangle pyel.el
(setq pyel-use-list-for-varargs t)
;;(setq pyel-directory "path/to/pyel/directory")
;;(require 'pyel)
#+END_SRC



