Skip to content

Instantly share code, notes, and snippets.

@theanalyst
Last active August 29, 2015 13:58
Show Gist options
  • Select an option

  • Save theanalyst/10017095 to your computer and use it in GitHub Desktop.

Select an option

Save theanalyst/10017095 to your computer and use it in GitHub Desktop.
Getting source of a function in hy.. WIP
(import re
linecache
inspect
[hy.lex.lexer [lexer]]
[itertools [count]])
(defmacro getstartlines [f]
"Gets the starting lines for a given function"
(if-python2 `(. ~f func-code co-firstlineno)
`(. ~f --code-- co-firstlineno)))
(defmacro inc! [var]
`(setv ~var (inc ~var)))
(defmacro dec! [var]
`(setv ~var (dec ~var)))
;; Wow! such hack! much crazy!
;; For starts, failure cases include unbalanced s-expressions
;; (where this function never terminates)
;; Also might fail for regexes that use double quotes inside them
;; (as we have a regex to skip those..kind of brings up xkcd.com/1313)
(defn sourcelines [f]
"Gets the source of a `function`, as a list"
(unless (inspect.isfunction f)
(raise (TypeError (.format "source only works for functions got % instead" f))))
(let [[file (inspect.getsourcefile f)]
[linecount (count (getstartlines f))]
[parencount 0]
[lines []]]
(if (.endswith file ".py")
(first (inspect.getsourcelines f))
(do
(while True
(setv line (linecache.getline file (next linecount)))
;; Try to match multiline docstrings..lexer will throw a lex exception
;; otherwise. However source of this function
;; and others using some double quote regexes will fail
(unless (re.match r"^(([^\"]*\"){2})*[^\"]*$" line)
(continue))
(for [token (lexer.lex line)]
(cond [(= "LPAREN" (.gettokentype token)) (inc! parencount)]
[(= "RPAREN" (.gettokentype token)) (dec! parencount)]))
(.append lines line)
(when (zero? parencount)
(break)))
lines))))
(defn source [f]
(.join "" ( sourcelines f)))
(print (source take))
(print (source inspect.getsourcelines))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment