123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- package zygo
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "strings"
- )
- // read new-line delimited text from a file into an array (slurpf "path-to-file")
- func SlurpfileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- var fn string
- switch fna := args[0].(type) {
- case *SexpStr:
- fn = fna.S
- default:
- return SexpNull, fmt.Errorf("slurp requires a string path to read. we got type %T / value = %v", args[0], args[0])
- }
- if !FileExists(string(fn)) {
- return SexpNull, fmt.Errorf("file '%s' does not exist", fn)
- }
- f, err := os.Open(fn)
- if err != nil {
- return SexpNull, err
- }
- defer f.Close()
- a := make([]Sexp, 0)
- bufIn := bufio.NewReader(f)
- lineNum := int64(1)
- for {
- lastline, err := bufIn.ReadBytes('\n')
- if err != nil && err != io.EOF {
- return SexpNull, err
- }
- n := len(lastline)
- if err == io.EOF && n == 0 {
- break
- }
- if n > 0 {
- if lastline[n-1] == '\n' {
- a = append(a, &SexpStr{S: string(lastline[:n-1])})
- } else {
- a = append(a, &SexpStr{S: string(lastline)})
- }
- lineNum += 1
- }
- if err == io.EOF {
- break
- }
- }
- //VPrintf("read %d lines\n", lineNum)
- return env.NewSexpArray(a), nil
- }
- // (writef <content> path); (write path) is the macro version.
- // (owritef <content> path): write an array of strings out to the named file,
- // overwriting it in the process. (owrite) is the macro version.
- // save is the same as write.
- func WriteToFileFunction(name string) ZlispUserFunction {
- return func(env *Zlisp, _ string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var fn string
- switch fna := args[1].(type) {
- case *SexpStr:
- fn = fna.S
- default:
- 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])
- }
- if name == "write" || name == "writef" || name == "save" {
- // don't overwrite existing file
- if FileExists(fn) {
- return SexpNull, fmt.Errorf("refusing to write to existing file '%s'",
- fn)
- }
- }
- // owrite / owritef overwrite indiscriminately.
- f, err := os.Create(fn)
- if err != nil {
- return SexpNull, err
- }
- defer f.Close()
- var slice []Sexp
- switch sl := args[0].(type) {
- case *SexpArray:
- slice = sl.Val
- for i := range slice {
- s := slice[i].SexpString(nil)
- if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
- s = s[1 : len(s)-1]
- } else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
- s = s[1 : len(s)-1]
- }
- _, err = fmt.Fprintf(f, "%s\n", s)
- if err != nil {
- return SexpNull, err
- }
- }
- case *SexpRaw:
- _, err = f.Write([]byte(sl.Val))
- if err != nil {
- return SexpNull, err
- }
- default:
- s := sl.SexpString(nil)
- if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
- s = s[1 : len(s)-1]
- } else if len(s) >= 2 && s[0] == '`' && s[len(s)-1] == '`' {
- s = s[1 : len(s)-1]
- }
- _, err = fmt.Fprintf(f, "%s\n", s)
- if err != nil {
- return SexpNull, err
- }
- }
- return SexpNull, nil
- }
- }
- // SplitStringFunction splits a string based on an arbitrary delimiter
- func SplitStringFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- // make sure the two args are strings
- s1, ok := args[0].(*SexpStr)
- if !ok {
- return SexpNull, fmt.Errorf("split requires a string to split, got %T", args[0])
- }
- s2, ok := args[1].(*SexpStr)
- if !ok {
- return SexpNull, fmt.Errorf("split requires a string as a delimiter, got %T", args[1])
- }
- toSplit := s1.S
- splitter := s2.S
- s := strings.Split(toSplit, splitter)
- split := make([]Sexp, len(s))
- for i := range split {
- split[i] = &SexpStr{S: s[i]}
- }
- return env.NewSexpArray(split), nil
- }
- // (nsplit "a\nb") -> ["a" "b"]
- func SplitStringOnNewlinesFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- args = append(args, &SexpStr{S: "\n"})
- return SplitStringFunction(env, name, args)
- }
|