diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-24 12:31:44 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-24 12:31:44 +0300 |
| commit | 8d74ab861d9c80784e6a7692dffea52a1659dd09 (patch) | |
| tree | e672c72372fc4c5f3714550ad1485b92b3e87437 | |
| parent | a864beae16f3a1deaf6d7e29d573899e8aedf960 (diff) | |
rpn: split handleOperator into focused helpers
Extract handleOperator (~65 lines) into:
- checkAndPushSymbol(): :x syntax check and symbol push
- resolveVariableOrConstant(): variable and constant lookups
- dispatchOperator(): operator dispatch with symbol fallback
All helpers under 30 lines. Behavior preserved - all tests pass.
| -rw-r--r-- | internal/rpn/rpn_parse.go | 58 |
1 files changed, 34 insertions, 24 deletions
diff --git a/internal/rpn/rpn_parse.go b/internal/rpn/rpn_parse.go index 007ec00..d54df8a 100644 --- a/internal/rpn/rpn_parse.go +++ b/internal/rpn/rpn_parse.go @@ -351,47 +351,58 @@ func (r *RPN) handleOperator(stack *Stack, token string, tokenIndex int) (string return "", nil } - // Check if it's a symbol syntax (:x) - // Only match :x where x is a valid identifier (not an operator like := or =:) + // Check symbol syntax (:x) + if pushed, err := r.checkAndPushSymbol(stack, token); err != nil { + return "", err + } else if pushed { + return "", nil + } + + // Resolve variable or constant + if r.resolveVariableOrConstant(stack, token) { + return "", nil + } + + // Handle operators and fallback to symbol for unknown identifiers + return r.dispatchOperator(stack, token) +} + +// checkAndPushSymbol checks for :x syntax and pushes a Symbol if valid. +// Returns (true, nil) if pushed, (false, nil) if not a symbol prefix, or (false, error). +func (r *RPN) checkAndPushSymbol(stack *Stack, token string) (bool, error) { if len(token) > 0 && token[0] == ':' { symbolName := token[1:] // Remove the leading : if symbolName == "" { - return "", fmt.Errorf("symbol name cannot be empty after colon") + return false, fmt.Errorf("symbol name cannot be empty after colon") } - // Only push as symbol if the remaining part is a valid identifier - // This prevents := and =: from being treated as : followed by = operator + // Only push as symbol if valid identifier (prevents := and =: as : then =) if isValidIdentifier(symbolName) { stack.Push(NewSymbol(symbolName)) - return "", nil + return true, nil } - // Not a valid symbol, fall through to check for operators } + return false, nil +} - // Check if it's a variable reference first (before operators) +// resolveVariableOrConstant looks up token as variable or constant and pushes value. +func (r *RPN) resolveVariableOrConstant(stack *Stack, token string) bool { if val, exists := r.vars.GetVariable(token); exists { stack.Push(NewNumber(val, r.ops.GetMode())) - return "", nil + return true } - // Check if it's a constant reference (before operators) if val, exists := r.consts.GetConstant(token); exists { stack.Push(NewNumber(val, r.ops.GetMode())) - return "", nil + return true } - // Note: variable and constant values are stored as float64, so precision - // is already lost at storage time; NewNumber here preserves that behavior. + return false +} - // Handle standard operators (common logic extracted for DRY) - // This must be done BEFORE pushing Symbol for unknown identifiers, - // so that operators are properly handled +// dispatchOperator executes registered operators and falls back to symbol for unknown identifiers. +func (r *RPN) dispatchOperator(stack *Stack, token string) (string, error) { result, handled, err := r.executeOperator(stack, token) if err != nil { - // If it's an unknown token error and we're at the evaluate stage, - // it might be a bare identifier that should be a symbol - // Check if the caller is the main evaluate loop + // Unknown token: fall back to symbol if valid identifier if !r.opRegistry.IsStandardOperator(token) && !r.opRegistry.IsHyperOperator(token) { - // For bare identifiers, push a Symbol instead of returning error - // But only if it looks like a valid identifier (alphanumeric/underscore, starts with letter/_) - // Don't push symbols for tokens with special characters like %, ., etc. if isValidIdentifier(token) { stack.Push(NewSymbol(token)) return "", nil @@ -403,8 +414,7 @@ func (r *RPN) handleOperator(stack *Stack, token string, tokenIndex int) (string return result, nil } - // For bare identifiers that don't exist as variables and aren't operators, - // push a Symbol (this implements the feature where unbound identifiers act as symbols) + // Bare identifier — push as symbol if isValidIdentifier(token) { stack.Push(NewSymbol(token)) } |
