diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index 2f9116e..af63446 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -6,7 +6,7 @@ import ( "git.maximhutz.com/max/lambda/internal/cli" "git.maximhutz.com/max/lambda/internal/config" - "git.maximhutz.com/max/lambda/internal/executer" + "git.maximhutz.com/max/lambda/internal/engine" "git.maximhutz.com/max/lambda/internal/explanation" "git.maximhutz.com/max/lambda/internal/performance" "git.maximhutz.com/max/lambda/internal/statistics" @@ -15,8 +15,8 @@ import ( "git.maximhutz.com/max/lambda/pkg/saccharine" ) -// Run main application. func main() { + // Parse CLI arguments. options, err := config.FromArgs() cli.HandleError(err) @@ -24,7 +24,8 @@ func main() { logger.Info("using program arguments", "args", os.Args) logger.Info("parsed CLI options", "options", options) - input, err := options.Source.Pull() + // Get input. + input, err := options.Source.Extract() cli.HandleError(err) // Parse tokens. @@ -35,33 +36,46 @@ func main() { // Turn tokens into syntax tree. expression, err := saccharine.Parse(tokens) cli.HandleError(err) - if options.Verbose { - logger.Info("parsed syntax tree", "tree", saccharine.Stringify(expression)) - } + logger.Info("parsed syntax tree", "tree", saccharine.Stringify(expression)) + // Compile expression to lambda calculus. compiled := convert.SaccharineToLambda(expression) - if options.Verbose { - logger.Info("compiled lambda expression", "tree", lambda.Stringify(compiled)) - } + logger.Info("compiled lambda expression", "tree", lambda.Stringify(compiled)) - process := executer.New(options, &compiled) + // Create reduction engine. + process := engine.New(options, &compiled) + + // If the user selected to track CPU performance, attach a profiler to the + // process. if options.Profile != "" { profiler := performance.Track(options.Profile) process.On("start", profiler.Start) process.On("end", profiler.End) } + // If the user selected to produce a step-by-step explanation, attach an + // observer here. if options.Explanation { explanation.Track(process) } - statistics := statistics.Track() - process.On("start", statistics.Start) - process.On("step", statistics.Step) - process.On("end", statistics.End) + // If the user opted to track statistics, attach a tracker here, too. + if options.Statistics { + statistics := statistics.Track() + process.On("start", statistics.Start) + process.On("step", statistics.Step) + process.On("end", statistics.End) + } + + // If the user selected for verbose debug logs, attach a reduction tracker. + if options.Verbose { + process.On("step", func() { + logger.Info("reduction", "tree", lambda.Stringify(compiled)) + }) + } process.Run() + // Return the final reduced result. fmt.Println(lambda.Stringify(compiled)) - fmt.Fprint(os.Stderr, statistics.Results.String()) } diff --git a/internal/config/config.go b/internal/config/config.go index 75030fb..757f55b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,9 +1,10 @@ package config -// Arguments given to program. +// Configuration settings for the program. type Config struct { Source Source // The source code given to the program. Verbose bool // Whether or not to print debug logs. Explanation bool // Whether or not to print an explanation of the reduction. Profile string // If not nil, print a CPU profile during execution. + Statistics bool // Whether or not to print statistics. } diff --git a/internal/config/get_logger.go b/internal/config/get_logger.go index 6f959ad..8880878 100644 --- a/internal/config/get_logger.go +++ b/internal/config/get_logger.go @@ -5,13 +5,14 @@ import ( "os" ) -// Define the correct logger for the program to use. +// Returns a structured logger with the appropriate configurations. func (c Config) GetLogger() *slog.Logger { - var level slog.Level + // By default, only print out errors. + level := slog.LevelError + + // If the user set the output to be "VERBOSE", return the debug logs. if c.Verbose { level = slog.LevelInfo - } else { - level = slog.LevelError } return slog.New( diff --git a/internal/config/parse_from_args.go b/internal/config/parse_from_args.go index 8b92d9f..9cbf870 100644 --- a/internal/config/parse_from_args.go +++ b/internal/config/parse_from_args.go @@ -7,13 +7,14 @@ import ( // Extract the program configuration from the command-line arguments. func FromArgs() (*Config, error) { - // Parse flags. + // Relevant flags. verbose := flag.Bool("v", false, "Verbosity. If set, the program will print logs.") explanation := flag.Bool("x", false, "Explanation. Whether or not to show all reduction steps.") + statistics := flag.Bool("s", false, "Statistics. If set, the process will print various statistics about the run.") profile := flag.String("p", "", "CPU profiling. If set, the program will run a performance profile during execution.") flag.Parse() - // Parse non-flag arguments. + // There must only be one input argument. if flag.NArg() == 0 { return nil, fmt.Errorf("no input given") } else if flag.NArg() > 1 { @@ -25,7 +26,7 @@ func FromArgs() (*Config, error) { if flag.Arg(0) == "-" { source = StdinSource{} } else { - source = StringSource{data: flag.Arg(0)} + source = StringSource{Data: flag.Arg(0)} } return &Config{ @@ -33,5 +34,6 @@ func FromArgs() (*Config, error) { Verbose: *verbose, Explanation: *explanation, Profile: *profile, + Statistics: *statistics, }, nil } diff --git a/internal/config/source.go b/internal/config/source.go index 7e78a98..9996f0c 100644 --- a/internal/config/source.go +++ b/internal/config/source.go @@ -5,21 +5,21 @@ import ( "os" ) -// Defines the consumption of different types of input sources. +// A method of extracting input from the user. type Source interface { - // Get the data. - Pull() (string, error) + // Fetch data from this source. + Extract() (string, error) } -// A source coming from a string. -type StringSource struct{ data string } +// A source defined by a string. +type StringSource struct{ Data string } -func (s StringSource) Pull() (string, error) { return s.data, nil } +func (s StringSource) Extract() (string, error) { return s.Data, nil } -// A source coming from standard input. +// A source pulling from standard input. type StdinSource struct{} -func (s StdinSource) Pull() (string, error) { +func (s StdinSource) Extract() (string, error) { data, err := io.ReadAll(os.Stdin) if err != nil { return "", err diff --git a/internal/executer/executer.go b/internal/engine/engine.go similarity index 61% rename from internal/executer/executer.go rename to internal/engine/engine.go index d3fa123..4ebbf8b 100644 --- a/internal/executer/executer.go +++ b/internal/engine/engine.go @@ -1,31 +1,26 @@ -package executer +package engine import ( - "log/slog" - "git.maximhutz.com/max/lambda/internal/config" "git.maximhutz.com/max/lambda/pkg/emitter" "git.maximhutz.com/max/lambda/pkg/lambda" ) -type Executor struct { +type Engine struct { Config *config.Config Expression *lambda.Expression emitter.Emitter } -func New(config *config.Config, expression *lambda.Expression) *Executor { - return &Executor{Config: config, Expression: expression} +func New(config *config.Config, expression *lambda.Expression) *Engine { + return &Engine{Config: config, Expression: expression} } -func (e Executor) Run() { +func (e Engine) Run() { e.Emit("start") for lambda.ReduceOnce(e.Expression) { e.Emit("step") - if e.Config.Verbose { - slog.Info("reduction", "tree", lambda.Stringify(*e.Expression)) - } } e.Emit("end") diff --git a/internal/explanation/tracker.go b/internal/explanation/tracker.go index 9e9ef98..48ee5b0 100644 --- a/internal/explanation/tracker.go +++ b/internal/explanation/tracker.go @@ -3,15 +3,15 @@ package explanation import ( "fmt" - "git.maximhutz.com/max/lambda/internal/executer" + "git.maximhutz.com/max/lambda/internal/engine" "git.maximhutz.com/max/lambda/pkg/lambda" ) type Tracker struct { - process *executer.Executor + process *engine.Engine } -func Track(process *executer.Executor) *Tracker { +func Track(process *engine.Engine) *Tracker { tracker := &Tracker{process: process} process.On("start", tracker.Start) process.On("step", tracker.Step) diff --git a/internal/statistics/tracker.go b/internal/statistics/tracker.go index c1e2d20..0f4b4ff 100644 --- a/internal/statistics/tracker.go +++ b/internal/statistics/tracker.go @@ -1,13 +1,14 @@ package statistics import ( + "fmt" + "os" "time" ) type Tracker struct { - start time.Time - steps uint64 - Results *Results + start time.Time + steps uint64 } func Track() *Tracker { @@ -17,7 +18,6 @@ func Track() *Tracker { func (t *Tracker) Start() { t.start = time.Now() t.steps = 0 - t.Results = nil } func (t *Tracker) Step() { @@ -25,8 +25,10 @@ func (t *Tracker) Step() { } func (t *Tracker) End() { - t.Results = &Results{ + results := Results{ StepsTaken: t.steps, TimeElapsed: uint64(time.Since(t.start).Milliseconds()), } + + fmt.Fprint(os.Stderr, results.String()) }