go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/cmd/starlark/starlark.go (about)

     1  // Copyright 2017 The Bazel Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // The starlark command interprets a Starlark file.
     6  // With no arguments, it starts a read-eval-print loop (REPL).
     7  package main // import "go.starlark.net/cmd/starlark"
     8  
     9  import (
    10  	"flag"
    11  	"fmt"
    12  	"log"
    13  	"os"
    14  	"runtime"
    15  	"runtime/pprof"
    16  	"strings"
    17  
    18  	"go.starlark.net/internal/compile"
    19  	"go.starlark.net/lib/json"
    20  	"go.starlark.net/lib/math"
    21  	"go.starlark.net/lib/time"
    22  	"go.starlark.net/repl"
    23  	"go.starlark.net/resolve"
    24  	"go.starlark.net/starlark"
    25  	"golang.org/x/term"
    26  )
    27  
    28  // flags
    29  var (
    30  	cpuprofile = flag.String("cpuprofile", "", "gather Go CPU profile in this file")
    31  	memprofile = flag.String("memprofile", "", "gather Go memory profile in this file")
    32  	profile    = flag.String("profile", "", "gather Starlark time profile in this file")
    33  	showenv    = flag.Bool("showenv", false, "on success, print final global environment")
    34  	execprog   = flag.String("c", "", "execute program `prog`")
    35  )
    36  
    37  func init() {
    38  	flag.BoolVar(&compile.Disassemble, "disassemble", compile.Disassemble, "show disassembly during compilation of each function")
    39  
    40  	// non-standard dialect flags
    41  	flag.BoolVar(&resolve.AllowSet, "set", resolve.AllowSet, "allow set data type")
    42  	flag.BoolVar(&resolve.AllowRecursion, "recursion", resolve.AllowRecursion, "allow while statements and recursive functions")
    43  	flag.BoolVar(&resolve.AllowGlobalReassign, "globalreassign", resolve.AllowGlobalReassign, "allow reassignment of globals, and if/for/while statements at top level")
    44  
    45  	// flags that are now standard
    46  	flag.BoolVar(&resolve.AllowFloat, "float", resolve.AllowFloat, "obsolete; no effect")
    47  	flag.BoolVar(&resolve.AllowLambda, "lambda", resolve.AllowLambda, "obsolete; no effect")
    48  }
    49  
    50  func main() {
    51  	os.Exit(doMain())
    52  }
    53  
    54  func doMain() int {
    55  	log.SetPrefix("starlark: ")
    56  	log.SetFlags(0)
    57  	flag.Parse()
    58  
    59  	if *cpuprofile != "" {
    60  		f, err := os.Create(*cpuprofile)
    61  		check(err)
    62  		err = pprof.StartCPUProfile(f)
    63  		check(err)
    64  		defer func() {
    65  			pprof.StopCPUProfile()
    66  			err := f.Close()
    67  			check(err)
    68  		}()
    69  	}
    70  	if *memprofile != "" {
    71  		f, err := os.Create(*memprofile)
    72  		check(err)
    73  		defer func() {
    74  			runtime.GC()
    75  			err := pprof.Lookup("heap").WriteTo(f, 0)
    76  			check(err)
    77  			err = f.Close()
    78  			check(err)
    79  		}()
    80  	}
    81  
    82  	if *profile != "" {
    83  		f, err := os.Create(*profile)
    84  		check(err)
    85  		err = starlark.StartProfile(f)
    86  		check(err)
    87  		defer func() {
    88  			err := starlark.StopProfile()
    89  			check(err)
    90  		}()
    91  	}
    92  
    93  	thread := &starlark.Thread{Load: repl.MakeLoad()}
    94  	globals := make(starlark.StringDict)
    95  
    96  	// Ideally this statement would update the predeclared environment.
    97  	// TODO(adonovan): plumb predeclared env through to the REPL.
    98  	starlark.Universe["json"] = json.Module
    99  	starlark.Universe["time"] = time.Module
   100  	starlark.Universe["math"] = math.Module
   101  
   102  	switch {
   103  	case flag.NArg() == 1 || *execprog != "":
   104  		var (
   105  			filename string
   106  			src      interface{}
   107  			err      error
   108  		)
   109  		if *execprog != "" {
   110  			// Execute provided program.
   111  			filename = "cmdline"
   112  			src = *execprog
   113  		} else {
   114  			// Execute specified file.
   115  			filename = flag.Arg(0)
   116  		}
   117  		thread.Name = "exec " + filename
   118  		globals, err = starlark.ExecFile(thread, filename, src, nil)
   119  		if err != nil {
   120  			repl.PrintError(err)
   121  			return 1
   122  		}
   123  	case flag.NArg() == 0:
   124  		stdinIsTerminal := term.IsTerminal(int(os.Stdin.Fd()))
   125  		if stdinIsTerminal {
   126  			fmt.Println("Welcome to Starlark (go.starlark.net)")
   127  		}
   128  		thread.Name = "REPL"
   129  		repl.REPL(thread, globals)
   130  		if stdinIsTerminal {
   131  			fmt.Println()
   132  		}
   133  	default:
   134  		log.Print("want at most one Starlark file name")
   135  		return 1
   136  	}
   137  
   138  	// Print the global environment.
   139  	if *showenv {
   140  		for _, name := range globals.Keys() {
   141  			if !strings.HasPrefix(name, "_") {
   142  				fmt.Fprintf(os.Stderr, "%s = %s\n", name, globals[name])
   143  			}
   144  		}
   145  	}
   146  
   147  	return 0
   148  }
   149  
   150  func check(err error) {
   151  	if err != nil {
   152  		log.Fatal(err)
   153  	}
   154  }