slurp.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package zygo
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strings"
  8. )
  9. // read new-line delimited text from a file into an array (slurpf "path-to-file")
  10. func SlurpfileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
  11. if len(args) != 1 {
  12. return SexpNull, WrongNargs
  13. }
  14. var fn string
  15. switch fna := args[0].(type) {
  16. case *SexpStr:
  17. fn = fna.S
  18. default:
  19. return SexpNull, fmt.Errorf("slurp requires a string path to read. we got type %T / value = %v", args[0], args[0])
  20. }
  21. if !FileExists(string(fn)) {
  22. return SexpNull, fmt.Errorf("file '%s' does not exist", fn)
  23. }
  24. f, err := os.Open(fn)
  25. if err != nil {
  26. return SexpNull, err
  27. }
  28. defer f.Close()
  29. a := make([]Sexp, 0)
  30. bufIn := bufio.NewReader(f)
  31. lineNum := int64(1)
  32. for {
  33. lastline, err := bufIn.ReadBytes('\n')
  34. if err != nil && err != io.EOF {
  35. return SexpNull, err
  36. }
  37. n := len(lastline)
  38. if err == io.EOF && n == 0 {
  39. break
  40. }
  41. if n > 0 {
  42. if lastline[n-1] == '\n' {
  43. a = append(a, &SexpStr{S: string(lastline[:n-1])})
  44. } else {
  45. a = append(a, &SexpStr{S: string(lastline)})
  46. }
  47. lineNum += 1
  48. }
  49. if err == io.EOF {
  50. break
  51. }
  52. }
  53. //VPrintf("read %d lines\n", lineNum)
  54. return env.NewSexpArray(a), nil
  55. }
  56. // (writef <content> path); (write path) is the macro version.
  57. // (owritef <content> path): write an array of strings out to the named file,
  58. // overwriting it in the process. (owrite) is the macro version.
  59. // save is the same as write.
  60. func WriteToFileFunction(name string) ZlispUserFunction {
  61. return func(env *Zlisp, _ string, args []Sexp) (Sexp, error) {
  62. if len(args) != 2 {
  63. return SexpNull, WrongNargs
  64. }
  65. var fn string
  66. switch fna := args[1].(type) {
  67. case *SexpStr:
  68. fn = fna.S
  69. default:
  70. return SexpNull, fmt.Errorf("owrite requires a string (SexpStr) path to write to as the second argument. we got type %T / value = %v", args[1], args[1])
  71. }
  72. if name == "write" || name == "writef" || name == "save" {
  73. // don't overwrite existing file
  74. if FileExists(fn) {
  75. return SexpNull, fmt.Errorf("refusing to write to existing file '%s'",
  76. fn)
  77. }
  78. }
  79. // owrite / owritef overwrite indiscriminately.
  80. f, err := os.Create(fn)
  81. if err != nil {
  82. return SexpNull, err
  83. }
  84. defer f.Close()
  85. var slice []Sexp
  86. switch sl := args[0].(type) {
  87. case *SexpArray:
  88. slice = sl.Val
  89. for i := range slice {
  90. s := slice[i].SexpString(nil)
  91. if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
  92. s = s[1 : len(s)-1]
  93. } else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
  94. s = s[1 : len(s)-1]
  95. }
  96. _, err = fmt.Fprintf(f, "%s\n", s)
  97. if err != nil {
  98. return SexpNull, err
  99. }
  100. }
  101. case *SexpRaw:
  102. _, err = f.Write([]byte(sl.Val))
  103. if err != nil {
  104. return SexpNull, err
  105. }
  106. default:
  107. s := sl.SexpString(nil)
  108. if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
  109. s = s[1 : len(s)-1]
  110. } else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
  111. s = s[1 : len(s)-1]
  112. }
  113. _, err = fmt.Fprintf(f, "%s\n", s)
  114. if err != nil {
  115. return SexpNull, err
  116. }
  117. }
  118. return SexpNull, nil
  119. }
  120. }
  121. // SplitStringFunction splits a string based on an arbitrary delimiter
  122. func SplitStringFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
  123. if len(args) != 2 {
  124. return SexpNull, WrongNargs
  125. }
  126. // make sure the two args are strings
  127. s1, ok := args[0].(*SexpStr)
  128. if !ok {
  129. return SexpNull, fmt.Errorf("split requires a string to split, got %T", args[0])
  130. }
  131. s2, ok := args[1].(*SexpStr)
  132. if !ok {
  133. return SexpNull, fmt.Errorf("split requires a string as a delimiter, got %T", args[1])
  134. }
  135. toSplit := s1.S
  136. splitter := s2.S
  137. s := strings.Split(toSplit, splitter)
  138. split := make([]Sexp, len(s))
  139. for i := range split {
  140. split[i] = &SexpStr{S: s[i]}
  141. }
  142. return env.NewSexpArray(split), nil
  143. }
  144. // (nsplit "a\nb") -> ["a" "b"]
  145. func SplitStringOnNewlinesFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
  146. if len(args) != 1 {
  147. return SexpNull, WrongNargs
  148. }
  149. args = append(args, &SexpStr{S: "\n"})
  150. return SplitStringFunction(env, name, args)
  151. }