package utils import ( "fmt" "strconv" "strings" ) // ParseParams splits a line into space-separated parameters, respecting quoted strings func ParseParams(s string) ([]string, error) { s = strings.TrimSpace(s) if s == "" { return []string{}, nil } var params []string var current strings.Builder inString := false for i := 0; i < len(s); i++ { ch := s[i] if ch == '"' { inString = !inString current.WriteByte(ch) continue } if !inString && (ch == ' ' || ch == '\t') { if current.Len() > 0 { params = append(params, current.String()) current.Reset() } } else { current.WriteByte(ch) } } if current.Len() > 0 { params = append(params, current.String()) } if inString { return nil, fmt.Errorf("unterminated string") } return params, nil } // NormalizeSpaces reduces multiple spaces to single space, respecting quoted strings func NormalizeSpaces(s string) string { s = strings.TrimSpace(s) var result strings.Builder inString := false lastWasSpace := false for i := 0; i < len(s); i++ { ch := s[i] if ch == '"' { inString = !inString result.WriteByte(ch) lastWasSpace = false continue } if !inString { if ch == ' ' || ch == '\t' { if !lastWasSpace { result.WriteByte(' ') lastWasSpace = true } } else { result.WriteByte(ch) lastWasSpace = false } } else { result.WriteByte(ch) lastWasSpace = false } } return result.String() } // IsStringLiteral checks if s is a quoted string func IsStringLiteral(s string) bool { l := len(s) return l >= 2 && s[0] == '"' && s[l-1] == '"' } // StripQuotes removes surrounding quotes from a string literal func StripQuotes(s string) string { if IsStringLiteral(s) { return s[1 : len(s)-1] } return s } // ToUpper converts string to uppercase func ToUpper(s string) string { return strings.ToUpper(s) } // ValidateIdentifier checks if s is a valid identifier (starts with letter/underscore, continues with alphanumeric/underscore) func ValidateIdentifier(s string) bool { if len(s) == 0 { return false } first := s[0] if !((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_') { return false } for i := 1; i < len(s); i++ { ch := s[i] if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_') { return false } } return true } // ConstantLookup is a function type for looking up constant values by name type ConstantLookup func(name string) (value int64, found bool) // EvaluateExpression evaluates a simple left-to-right expression // Supports: // - Decimal: 123 // - Hex: $FF // - Binary: !11111111 // - Constants: MAXVAL (via lookup function) // - Operators: +, -, *, /, | (binary OR), & (binary AND) // // Returns value and error. If lookup is nil, constants are not supported. func EvaluateExpression(expr string, lookup ConstantLookup) (int64, error) { expr = strings.TrimSpace(expr) if expr == "" { return 0, fmt.Errorf("empty expression") } // Split expression into terms and operators var terms []string var operators []rune var currentTerm strings.Builder for i := 0; i < len(expr); i++ { ch := expr[i] if isOperator(ch) { // Save current term if currentTerm.Len() > 0 { terms = append(terms, strings.TrimSpace(currentTerm.String())) currentTerm.Reset() } operators = append(operators, rune(ch)) } else { currentTerm.WriteByte(ch) } } // Save final term if currentTerm.Len() > 0 { terms = append(terms, strings.TrimSpace(currentTerm.String())) } if len(terms) == 0 { return 0, fmt.Errorf("no terms in expression") } if len(operators) != len(terms)-1 { return 0, fmt.Errorf("mismatched operators and terms") } // Evaluate first term result, err := evaluateTerm(terms[0], lookup) if err != nil { return 0, fmt.Errorf("term %q: %w", terms[0], err) } // Apply operators left-to-right for i, op := range operators { nextVal, err := evaluateTerm(terms[i+1], lookup) if err != nil { return 0, fmt.Errorf("term %q: %w", terms[i+1], err) } switch op { case '+': result = result + nextVal case '-': result = result - nextVal case '*': result = result * nextVal case '/': if nextVal == 0 { return 0, fmt.Errorf("division by zero") } result = result / nextVal case '|': result = result | nextVal case '&': result = result & nextVal case '^': result = result ^ nextVal default: return 0, fmt.Errorf("unknown operator %q", op) } } return result, nil } // isOperator checks if a character is an operator func isOperator(ch byte) bool { return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '|' || ch == '&' } // evaluateTerm evaluates a single term (number or constant) func evaluateTerm(term string, lookup ConstantLookup) (int64, error) { term = strings.TrimSpace(term) if term == "" { return 0, fmt.Errorf("empty term") } // Check for hex: $FF if strings.HasPrefix(term, "$") { val, err := strconv.ParseInt(term[1:], 16, 64) if err != nil { return 0, fmt.Errorf("invalid hex number: %w", err) } return val, nil } // Check for binary: %11111111 if strings.HasPrefix(term, "%") { val, err := strconv.ParseInt(term[1:], 2, 64) if err != nil { return 0, fmt.Errorf("invalid binary number: %w", err) } return val, nil } // Check for identifier (constant) first := term[0] if (first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_' { if lookup == nil { return 0, fmt.Errorf("constant %q not supported (no lookup function)", term) } val, found := lookup(term) if !found { return 0, fmt.Errorf("constant %q not found", term) } return val, nil } // Decimal number val, err := strconv.ParseInt(term, 10, 64) if err != nil { return 0, fmt.Errorf("invalid decimal number: %w", err) } return val, nil }