feat: some basic commands
This commit is contained in:
@@ -1,55 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.maximhutz.com/max/lambda/internal/cli"
|
||||
"git.maximhutz.com/max/lambda/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse CLI arguments.
|
||||
options, err := config.FromArgs()
|
||||
cli.HandleError(err)
|
||||
func Lambda() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "lambda",
|
||||
Short: "Lambda calculus interpreter",
|
||||
Long: "A lambda calculus interpreter supporting multiple representations.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Legacy behavior when no subcommand is given.
|
||||
options, err := config.FromArgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger := options.GetLogger()
|
||||
logger.Info("using program arguments", "args", os.Args)
|
||||
logger.Info("parsed CLI options", "options", options)
|
||||
logger := options.GetLogger()
|
||||
logger.Info("using program arguments", "args", os.Args)
|
||||
logger.Info("parsed CLI options", "options", options)
|
||||
|
||||
r := GetRegistry()
|
||||
r := GetRegistry()
|
||||
|
||||
// Get input.
|
||||
input, err := options.Source.Extract()
|
||||
cli.HandleError(err)
|
||||
// Get input.
|
||||
input, err := options.Source.Extract()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse code into syntax tree.
|
||||
repr, err := r.Unmarshal(input, "saccharine")
|
||||
cli.HandleError(err)
|
||||
logger.Info("parsed syntax tree", "tree", repr)
|
||||
// Parse code into syntax tree.
|
||||
repr, err := r.Unmarshal(input, "saccharine")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("parsed syntax tree", "tree", repr)
|
||||
|
||||
// Compile expression to lambda calculus.
|
||||
compiled, err := r.ConvertTo(repr, "lambda")
|
||||
cli.HandleError(err)
|
||||
logger.Info("compiled λ expression", "tree", compiled)
|
||||
// Compile expression to lambda calculus.
|
||||
compiled, err := r.ConvertTo(repr, "lambda")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("compiled λ expression", "tree", compiled)
|
||||
|
||||
// Create reducer with the compiled expression.
|
||||
engine, err := r.GetDefaultEngine("lambda")
|
||||
cli.HandleError(err)
|
||||
// Create reducer with the compiled expression.
|
||||
engine, err := r.GetDefaultEngine("lambda")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
process := engine.Load()
|
||||
err = process.Set(compiled)
|
||||
cli.HandleError(err)
|
||||
// Run reduction.
|
||||
for process.Step(1) {
|
||||
process := engine.Load()
|
||||
err = process.Set(compiled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run reduction.
|
||||
for process.Step(1) {
|
||||
}
|
||||
|
||||
// Return the final reduced result.
|
||||
result, err := process.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := r.Marshal(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return options.Destination.Write(output)
|
||||
},
|
||||
}
|
||||
|
||||
// Return the final reduced result.
|
||||
result, err := process.Get()
|
||||
cli.HandleError(err)
|
||||
cmd.PersistentFlags().BoolP("verbose", "v", false, "Enable verbose output")
|
||||
|
||||
output, err := r.Marshal(result)
|
||||
cli.HandleError(err)
|
||||
cmd.AddCommand(LambdaConvert())
|
||||
cmd.AddCommand(LambdaEngine())
|
||||
|
||||
err = options.Destination.Write(output)
|
||||
cli.HandleError(err)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func main() {
|
||||
lambda := Lambda()
|
||||
if err := lambda.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
100
cmd/lambda/lambda_convert.go
Normal file
100
cmd/lambda/lambda_convert.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.maximhutz.com/max/lambda/internal/cli"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// inferReprFromPath returns the repr type based on file extension.
|
||||
func inferReprFromPath(path string) (string, error) {
|
||||
switch ext := strings.ToLower(filepath.Ext(path)); ext {
|
||||
case ".lam", ".lambda":
|
||||
return "lambda", nil
|
||||
case ".sac", ".saccharine":
|
||||
return "saccharine", nil
|
||||
default:
|
||||
return "", fmt.Errorf("unknown file extension '%s'", ext)
|
||||
}
|
||||
}
|
||||
|
||||
func LambdaConvert() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "convert <input> <output>",
|
||||
Short: "Convert between lambda calculus representations",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
inputPath := args[0]
|
||||
outputPath := args[1]
|
||||
|
||||
// Infer repr types from extensions.
|
||||
inputRepr, err := inferReprFromPath(inputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("input file: %w", err)
|
||||
}
|
||||
|
||||
outputRepr, err := inferReprFromPath(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("output file: %w", err)
|
||||
}
|
||||
|
||||
// Read input file.
|
||||
input, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading input file: %w", err)
|
||||
}
|
||||
|
||||
r := GetRegistry()
|
||||
|
||||
// Parse input into syntax tree.
|
||||
repr, err := r.Unmarshal(string(input), inputRepr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing input: %w", err)
|
||||
}
|
||||
|
||||
if viper.GetBool("verbose") {
|
||||
fmt.Fprintf(os.Stderr, "Parsed %s from %s\n", inputRepr, inputPath)
|
||||
}
|
||||
|
||||
// Convert to output repr if different.
|
||||
var result cli.Repr
|
||||
if inputRepr != outputRepr {
|
||||
result, err = r.ConvertTo(repr, outputRepr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting %s to %s: %w", inputRepr, outputRepr, err)
|
||||
}
|
||||
|
||||
if viper.GetBool("verbose") {
|
||||
fmt.Fprintf(os.Stderr, "Converted to %s\n", outputRepr)
|
||||
}
|
||||
} else {
|
||||
result = repr
|
||||
}
|
||||
|
||||
// Marshal output.
|
||||
output, err := r.Marshal(result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling output: %w", err)
|
||||
}
|
||||
|
||||
// Write output file.
|
||||
err = os.WriteFile(outputPath, []byte(output), 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing output file: %w", err)
|
||||
}
|
||||
|
||||
if viper.GetBool("verbose") {
|
||||
fmt.Fprintf(os.Stderr, "Wrote %s to %s\n", outputRepr, outputPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
19
cmd/lambda/lambda_engine.go
Normal file
19
cmd/lambda/lambda_engine.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func LambdaEngine() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "engine",
|
||||
Short: "Information about available engines",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(LambdaEngineList())
|
||||
|
||||
return cmd
|
||||
}
|
||||
26
cmd/lambda/lambda_engine_list.go
Normal file
26
cmd/lambda/lambda_engine_list.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func LambdaEngineList() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List available engines",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
r := GetRegistry()
|
||||
|
||||
for engine := range r.ListEngines() {
|
||||
fmt.Println(engine.Name())
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
Reference in New Issue
Block a user