feat: statistics flag, commented some more

This commit is contained in:
2025-12-29 20:00:29 -05:00
parent 3f9f3a603f
commit 351faa7e08
8 changed files with 64 additions and 49 deletions

View File

@@ -6,7 +6,7 @@ import (
"git.maximhutz.com/max/lambda/internal/cli" "git.maximhutz.com/max/lambda/internal/cli"
"git.maximhutz.com/max/lambda/internal/config" "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/explanation"
"git.maximhutz.com/max/lambda/internal/performance" "git.maximhutz.com/max/lambda/internal/performance"
"git.maximhutz.com/max/lambda/internal/statistics" "git.maximhutz.com/max/lambda/internal/statistics"
@@ -15,8 +15,8 @@ import (
"git.maximhutz.com/max/lambda/pkg/saccharine" "git.maximhutz.com/max/lambda/pkg/saccharine"
) )
// Run main application.
func main() { func main() {
// Parse CLI arguments.
options, err := config.FromArgs() options, err := config.FromArgs()
cli.HandleError(err) cli.HandleError(err)
@@ -24,7 +24,8 @@ func main() {
logger.Info("using program arguments", "args", os.Args) logger.Info("using program arguments", "args", os.Args)
logger.Info("parsed CLI options", "options", options) logger.Info("parsed CLI options", "options", options)
input, err := options.Source.Pull() // Get input.
input, err := options.Source.Extract()
cli.HandleError(err) cli.HandleError(err)
// Parse tokens. // Parse tokens.
@@ -35,33 +36,46 @@ func main() {
// Turn tokens into syntax tree. // Turn tokens into syntax tree.
expression, err := saccharine.Parse(tokens) expression, err := saccharine.Parse(tokens)
cli.HandleError(err) 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) 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 != "" { if options.Profile != "" {
profiler := performance.Track(options.Profile) profiler := performance.Track(options.Profile)
process.On("start", profiler.Start) process.On("start", profiler.Start)
process.On("end", profiler.End) process.On("end", profiler.End)
} }
// If the user selected to produce a step-by-step explanation, attach an
// observer here.
if options.Explanation { if options.Explanation {
explanation.Track(process) explanation.Track(process)
} }
statistics := statistics.Track() // If the user opted to track statistics, attach a tracker here, too.
process.On("start", statistics.Start) if options.Statistics {
process.On("step", statistics.Step) statistics := statistics.Track()
process.On("end", statistics.End) 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() process.Run()
// Return the final reduced result.
fmt.Println(lambda.Stringify(compiled)) fmt.Println(lambda.Stringify(compiled))
fmt.Fprint(os.Stderr, statistics.Results.String())
} }

View File

@@ -1,9 +1,10 @@
package config package config
// Arguments given to program. // Configuration settings for the program.
type Config struct { type Config struct {
Source Source // The source code given to the program. Source Source // The source code given to the program.
Verbose bool // Whether or not to print debug logs. Verbose bool // Whether or not to print debug logs.
Explanation bool // Whether or not to print an explanation of the reduction. Explanation bool // Whether or not to print an explanation of the reduction.
Profile string // If not nil, print a CPU profile during execution. Profile string // If not nil, print a CPU profile during execution.
Statistics bool // Whether or not to print statistics.
} }

View File

@@ -5,13 +5,14 @@ import (
"os" "os"
) )
// Define the correct logger for the program to use. // Returns a structured logger with the appropriate configurations.
func (c Config) GetLogger() *slog.Logger { 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 { if c.Verbose {
level = slog.LevelInfo level = slog.LevelInfo
} else {
level = slog.LevelError
} }
return slog.New( return slog.New(

View File

@@ -7,13 +7,14 @@ import (
// Extract the program configuration from the command-line arguments. // Extract the program configuration from the command-line arguments.
func FromArgs() (*Config, error) { func FromArgs() (*Config, error) {
// Parse flags. // Relevant flags.
verbose := flag.Bool("v", false, "Verbosity. If set, the program will print logs.") 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.") 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.") profile := flag.String("p", "", "CPU profiling. If set, the program will run a performance profile during execution.")
flag.Parse() flag.Parse()
// Parse non-flag arguments. // There must only be one input argument.
if flag.NArg() == 0 { if flag.NArg() == 0 {
return nil, fmt.Errorf("no input given") return nil, fmt.Errorf("no input given")
} else if flag.NArg() > 1 { } else if flag.NArg() > 1 {
@@ -25,7 +26,7 @@ func FromArgs() (*Config, error) {
if flag.Arg(0) == "-" { if flag.Arg(0) == "-" {
source = StdinSource{} source = StdinSource{}
} else { } else {
source = StringSource{data: flag.Arg(0)} source = StringSource{Data: flag.Arg(0)}
} }
return &Config{ return &Config{
@@ -33,5 +34,6 @@ func FromArgs() (*Config, error) {
Verbose: *verbose, Verbose: *verbose,
Explanation: *explanation, Explanation: *explanation,
Profile: *profile, Profile: *profile,
Statistics: *statistics,
}, nil }, nil
} }

View File

@@ -5,21 +5,21 @@ import (
"os" "os"
) )
// Defines the consumption of different types of input sources. // A method of extracting input from the user.
type Source interface { type Source interface {
// Get the data. // Fetch data from this source.
Pull() (string, error) Extract() (string, error)
} }
// A source coming from a string. // A source defined by a string.
type StringSource struct{ data 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{} type StdinSource struct{}
func (s StdinSource) Pull() (string, error) { func (s StdinSource) Extract() (string, error) {
data, err := io.ReadAll(os.Stdin) data, err := io.ReadAll(os.Stdin)
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -1,31 +1,26 @@
package executer package engine
import ( import (
"log/slog"
"git.maximhutz.com/max/lambda/internal/config" "git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/emitter" "git.maximhutz.com/max/lambda/pkg/emitter"
"git.maximhutz.com/max/lambda/pkg/lambda" "git.maximhutz.com/max/lambda/pkg/lambda"
) )
type Executor struct { type Engine struct {
Config *config.Config Config *config.Config
Expression *lambda.Expression Expression *lambda.Expression
emitter.Emitter emitter.Emitter
} }
func New(config *config.Config, expression *lambda.Expression) *Executor { func New(config *config.Config, expression *lambda.Expression) *Engine {
return &Executor{Config: config, Expression: expression} return &Engine{Config: config, Expression: expression}
} }
func (e Executor) Run() { func (e Engine) Run() {
e.Emit("start") e.Emit("start")
for lambda.ReduceOnce(e.Expression) { for lambda.ReduceOnce(e.Expression) {
e.Emit("step") e.Emit("step")
if e.Config.Verbose {
slog.Info("reduction", "tree", lambda.Stringify(*e.Expression))
}
} }
e.Emit("end") e.Emit("end")

View File

@@ -3,15 +3,15 @@ package explanation
import ( import (
"fmt" "fmt"
"git.maximhutz.com/max/lambda/internal/executer" "git.maximhutz.com/max/lambda/internal/engine"
"git.maximhutz.com/max/lambda/pkg/lambda" "git.maximhutz.com/max/lambda/pkg/lambda"
) )
type Tracker struct { 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} tracker := &Tracker{process: process}
process.On("start", tracker.Start) process.On("start", tracker.Start)
process.On("step", tracker.Step) process.On("step", tracker.Step)

View File

@@ -1,13 +1,14 @@
package statistics package statistics
import ( import (
"fmt"
"os"
"time" "time"
) )
type Tracker struct { type Tracker struct {
start time.Time start time.Time
steps uint64 steps uint64
Results *Results
} }
func Track() *Tracker { func Track() *Tracker {
@@ -17,7 +18,6 @@ func Track() *Tracker {
func (t *Tracker) Start() { func (t *Tracker) Start() {
t.start = time.Now() t.start = time.Now()
t.steps = 0 t.steps = 0
t.Results = nil
} }
func (t *Tracker) Step() { func (t *Tracker) Step() {
@@ -25,8 +25,10 @@ func (t *Tracker) Step() {
} }
func (t *Tracker) End() { func (t *Tracker) End() {
t.Results = &Results{ results := Results{
StepsTaken: t.steps, StepsTaken: t.steps,
TimeElapsed: uint64(time.Since(t.start).Milliseconds()), TimeElapsed: uint64(time.Since(t.start).Milliseconds()),
} }
fmt.Fprint(os.Stderr, results.String())
} }