github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/nash.go (about)

     1  // Package nash provides a library to embed the `nash` scripting language
     2  // within your program or create your own nash cli.
     3  package nash
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"io"
     9  
    10  	"github.com/madlambda/nash/ast"
    11  	shell "github.com/madlambda/nash/internal/sh"
    12  	"github.com/madlambda/nash/sh"
    13  )
    14  
    15  type (
    16  	// Shell is the execution engine of the scripting language.
    17  	Shell struct {
    18  		interp *shell.Shell
    19  	}
    20  )
    21  
    22  func newShell(nashpath string, nashroot string, abort bool) (*Shell, error) {
    23  	var (
    24  		nash Shell
    25  		err  error
    26  	)
    27  
    28  	if abort {
    29  		nash.interp, err = shell.NewAbortShell(nashpath, nashroot)
    30  	} else {
    31  		nash.interp, err = shell.NewShell(nashpath, nashroot)
    32  	}
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	return &nash, nil
    38  }
    39  
    40  // New creates a new `nash.Shell` instance.
    41  func New(nashpath string, nashroot string) (*Shell, error) {
    42  	return newShell(nashpath, nashroot, false)
    43  }
    44  
    45  // NewAbort creates a new shell that aborts in case of error on initialization.
    46  // Useful for tests, to avoid trashing the output log.
    47  func NewAbort(nashpath string, nashroot string) (*Shell, error) {
    48  	return newShell(nashpath, nashroot, true)
    49  }
    50  
    51  // SetDebug enable some logging for debug purposes.
    52  func (nash *Shell) SetDebug(b bool) {
    53  	nash.interp.SetDebug(b)
    54  }
    55  
    56  func (nash *Shell) Log(fmt string, args ...interface{}) {
    57  	nash.interp.Log(fmt, args...)
    58  }
    59  
    60  // SetInteractive enables interactive (shell) mode.
    61  func (nash *Shell) SetInteractive(b bool) {
    62  	nash.interp.SetInteractive(b)
    63  }
    64  
    65  func (nash *Shell) NashPath() string {
    66  	return nash.interp.NashPath()
    67  }
    68  
    69  // Environ returns the set of environment variables in the shell
    70  func (nash *Shell) Environ() shell.Env {
    71  	return nash.interp.Environ()
    72  }
    73  
    74  // GetFn gets the function object.
    75  func (nash *Shell) GetFn(name string) (sh.FnDef, error) {
    76  	fnObj, err := nash.interp.GetFn(name)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return fnObj.Fn(), nil
    81  }
    82  
    83  // Prompt returns the environment prompt or the default one
    84  func (nash *Shell) Prompt() string {
    85  	value, ok := nash.interp.Getenv("PROMPT")
    86  
    87  	if ok {
    88  		return value.String()
    89  	}
    90  
    91  	return "<no prompt> "
    92  }
    93  
    94  // SetNashdPath sets an alternativa path to nashd
    95  func (nash *Shell) SetNashdPath(path string) {
    96  	nash.interp.SetNashdPath(path)
    97  }
    98  
    99  // Exec executes the code specified by string content.
   100  // By default, nash uses os.Stdin, os.Stdout and os.Stderr as input, output
   101  // and error file descriptors. You can change it with SetStdin, SetStdout and Stderr,
   102  // respectively.
   103  // The path is only used for error line reporting. If content represents a file, then
   104  // setting path to this filename should improve debugging (or no).
   105  func (nash *Shell) Exec(path, content string) error {
   106  	return nash.interp.Exec(path, content)
   107  }
   108  
   109  // ExecOutput executes the code specified by string content.
   110  //
   111  // It behaves like **Exec** with the exception that it will ignore any
   112  // stdout parameter (and the default os.Stdout) and will return the
   113  // whole stdout output in memory.
   114  //
   115  // This method has no side effects, it will preserve any previously
   116  // setted stdout, it will only ignore the configured stdout to run
   117  // the provided script content;
   118  func (nash *Shell) ExecOutput(path, content string) ([]byte, error) {
   119  	oldstdout := nash.Stdout()
   120  	defer nash.SetStdout(oldstdout)
   121  
   122  	var output bytes.Buffer
   123  	nash.SetStdout(&output)
   124  
   125  	err := nash.interp.Exec(path, content)
   126  	return output.Bytes(), err
   127  }
   128  
   129  // ExecuteString executes the script content.
   130  // Deprecated: Use Exec instead.
   131  func (nash *Shell) ExecuteString(path, content string) error {
   132  	return nash.interp.Exec(path, content)
   133  }
   134  
   135  // ExecFile executes the script content of the file specified by path
   136  // and passes as arguments to the script the given args slice.
   137  func (nash *Shell) ExecFile(path string, args ...string) error {
   138  	if len(args) > 0 {
   139  		err := nash.ExecuteString("setting args", `var ARGS = `+args2Nash(args))
   140  		if err != nil {
   141  			return fmt.Errorf("Failed to set nash arguments: %s", err.Error())
   142  		}
   143  	}
   144  	return nash.interp.ExecFile(path)
   145  }
   146  
   147  // ExecuteFile executes the given file.
   148  // Deprecated: Use ExecFile instead.
   149  func (nash *Shell) ExecuteFile(path string) error {
   150  	return nash.interp.ExecFile(path)
   151  }
   152  
   153  // ExecuteTree executes the given tree.
   154  // Deprecated: Use ExecTree instead.
   155  func (nash *Shell) ExecuteTree(tr *ast.Tree) ([]sh.Obj, error) {
   156  	return nash.interp.ExecuteTree(tr)
   157  }
   158  
   159  // ExecTree evaluates the given abstract syntax tree.
   160  // it returns the object result of eval or nil when not applied and error.
   161  func (nash *Shell) ExecTree(tree *ast.Tree) ([]sh.Obj, error) {
   162  	return nash.interp.ExecuteTree(tree)
   163  }
   164  
   165  // SetStdout set the stdout of the nash engine.
   166  func (nash *Shell) SetStdout(out io.Writer) {
   167  	nash.interp.SetStdout(out)
   168  }
   169  
   170  // SetStderr set the stderr of nash engine
   171  func (nash *Shell) SetStderr(err io.Writer) {
   172  	nash.interp.SetStderr(err)
   173  }
   174  
   175  // SetStdin set the stdin of the nash engine
   176  func (nash *Shell) SetStdin(in io.Reader) {
   177  	nash.interp.SetStdin(in)
   178  }
   179  
   180  // Stdin is the interpreter standard input
   181  func (nash *Shell) Stdin() io.Reader { return nash.interp.Stdin() }
   182  
   183  // Stdout is the interpreter standard output
   184  func (nash *Shell) Stdout() io.Writer { return nash.interp.Stdout() }
   185  
   186  // Stderr is the interpreter standard error
   187  func (nash *Shell) Stderr() io.Writer { return nash.interp.Stderr() }
   188  
   189  // Setvar sets or updates the variable in the nash session. It
   190  // returns true if variable was found and properly updated.
   191  func (nash *Shell) Setvar(name string, value sh.Obj) bool {
   192  	return nash.interp.Setvar(name, value)
   193  }
   194  
   195  // Newvar creates a new variable in the interpreter scope
   196  func (nash *Shell) Newvar(name string, value sh.Obj) {
   197  	nash.interp.Newvar(name, value)
   198  }
   199  
   200  // Getvar retrieves a variable from nash session
   201  func (nash *Shell) Getvar(name string) (sh.Obj, bool) {
   202  	return nash.interp.Getvar(name)
   203  }
   204  
   205  func args2Nash(args []string) string {
   206  	ret := "("
   207  
   208  	for i := 0; i < len(args); i++ {
   209  		ret += `"` + args[i] + `"`
   210  
   211  		if i < (len(args) - 1) {
   212  			ret += " "
   213  		}
   214  	}
   215  
   216  	return ret + ")"
   217  }