From 62699a0e379a9224f9fe25bb9d9343457d564487 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Sun, 11 Jan 2026 21:43:11 +0000 Subject: [PATCH] feat: add file input flag (#12) ## Description The lambda CLI previously only supported inline string expressions and stdin input. This PR adds support for reading lambda expressions from files using the `-f` flag. This makes it easier to work with larger programs stored in files. Changes: - Added `FileSource` type to `internal/config/source.go` for reading from file paths. - Added `-f` flag to CLI argument parser with validation to prevent conflicting inputs. - Updated Makefile targets (`run`, `profile`, `explain`) to use `-f` flag instead of stdin redirection. ### Decisions The `-f` flag takes precedence over positional arguments. If both are specified, an error is returned to avoid ambiguity. ## Benefits - More intuitive workflow for file-based lambda programs. - Cleaner Makefile targets without stdin redirection. - Consistent with common CLI conventions (e.g., `grep -f`, `awk -f`). ## Checklist - [x] Code follows conventional commit format. - [x] Branch follows naming convention (`/`). - [ ] Tests pass (if applicable). - [ ] Documentation updated (if applicable). Reviewed-on: https://git.maximhutz.com/mvhutz/lambda/pulls/12 Co-authored-by: M.V. Hutz Co-committed-by: M.V. Hutz --- Makefile | 6 +++--- internal/config/parse_from_args.go | 27 +++++++++++++++++---------- internal/config/source.go | 12 ++++++++++++ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 45742d2..7f15f78 100644 --- a/Makefile +++ b/Makefile @@ -20,13 +20,13 @@ build: chmod +x ${BINARY_NAME} run: build - ./${BINARY_NAME} - < ./samples/$(TEST).txt > program.out + ./${BINARY_NAME} -f ./samples/$(TEST).txt > program.out profile: build - ./${BINARY_NAME} -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out + ./${BINARY_NAME} -p profile/cpu.prof -f ./samples/$(TEST).txt > program.out explain: build - ./${BINARY_NAME} -x -p profile/cpu.prof - < ./samples/$(TEST).txt > program.out + ./${BINARY_NAME} -x -p profile/cpu.prof -f ./samples/$(TEST).txt > program.out graph: go tool pprof -raw -output=profile/cpu.raw profile/cpu.prof diff --git a/internal/config/parse_from_args.go b/internal/config/parse_from_args.go index 6a2b688..41f738e 100644 --- a/internal/config/parse_from_args.go +++ b/internal/config/parse_from_args.go @@ -12,21 +12,28 @@ func FromArgs() (*Config, error) { 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 an output file is defined, the program will profile its execution and dump its results into it.") + file := flag.String("f", "", "File. If set, read source from the specified file.") flag.Parse() - // There must only be one input argument. - if flag.NArg() == 0 { - return nil, fmt.Errorf("no input given") - } else if flag.NArg() > 1 { - return nil, fmt.Errorf("more than 1 command-line argument") - } - // Parse source type. var source Source - if flag.Arg(0) == "-" { - source = StdinSource{} + if *file != "" { + // File flag takes precedence. + if flag.NArg() > 0 { + return nil, fmt.Errorf("cannot specify both -f flag and positional argument") + } + source = FileSource{Path: *file} + } else if flag.NArg() == 0 { + return nil, fmt.Errorf("no input given") + } else if flag.NArg() > 1 { + return nil, fmt.Errorf("more than 1 command-line argument") } else { - source = StringSource{Data: flag.Arg(0)} + // Positional argument. + if flag.Arg(0) == "-" { + source = StdinSource{} + } else { + source = StringSource{Data: flag.Arg(0)} + } } return &Config{ diff --git a/internal/config/source.go b/internal/config/source.go index 9996f0c..758eaa0 100644 --- a/internal/config/source.go +++ b/internal/config/source.go @@ -27,3 +27,15 @@ func (s StdinSource) Extract() (string, error) { return string(data), nil } + +// A source reading from a file. +type FileSource struct{ Path string } + +func (s FileSource) Extract() (string, error) { + data, err := os.ReadFile(s.Path) + if err != nil { + return "", err + } + + return string(data), nil +}