github.com/traefik/yaegi@v0.15.1/cmd/yaegi/yaegi.go (about)

     1  /*
     2  Yaegi interprets Go programs.
     3  
     4  Yaegi reads Go language programs from standard input, string
     5  parameters or files and run them.
     6  
     7  If invoked with no arguments, it processes the standard input in
     8  a Read-Eval-Print-Loop. A prompt is displayed if standard input is
     9  a terminal.
    10  
    11  File Mode
    12  
    13  In file mode, as in a standard Go compiler, source files are read entirely
    14  before being parsed, then evaluated. It allows to handle forward
    15  declarations and to have package code split in multiple source files.
    16  
    17  Go specifications fully apply in this mode.
    18  
    19  All files are interpreted in file mode except the initial file if it
    20  starts with "#!" characters (the shebang pattern to allow executable
    21  scripts), for example "#!/usr/bin/env yaegi". In that case, the initial
    22  file is interpreted in REPL mode.
    23  
    24  REPL mode
    25  
    26  In REPL mode, the interpreter parses the code incrementally. As soon
    27  as a statement is complete, it evaluates it. This makes the interpreter
    28  suitable for interactive command line and scripts.
    29  
    30  Go specifications apply with the following differences:
    31  
    32  All local and global declarations (const, var, type, func) are allowed,
    33  including in short form, except that all identifiers must be defined
    34  before use (as declarations inside a standard Go function).
    35  
    36  The statements are evaluated in the global space, within an implicit
    37  "main" package.
    38  
    39  It is not necessary to have a package statement, or a main function in
    40  REPL mode. Import statements for preloaded binary packages can also
    41  be avoided (i.e. all the standard library except the few packages
    42  where default names collide, as "math/rand" and "crypto/rand", for which
    43  an explicit import is still necessary).
    44  
    45  Note that the source packages are always interpreted in file mode,
    46  even if imported from REPL.
    47  
    48  The following extract is a valid executable script:
    49  
    50  	#!/usr/bin/env yaegi
    51  	helloHandler := func(w http.ResponseWriter, req *http.Request) {
    52  	   io.WriteString(w, "Hello, world!\n")
    53  	}
    54  	http.HandleFunc("/hello", helloHandler)
    55  	log.Fatal(http.ListenAndServe(":8080", nil))
    56  
    57  Example of a one liner:
    58  
    59  	$ yaegi -e 'println(reflect.TypeOf(fmt.Print))'
    60  
    61  Options:
    62  	-e string
    63  	   evaluate the string and return.
    64      -i
    65  	   start an interactive REPL after file execution.
    66  	-syscall
    67  	   include syscall symbols.
    68  	-tags tag,list
    69  	   a comma-separated list of build tags to consider satisfied during
    70  	   the interpretation.
    71  	-unsafe
    72  	  include unsafe symbols.
    73  
    74  Environment variables:
    75    YAEGI_SYSCALL=1
    76      Include syscall symbols (same as -syscall flag).
    77    YAEGI_UNRESTRICTED=1
    78      Include unrestricted symbols (same as -unrestricted flag).
    79    YAEGI_UNSAFE=1
    80      Include unsafe symbols (same as -unsafe flag).
    81    YAEGI_PROMPT=1
    82      Force enable the printing of the REPL prompt and the result of last instruction,
    83      even if stdin is not a terminal.
    84    YAEGI_AST_DOT=1
    85      Generate and display graphviz dot of AST with dotty(1)
    86    YAEGI_CFG_DOT=1
    87      Generate and display graphviz dot of CFG with dotty(1)
    88    YAEGI_DOT_CMD='dot -Tsvg -ofoo.svg'
    89      Defines how to process the dot code generated whenever YAEGI_AST_DOT and/or
    90      YAEGI_CFG_DOT is enabled. If any of YAEGI_AST_DOT or YAEGI_CFG_DOT is set,
    91      but YAEGI_DOT_CMD is not defined, the default is to write to a .dot file
    92      next to the Go source file.
    93  */
    94  package main
    95  
    96  import (
    97  	"errors"
    98  	"flag"
    99  	"fmt"
   100  	"log"
   101  	"os"
   102  
   103  	"github.com/traefik/yaegi/interp"
   104  )
   105  
   106  const (
   107  	Extract = "extract"
   108  	Help    = "help"
   109  	Run     = "run"
   110  	Test    = "test"
   111  	Version = "version"
   112  )
   113  
   114  var version = "devel" // This may be overwritten at build time.
   115  
   116  func main() {
   117  	var cmd string
   118  	var err error
   119  	var exitCode int
   120  
   121  	log.SetFlags(log.Lshortfile) // Ease debugging.
   122  
   123  	if len(os.Args) > 1 {
   124  		cmd = os.Args[1]
   125  	}
   126  
   127  	switch cmd {
   128  	case Extract:
   129  		err = extractCmd(os.Args[2:])
   130  	case Help, "-h", "--help":
   131  		err = help(os.Args[2:])
   132  	case Run:
   133  		err = run(os.Args[2:])
   134  	case Test:
   135  		err = test(os.Args[2:])
   136  	case Version:
   137  		fmt.Println(version)
   138  	default:
   139  		// If no command is given, fallback to default "run" command.
   140  		// This allows scripts starting with "#!/usr/bin/env yaegi",
   141  		// as passing more than 1 argument to #! executable may be not supported
   142  		// on all platforms.
   143  		cmd = Run
   144  		err = run(os.Args[1:])
   145  	}
   146  
   147  	if err != nil && !errors.Is(err, flag.ErrHelp) {
   148  		fmt.Fprintln(os.Stderr, fmt.Errorf("%s: %w", cmd, err))
   149  		if p, ok := err.(interp.Panic); ok {
   150  			fmt.Fprintln(os.Stderr, string(p.Stack))
   151  		}
   152  		exitCode = 1
   153  	}
   154  	os.Exit(exitCode)
   155  }