feat: better structured internal

This commit is contained in:
2025-12-25 17:21:16 -05:00
parent d9deee0734
commit 3351eaddfc
8 changed files with 102 additions and 31 deletions

View File

@@ -7,35 +7,37 @@ import (
"time"
"git.maximhutz.com/max/lambda/internal/cli"
"git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/lambda"
"git.maximhutz.com/max/lambda/pkg/parser"
"git.maximhutz.com/max/lambda/pkg/tokenizer"
)
// Run main application.
func main() {
options, err := cli.ParseOptions(os.Args[1:])
options, err := config.FromArgs()
cli.HandleError(err)
logger := cli.GetLogger(*options)
logger := options.GetLogger()
logger.Info("Using program arguments.", "args", os.Args)
logger.Info("Parsed CLI options.", "options", options)
if options.Input == "-" {
options.Input, err = cli.ReadInput()
input, err := options.Source.Pull()
cli.HandleError(err)
}
tokens, fails := tokenizer.GetTokens([]rune(options.Input))
// Parse tokens.
tokens, fails := tokenizer.GetTokens([]rune(input))
if len(fails) > 0 {
cli.HandleError(errors.Join(fails...))
}
logger.Info("Parsed tokens.", "tokens", tokens)
// Turn tokens into syntax tree.
expression, err := parser.GetTree(tokens)
cli.HandleError(err)
logger.Info("Parsed syntax tree.", "tree", lambda.Stringify(expression))
// Reduce expression.
start := time.Now()
if options.Explanation {

View File

@@ -5,18 +5,24 @@ import (
"fmt"
)
// Arguments given to program.
type CLIOptions struct {
// The source code given to the program.
Input string
// Whether or not to print debug logs.
Verbose bool
// Whether or not to print an explanation of the reduction.
Explanation bool
}
func ParseOptions(args []string) (*CLIOptions, error) {
// Parse flags and arguments.
// Extract the program configuration from the command-line arguments.
func ParseOptions() (*CLIOptions, error) {
// Parse 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.")
flag.Parse()
// Parse non-flag arguments.
if flag.NArg() == 0 {
return nil, fmt.Errorf("No input given.")
} else if flag.NArg() > 1 {

View File

@@ -5,6 +5,8 @@ import (
"os"
)
// A helper function to handle errors in the program. If it is given an error,
// the program will exist, and print the error.
func HandleError(err error) {
if err == nil {
return

View File

@@ -1,15 +0,0 @@
package cli
import (
"io"
"os"
)
func ReadInput() (string, error) {
data, err := io.ReadAll(os.Stdin)
if err != nil {
return "", err
}
return string(data), nil
}

11
internal/config/config.go Normal file
View File

@@ -0,0 +1,11 @@
package config
// Arguments given to program.
type Config struct {
// The source code given to the program.
Source Source
// Whether or not to print debug logs.
Verbose bool
// Whether or not to print an explanation of the reduction.
Explanation bool
}

View File

@@ -1,13 +1,14 @@
package cli
package config
import (
"log/slog"
"os"
)
func GetLogger(o CLIOptions) *slog.Logger {
// Define the correct logger for the program to use.
func (this Config) GetLogger() *slog.Logger {
var level slog.Level
if o.Verbose {
if this.Verbose {
level = slog.LevelInfo
} else {
level = slog.LevelError

View File

@@ -0,0 +1,35 @@
package config
import (
"flag"
"fmt"
)
// Extract the program configuration from the command-line arguments.
func FromArgs() (*Config, error) {
// Parse 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.")
flag.Parse()
// Parse non-flag arguments.
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{}
} else {
source = StringSource{data: flag.Arg(0)}
}
return &Config{
Source: source,
Verbose: *verbose,
Explanation: *explanation,
}, nil
}

29
internal/config/source.go Normal file
View File

@@ -0,0 +1,29 @@
package config
import (
"io"
"os"
)
// Defines the consumption of different types of input sources.
type Source interface {
// Get the data.
Pull() (string, error)
}
// A source coming from a string.
type StringSource struct{ data string }
func (this StringSource) Pull() (string, error) { return this.data, nil }
// A source coming from standard input.
type StdinSource struct{}
func (this StdinSource) Pull() (string, error) {
data, err := io.ReadAll(os.Stdin)
if err != nil {
return "", err
}
return string(data), nil
}