github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/cli/cli.go (about)

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	cli "github.com/urfave/cli/v2"
    12  
    13  	"github.com/nevalang/neva/internal/builder"
    14  	"github.com/nevalang/neva/internal/compiler"
    15  	"github.com/nevalang/neva/internal/interpreter"
    16  	"github.com/nevalang/neva/pkg"
    17  )
    18  
    19  func NewApp( //nolint:funlen
    20  	workdir string,
    21  	bldr builder.Builder,
    22  	goc compiler.Compiler,
    23  	nativec compiler.Compiler,
    24  	wasmc compiler.Compiler,
    25  	jsonc compiler.Compiler,
    26  	dotc compiler.Compiler,
    27  ) *cli.App {
    28  	var (
    29  		target string
    30  		debug  bool
    31  	)
    32  
    33  	return &cli.App{
    34  		Name:  "neva",
    35  		Usage: "Flow-based programming language",
    36  		Commands: []*cli.Command{
    37  			{
    38  				Name:  "version",
    39  				Usage: "Get current Nevalang version",
    40  				Action: func(_ *cli.Context) error {
    41  					fmt.Println(pkg.Version)
    42  					return nil
    43  				},
    44  			},
    45  			{
    46  				Name:  "new",
    47  				Usage: "Create new Nevalang project",
    48  				Args:  true,
    49  				Action: func(cCtx *cli.Context) error {
    50  					if path := cCtx.Args().First(); path != "" {
    51  						if err := os.Mkdir(path, 0755); err != nil {
    52  							return err
    53  						}
    54  						return createNevaMod(path)
    55  					}
    56  					return createNevaMod(workdir)
    57  				},
    58  			},
    59  			{
    60  				Name:      "get",
    61  				Usage:     "Add dependency to current module",
    62  				Args:      true,
    63  				ArgsUsage: "Provide path to the module",
    64  				Action: func(cCtx *cli.Context) error {
    65  					installedPath, err := bldr.Get(
    66  						workdir,
    67  						cCtx.Args().Get(0),
    68  						cCtx.Args().Get(1),
    69  					)
    70  					if err != nil {
    71  						return err
    72  					}
    73  					fmt.Printf(
    74  						"%s installed to %s\n", cCtx.Args().Get(0),
    75  						installedPath,
    76  					)
    77  					return nil
    78  				},
    79  			},
    80  			{
    81  				Name:      "run",
    82  				Usage:     "Run neva program from source code in interpreter mode",
    83  				Args:      true,
    84  				ArgsUsage: "Provide path to the executable package",
    85  				Flags: []cli.Flag{
    86  					&cli.BoolFlag{
    87  						Name:        "debug",
    88  						Usage:       "Show message events in stdout",
    89  						Destination: &debug,
    90  					},
    91  				},
    92  				Action: func(cCtx *cli.Context) error {
    93  					dirFromArg, err := getMainPkgFromArgs(cCtx)
    94  					if err != nil {
    95  						return err
    96  					}
    97  					intr := interpreter.New(bldr, goc, debug)
    98  					if err := intr.Interpret(
    99  						context.Background(),
   100  						workdir,
   101  						dirFromArg,
   102  					); err != nil {
   103  						return err
   104  					}
   105  					return nil
   106  				},
   107  			},
   108  			{
   109  				Name:  "build",
   110  				Usage: "Build executable binary from neva program source code",
   111  				Args:  true,
   112  				Flags: []cli.Flag{
   113  					&cli.StringFlag{
   114  						Name:        "target",
   115  						Required:    false,
   116  						Usage:       "Emit Go or WASM instead of machine code",
   117  						Destination: &target,
   118  						Action: func(ctx *cli.Context, s string) error {
   119  							switch s {
   120  							case "go", "wasm", "native", "json", "dot":
   121  							default:
   122  								return fmt.Errorf("Unknown target %s", s)
   123  							}
   124  							return nil
   125  						},
   126  					},
   127  				},
   128  				ArgsUsage: "Provide path to the executable package",
   129  				Action: func(cCtx *cli.Context) error {
   130  					dirFromArg, err := getMainPkgFromArgs(cCtx)
   131  					if err != nil {
   132  						return err
   133  					}
   134  					switch target {
   135  					case "go":
   136  						return goc.Compile(
   137  							workdir, dirFromArg, workdir,
   138  						)
   139  					case "wasm":
   140  						return wasmc.Compile(
   141  							workdir, dirFromArg, workdir,
   142  						)
   143  					case "json":
   144  						return jsonc.Compile(
   145  							workdir, dirFromArg, workdir,
   146  						)
   147  					case "dot":
   148  						return dotc.Compile(
   149  							workdir, dirFromArg, workdir,
   150  						)
   151  					default:
   152  						return nativec.Compile(
   153  							workdir, dirFromArg, workdir,
   154  						)
   155  					}
   156  				},
   157  			},
   158  		},
   159  	}
   160  }
   161  
   162  func getMainPkgFromArgs(cCtx *cli.Context) (string, error) {
   163  	firstArg := cCtx.Args().First()
   164  	dirFromArg := strings.TrimSuffix(firstArg, "/main.neva")
   165  	if filepath.Ext(dirFromArg) != "" {
   166  		return "", errors.New(
   167  			"Use path to directory with executable package, relative to module root",
   168  		)
   169  	}
   170  	return dirFromArg, nil
   171  }
   172  
   173  func createNevaMod(path string) error {
   174  	// Create neva.yml file
   175  	nevaYmlContent := fmt.Sprintf("neva: %s", pkg.Version)
   176  	if err := os.WriteFile(
   177  		filepath.Join(path, "neva.yml"),
   178  		[]byte(nevaYmlContent),
   179  		0644,
   180  	); err != nil {
   181  		return err
   182  	}
   183  
   184  	// Create src sub-directory
   185  	srcPath := filepath.Join(path, "src")
   186  	if err := os.Mkdir(srcPath, 0755); err != nil {
   187  		return err
   188  	}
   189  
   190  	// Create main.neva file
   191  	mainNevaContent := `component Main(start) (stop) {
   192  	nodes { Println }
   193  	net {
   194  		:start -> ('Hello, World!' -> println -> :stop)
   195  	}
   196  }`
   197  
   198  	if err := os.WriteFile(
   199  		filepath.Join(srcPath, "main.neva"),
   200  		[]byte(mainNevaContent),
   201  		0644,
   202  	); err != nil {
   203  		return err
   204  	}
   205  
   206  	return nil
   207  }