Main Parser
class Parser {
[string[]]$Tokens
[int]$Position = 0
Parser([string]$input) {
# Tokenize: split by spaces but keep parentheses
$this.Tokens = $input.Replace('(',' ( ').Replace(')',' ) ').Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)
}
[string] Peek() {
if ($this.Position -lt $this.Tokens.Count) { return $this.Tokens[$this.Position] }
return $null
}
[string] Consume() {
return $this.Tokens[$this.Position++]
}
# Level 1: OR (Lowest precedence)
[hashtable] ParseExpression() {
$node = $this.ParseTerm()
while ($this.Peek() -eq "OR") {
$operator = $this.Consume()
$right = $this.ParseTerm()
$node = @{ Operator = $operator; Left = $node; Right = $right }
}
return $node
}
# Level 2: AND
[hashtable] ParseTerm() {
$node = $this.ParseFactor()
while ($this.Peek() -eq "AND") {
$operator = $this.Consume()
$right = $this.ParseFactor()
$node = @{ Operator = $operator; Left = $node; Right = $right }
}
return $node
}
# Level 3: Values and Parentheses (Highest precedence)
[hashtable] ParseFactor() {
$token = $this.Consume()
if ($token -eq "(") {
$node = $this.ParseExpression()
[void]$this.Consume() # Consume matching ')'
return $node
}
return @{ Value = $token }
}
}
Usage
$Query = “Status AND (User OR Admin)”
$Parser = [Parser]::new($Query)
$OutputTree = $Parser.ParseExpression()
Display as JSON to see the nested structure clearly
$OutputTree | ConvertTo-Json -Depth 10