diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-25 17:12:13 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-25 17:12:13 +0200 |
| commit | 7065e66197d8994ab48dd1d313463d8ff6dfcf2b (patch) | |
| tree | 772975256463e118b59c362d91077da61ec765fb /cmd | |
| parent | 1c057b7d17feaff8d066b6f5d0fd9d8a5607611a (diff) | |
refactor: Split internal/rpn/rpn.go into separate files for better SRP
Created new files for better separation of concerns:
- internal/rpn/mode.go: CalculationMode enum
- internal/rpn/rpn_state.go: RPN struct and state management
- internal/rpn/rpn_ops.go: Operator execution and evaluation
- internal/rpn/rpn_parse.go: Parsing and assignment handling
Original rpn.go now contains only the RPN struct and constructor.
Benefits:
- Better Single Responsibility Principle (SRP)
- Improved code organization and readability
- Easier to maintain and test individual components
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/gt/main.go.bak | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/cmd/gt/main.go.bak b/cmd/gt/main.go.bak new file mode 100644 index 0000000..eccabf7 --- /dev/null +++ b/cmd/gt/main.go.bak @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 Paul Buetow + +// Package gt provides a command-line percentage calculator with RPN support. +// +// gt is a versatile calculator that supports both percentage calculations and +// Reverse Polish Notation (RPN) expressions. It can be used in two modes: +// +// 1. Command-line mode: Pass calculations as arguments +// gt 20% of 150 # Calculate 20% of 150 +// gt 3 4 + # RPN expression: 3 + 4 +// +// 2. Interactive REPL mode: Run without arguments to start an interactive session +// gt # Start interactive REPL +// +// Percentage Calculations +// +// The calculator supports various percentage formats: +// - Basic percentage: "20% of 150" → 30 +// - With prefix: "what is 20% of 150" → 30 +// - Reverse percentage: "30 is what % of 150" → 20% +// - Find base: "30 is 20% of what" → 150 +// +// RPN (Reverse Polish Notation) Support +// +// RPN expressions use postfix notation where operators follow operands: +// - Basic operations: "3 4 +" (3 + 4), "5 2 -" (5 - 2) +// - Complex expressions: "3 4 + 4 4 - *" ((3 + 4) * (4 - 4)) +// - Exponentiation: "2 3 ^" (2^3 = 8) +// - Variable assignment: "x 5 = x x +" (assign x=5, then x + x) +// - Stack operations: "dup swap pop show" +// +// Error Handling +// +// Errors from calculations or parsing are printed to stdout with exit code 1. +// Invalid RPN expressions and malformed percentage queries both return errors. +// +// Architecture +// +// The package uses a layered architecture: +// - main.go: Entry point and command routing +// - calculator/: Handles percentage calculation parsing +// - rpn/: Handles RPN expression parsing and evaluation +// - repl/: Provides interactive Read-Eval-Print Loop mode +// +// See the cmd/gt/internal package for version information. +package main + +import ( + "fmt" + "os" + "strings" + + "codeberg.org/snonux/perc/internal" + "codeberg.org/snonux/perc/internal/calculator" + "codeberg.org/snonux/perc/internal/repl" + "codeberg.org/snonux/perc/internal/rpn" + "github.com/mattn/go-isatty" +) + +// main is the entry point for the gt command-line calculator. +func main() { + output, err := runCommand(os.Args) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + fmt.Println(output) +} + +// runCommand processes command-line arguments and executes the appropriate action. +// +// It handles: +// - No arguments: Start REPL mode if stdin is a TTY, otherwise show usage +// - "version" argument: Return the version string +// - Other arguments: Try RPN parsing first, then fall back to percentage calculation +func runCommand(args []string) (string, error) { + if len(args) < 2 { + // No args provided - check if stdin is a TTY for REPL mode + if isatty.IsTerminal(os.Stdin.Fd()) { + if err := runREPL(); err != nil { + return "", err + } + return "", nil + } + printUsage() + return "", fmt.Errorf("no input provided") + } + + if args[1] == "version" { + return internal.Version, nil + } + + input := strings.Join(args[1:], " ") + + // Try RPN parsing first (for bare RPN expressions like "3 4 +") + rpnResult, rpnErr := runRPN(input) + if rpnErr == nil { + return rpnResult, nil + } + + // Fall back to percentage calculation + result, err := calculator.Parse(input) + if err != nil { + return "", fmt.Errorf("rpn fallback failed for input %q: %w", input, err) + } + + return result, nil +} + +// runREPL starts the interactive REPL mode. +// +// It wraps repl.RunREPL() and returns an error if the REPL fails to start. +func runREPL() error { + if err := repl.RunREPL(); err != nil { + return fmt.Errorf("REPL error: %w", err) + } + return nil +} + +// runRPN parses and evaluates an RPN (Reverse Polish Notation) expression. +// +// It creates a fresh RPN calculator with fresh variable store for each call, +// making it suitable for one-off calculations. +func runRPN(input string) (string, error) { + vars := rpn.NewVariables() + rpnCalc := rpn.NewRPN(vars) + return rpnCalc.ParseAndEvaluate(input) +} + +// printUsage displays the command-line usage information and examples. +func printUsage() { + fmt.Println("Usage: gt <calculation>") + fmt.Println(" gt version") + fmt.Println("\nPercentage calculator examples:") + fmt.Println(" gt 20% of 150") + fmt.Println(" gt what is 20% of 150") + fmt.Println(" gt 30 is what % of 150") + fmt.Println(" gt 30 is 20% of what") + fmt.Println("\nRPN (postfix notation) examples:") + fmt.Println(" gt 3 4 +") + fmt.Println(" gt 3 4 + 4 4 - *") + fmt.Println(" gt x 5 = x x +") + fmt.Println(" gt 2 3 ^") + fmt.Println(" gt dup swap pop show") + fmt.Println("\nStart REPL mode interactively by running without arguments:") + fmt.Println(" gt") +} |
