repl.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. package zygo
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/gob"
  6. "fmt"
  7. "io"
  8. "os"
  9. "runtime/pprof"
  10. "strconv"
  11. "strings"
  12. )
  13. var precounts map[string]int
  14. var postcounts map[string]int
  15. func CountPreHook(env *Zlisp, name string, args []Sexp) {
  16. precounts[name] += 1
  17. }
  18. func CountPostHook(env *Zlisp, name string, retval Sexp) {
  19. postcounts[name] += 1
  20. }
  21. func getLine(reader *bufio.Reader) (string, error) {
  22. line := make([]byte, 0)
  23. for {
  24. linepart, hasMore, err := reader.ReadLine()
  25. if err != nil {
  26. return "", err
  27. }
  28. line = append(line, linepart...)
  29. if !hasMore {
  30. break
  31. }
  32. }
  33. return string(line), nil
  34. }
  35. // NB at the moment this doesn't track comment and strings state,
  36. // so it will fail if unbalanced '(' are found in either.
  37. func isBalanced(str string) bool {
  38. parens := 0
  39. squares := 0
  40. for _, c := range str {
  41. switch c {
  42. case '(':
  43. parens++
  44. case ')':
  45. parens--
  46. case '[':
  47. squares++
  48. case ']':
  49. squares--
  50. }
  51. }
  52. return parens == 0 && squares == 0
  53. }
  54. var continuationPrompt = "... "
  55. func (pr *Prompter) getExpressionOrig(reader *bufio.Reader) (readin string, err error) {
  56. line, err := getLine(reader)
  57. if err != nil {
  58. return "", err
  59. }
  60. for !isBalanced(line) {
  61. fmt.Printf(continuationPrompt)
  62. nextline, err := getLine(reader)
  63. if err != nil {
  64. return "", err
  65. }
  66. line += "\n" + nextline
  67. }
  68. return line, nil
  69. }
  70. // liner reads Stdin only. If noLiner, then we read from reader.
  71. func (pr *Prompter) getExpressionWithLiner(env *Zlisp, reader *bufio.Reader, noLiner bool) (readin string, xs []Sexp, err error) {
  72. var line, nextline string
  73. if noLiner {
  74. fmt.Printf(pr.prompt)
  75. line, err = getLine(reader)
  76. } else {
  77. line, err = pr.Getline(nil)
  78. }
  79. if err != nil {
  80. return "", nil, err
  81. }
  82. err = UnexpectedEnd
  83. var x []Sexp
  84. // test parse, but don't load or generate bytecode
  85. env.parser.ResetAddNewInput(bytes.NewBuffer([]byte(line + "\n")))
  86. x, err = env.parser.ParseTokens()
  87. //P("\n after ResetAddNewInput, err = %v. x = '%s'\n", err, SexpArray(x).SexpString())
  88. if len(x) > 0 {
  89. xs = append(xs, x...)
  90. }
  91. for err == ErrMoreInputNeeded || err == UnexpectedEnd || err == ResetRequested {
  92. if noLiner {
  93. fmt.Printf(continuationPrompt)
  94. nextline, err = getLine(reader)
  95. } else {
  96. nextline, err = pr.Getline(&continuationPrompt)
  97. }
  98. if err != nil {
  99. return "", nil, err
  100. }
  101. env.parser.NewInput(bytes.NewBuffer([]byte(nextline + "\n")))
  102. x, err = env.parser.ParseTokens()
  103. if len(x) > 0 {
  104. for i := range x {
  105. if x[i] == SexpEnd {
  106. P("found an SexpEnd token, omitting it")
  107. continue
  108. }
  109. xs = append(xs, x[i])
  110. }
  111. }
  112. switch err {
  113. case nil:
  114. line += "\n" + nextline
  115. Q("no problem parsing line '%s' into '%s', proceeding...\n", line, (&SexpArray{Val: x, Env: env}).SexpString(nil))
  116. return line, xs, nil
  117. case ResetRequested:
  118. continue
  119. case ErrMoreInputNeeded:
  120. continue
  121. default:
  122. return "", nil, fmt.Errorf("Error on line %d: %v\n", env.parser.Linenum(), err)
  123. }
  124. }
  125. return line, xs, nil
  126. }
  127. func processDumpCommand(env *Zlisp, args []string) {
  128. if len(args) == 0 {
  129. env.DumpEnvironment()
  130. } else {
  131. err := env.DumpFunctionByName(args[0])
  132. if err != nil {
  133. fmt.Println(err)
  134. }
  135. }
  136. }
  137. func Repl(env *Zlisp, cfg *ZlispConfig) {
  138. var reader *bufio.Reader
  139. if cfg.NoLiner {
  140. // reader is used if one wishes to drop the liner library.
  141. // Useful for not full terminal env, like under test.
  142. reader = bufio.NewReader(os.Stdin)
  143. }
  144. if cfg.Trace {
  145. // debug tracing
  146. env.debugExec = true
  147. }
  148. if !cfg.Quiet {
  149. if cfg.Sandboxed {
  150. fmt.Printf("zygo [sandbox mode] version %s\n", Version())
  151. } else {
  152. fmt.Printf("zygo version %s\n", Version())
  153. }
  154. fmt.Printf("press tab (repeatedly) to get completion suggestions. Shift-tab goes back. Ctrl-d to exit.\n")
  155. }
  156. var pr *Prompter // can be nil if noLiner
  157. if !cfg.NoLiner {
  158. pr = NewPrompter(cfg.Prompt)
  159. defer pr.Close()
  160. } else {
  161. pr = &Prompter{prompt: cfg.Prompt}
  162. }
  163. infixSym := env.MakeSymbol("infix")
  164. for {
  165. line, exprsInput, err := pr.getExpressionWithLiner(env, reader, cfg.NoLiner)
  166. //Q("\n exprsInput(len=%d) = '%v'\n line = '%s'\n", len(exprsInput), (&SexpArray{Val: exprsInput}).SexpString(nil), line)
  167. if err != nil {
  168. fmt.Println(err)
  169. if err == io.EOF {
  170. os.Exit(0)
  171. }
  172. env.Clear()
  173. continue
  174. }
  175. parts := strings.Split(strings.Trim(line, " "), " ")
  176. //parts := strings.Split(line, " ")
  177. if len(parts) == 0 {
  178. continue
  179. }
  180. first := strings.Trim(parts[0], " ")
  181. if first == ".quit" {
  182. break
  183. }
  184. if first == ".cd" {
  185. if len(parts) < 2 {
  186. fmt.Printf("provide directory path to change to.\n")
  187. continue
  188. }
  189. err := os.Chdir(parts[1])
  190. if err != nil {
  191. fmt.Printf("error: %s\n", err)
  192. continue
  193. }
  194. pwd, err := os.Getwd()
  195. if err == nil {
  196. fmt.Printf("cur dir: %s\n", pwd)
  197. } else {
  198. fmt.Printf("error: %s\n", err)
  199. }
  200. continue
  201. }
  202. // allow & at the repl to take the address of an expression
  203. if len(first) > 0 && first[0] == '&' {
  204. //P("saw & at repl, first='%v', parts='%#v'. exprsInput = '%#v'", first, parts, exprsInput)
  205. exprsInput = []Sexp{MakeList(exprsInput)}
  206. }
  207. // allow * at the repl to dereference a pointer and print
  208. if len(first) > 0 && first[0] == '*' {
  209. //P("saw * at repl, first='%v', parts='%#v'. exprsInput = '%#v'", first, parts, exprsInput)
  210. exprsInput = []Sexp{MakeList(exprsInput)}
  211. }
  212. if first == ".dump" {
  213. processDumpCommand(env, parts[1:])
  214. continue
  215. }
  216. if first == ".gls" {
  217. fmt.Printf("\nScopes:\n")
  218. prev := env.showGlobalScope
  219. env.showGlobalScope = true
  220. err = env.ShowStackStackAndScopeStack()
  221. env.showGlobalScope = prev
  222. if err != nil {
  223. fmt.Printf("%s\n", err)
  224. }
  225. continue
  226. }
  227. if first == ".ls" {
  228. err := env.ShowStackStackAndScopeStack()
  229. if err != nil {
  230. fmt.Println(err)
  231. }
  232. continue
  233. }
  234. if first == ".verb" {
  235. Verbose = !Verbose
  236. fmt.Printf("verbose: %v.\n", Verbose)
  237. continue
  238. }
  239. if first == ".debug" {
  240. env.debugExec = true
  241. fmt.Printf("instruction debugging on.\n")
  242. continue
  243. }
  244. if first == ".undebug" {
  245. env.debugExec = false
  246. fmt.Printf("instruction debugging off.\n")
  247. continue
  248. }
  249. var expr Sexp
  250. n := len(exprsInput)
  251. if n > 0 {
  252. infixWrappedSexp := MakeList([]Sexp{infixSym, &SexpArray{Val: exprsInput, Env: env}})
  253. expr, err = env.EvalExpressions([]Sexp{infixWrappedSexp})
  254. } else {
  255. line = env.ReplLineInfixWrap(line)
  256. expr, err = env.EvalString(line + " ") // print standalone variables
  257. }
  258. switch err {
  259. case nil:
  260. case NoExpressionsFound:
  261. env.Clear()
  262. continue
  263. default:
  264. fmt.Print(env.GetStackTrace(err))
  265. env.Clear()
  266. continue
  267. }
  268. if expr != SexpNull {
  269. // try to print strings more elegantly!
  270. switch e := expr.(type) {
  271. case *SexpStr:
  272. if e.backtick {
  273. fmt.Printf("`%s`\n", e.S)
  274. } else {
  275. fmt.Printf("%s\n", strconv.Quote(e.S))
  276. }
  277. default:
  278. switch sym := expr.(type) {
  279. case Selector:
  280. Q("repl calling RHS() on Selector")
  281. rhs, err := sym.RHS(env)
  282. if err != nil {
  283. Q("repl problem in call to RHS() on SexpSelector: '%v'", err)
  284. fmt.Print(env.GetStackTrace(err))
  285. env.Clear()
  286. continue
  287. } else {
  288. Q("got back rhs of type %T", rhs)
  289. fmt.Println(rhs.SexpString(nil))
  290. continue
  291. }
  292. case *SexpSymbol:
  293. if sym.isDot {
  294. resolved, err := dotGetSetHelper(env, sym.name, nil)
  295. if err != nil {
  296. fmt.Print(env.GetStackTrace(err))
  297. env.Clear()
  298. continue
  299. }
  300. fmt.Println(resolved.SexpString(nil))
  301. continue
  302. }
  303. }
  304. fmt.Println(expr.SexpString(nil))
  305. }
  306. }
  307. }
  308. }
  309. func runScript(env *Zlisp, fname string, cfg *ZlispConfig) {
  310. file, err := os.Open(fname)
  311. if err != nil {
  312. fmt.Println(err)
  313. return
  314. }
  315. defer file.Close()
  316. err = env.LoadFile(file)
  317. if err != nil {
  318. fmt.Println(err)
  319. if cfg.ExitOnFailure {
  320. os.Exit(-1)
  321. }
  322. return
  323. }
  324. _, err = env.Run()
  325. if cfg.CountFuncCalls {
  326. fmt.Println("Pre:")
  327. for name, count := range precounts {
  328. fmt.Printf("\t%s: %d\n", name, count)
  329. }
  330. fmt.Println("Post:")
  331. for name, count := range postcounts {
  332. fmt.Printf("\t%s: %d\n", name, count)
  333. }
  334. }
  335. if err != nil {
  336. fmt.Print(env.GetStackTrace(err))
  337. if cfg.ExitOnFailure {
  338. os.Exit(-1)
  339. }
  340. Repl(env, cfg)
  341. }
  342. }
  343. func (env *Zlisp) StandardSetup() {
  344. env.ImportBaseTypes()
  345. env.ImportEval()
  346. env.ImportTime()
  347. env.ImportPackageBuilder()
  348. env.ImportMsgpackMap()
  349. defmap := `(defmac defmap [name] ^(defn ~name [& rest] (msgmap (quote ~name) rest)))`
  350. _, err := env.EvalString(defmap)
  351. panicOn(err)
  352. // colonOp := `(defmac : [key hmap & def] ^(hget ~hmap (quote ~key) ~@def))`
  353. // _, err = env.EvalString(colonOp)
  354. // panicOn(err)
  355. rangeMacro := `(defmac range [key value myhash & body]
  356. ^(let [n (len ~myhash)]
  357. (for [(def i 0) (< i n) (def i (+ i 1))]
  358. (begin
  359. (mdef (quote ~key) (quote ~value) (hpair ~myhash i))
  360. ~@body))))`
  361. _, err = env.EvalString(rangeMacro)
  362. panicOn(err)
  363. reqMacro := `(defmac req [a] ^(source (sym2str (quote ~a))))`
  364. _, err = env.EvalString(reqMacro)
  365. panicOn(err)
  366. incrMacro := `(defmac ++ [a] ^(set ~a (+ ~a 1)))`
  367. _, err = env.EvalString(incrMacro)
  368. panicOn(err)
  369. incrEqMacro := `(defmac += [a b] ^(set ~a (+ ~a ~b)))`
  370. _, err = env.EvalString(incrEqMacro)
  371. panicOn(err)
  372. decrMacro := `(defmac -- [a] ^(set ~a (- ~a 1)))`
  373. _, err = env.EvalString(decrMacro)
  374. panicOn(err)
  375. decrEqMacro := `(defmac -= [a b] ^(set ~a (- ~a ~b)))`
  376. _, err = env.EvalString(decrEqMacro)
  377. panicOn(err)
  378. env.ImportChannels()
  379. env.ImportGoroutines()
  380. env.ImportRegex()
  381. env.ImportRandom()
  382. gob.Register(SexpHash{})
  383. gob.Register(SexpArray{})
  384. }
  385. // like main() for a standalone repl, now in library
  386. func ReplMain(cfg *ZlispConfig) {
  387. var env *Zlisp
  388. if cfg.LoadDemoStructs {
  389. RegisterDemoStructs()
  390. }
  391. if cfg.Sandboxed {
  392. env = NewZlispSandbox()
  393. } else {
  394. env = NewZlisp()
  395. }
  396. env.StandardSetup()
  397. if cfg.LoadDemoStructs {
  398. // avoid data conflicts by only loading these in demo mode.
  399. env.ImportDemoData()
  400. }
  401. if cfg.CpuProfile != "" {
  402. f, err := os.Create(cfg.CpuProfile)
  403. if err != nil {
  404. fmt.Println(err)
  405. os.Exit(-1)
  406. }
  407. err = pprof.StartCPUProfile(f)
  408. if err != nil {
  409. fmt.Println(err)
  410. os.Exit(-1)
  411. }
  412. defer pprof.StopCPUProfile()
  413. }
  414. precounts = make(map[string]int)
  415. postcounts = make(map[string]int)
  416. if cfg.CountFuncCalls {
  417. env.AddPreHook(CountPreHook)
  418. env.AddPostHook(CountPostHook)
  419. }
  420. if cfg.Command != "" {
  421. _, err := env.EvalString(cfg.Command)
  422. if err != nil {
  423. fmt.Fprintf(os.Stderr, "%v\n", err)
  424. os.Exit(1)
  425. }
  426. os.Exit(0)
  427. }
  428. runRepl := true
  429. args := cfg.Flags.Args()
  430. if len(args) > 0 {
  431. runRepl = false
  432. runScript(env, args[0], cfg)
  433. if cfg.AfterScriptDontExit {
  434. runRepl = true
  435. }
  436. }
  437. if runRepl {
  438. Repl(env, cfg)
  439. }
  440. if cfg.MemProfile != "" {
  441. f, err := os.Create(cfg.MemProfile)
  442. if err != nil {
  443. fmt.Println(err)
  444. os.Exit(-1)
  445. }
  446. defer f.Close()
  447. err = pprof.Lookup("heap").WriteTo(f, 1)
  448. if err != nil {
  449. fmt.Println(err)
  450. os.Exit(-1)
  451. }
  452. }
  453. }
  454. func (env *Zlisp) ReplLineInfixWrap(line string) string {
  455. return "{" + line + "}"
  456. }