Back to Powertoys

MCCS Capabilities String Parser - Recursive Descent Design

doc/devdocs/modules/powerdisplay/mccsParserDesign.md

0.98.16.6 KB
Original Source

MCCS Capabilities String Parser - Recursive Descent Design

Overview

This document describes the recursive descent parser implementation for DDC/CI MCCS (Monitor Control Command Set) capabilities strings.

Attention!

This document and the code implement are generated by Copilot.

Grammar Definition (BNF)

bnf
capabilities      ::= ['('] segment* [')']
segment           ::= identifier '(' segment_content ')'
segment_content   ::= text | vcp_entries | hex_list
vcp_entries       ::= vcp_entry*
vcp_entry         ::= hex_byte [ '(' hex_list ')' ]
hex_list          ::= hex_byte*
hex_byte          ::= [0-9A-Fa-f]{2}
identifier        ::= [a-z_A-Z]+
text              ::= [^()]+

Example Input

(prot(monitor)type(lcd)model(PD3220U)cmds(01 02 03 07)vcp(10 12 14(04 05 06) 16 60(11 12 0F) DC DF)mccs_ver(2.2)vcpname(F0(Custom Setting)))

Parser Architecture

Component Hierarchy

MccsCapabilitiesParser (main parser)
├── ParseCapabilities()      → MccsParseResult
├── ParseSegment()           → ParsedSegment?
├── ParseBalancedContent()   → string
├── ParseIdentifier()        → ReadOnlySpan<char>
├── ApplySegment()           → void
│   ├── ParseHexList()       → List<byte>
│   ├── ParseVcpEntries()    → Dictionary<byte, VcpCodeInfo>
│   └── ParseVcpNames()      → void
│
├── VcpEntryParser (sub-parser for vcp() content)
│   └── TryParseEntry()      → VcpEntry
│
├── VcpNameParser (sub-parser for vcpname() content)
│   └── TryParseEntry()      → (byte code, string name)
│
└── WindowParser (sub-parser for windowN() content)
    ├── Parse()              → WindowCapability
    └── ParseSubSegment()    → (name, content)?

Design Principles

  1. ref struct for Zero Allocation

    • Main parser uses ref struct to avoid heap allocation
    • Works with ReadOnlySpan<char> for efficient string slicing
    • No intermediate string allocations during parsing
  2. Recursive Descent Pattern

    • Each grammar rule has a corresponding parse method
    • Methods call each other recursively for nested structures
    • Single-character lookahead via Peek()
  3. Error Recovery

    • Errors are accumulated, not thrown
    • Parser attempts to continue after errors
    • Returns partial results when possible
  4. Sub-parsers for Specialized Content

    • VcpEntryParser for VCP code entries
    • VcpNameParser for custom VCP names
    • Each sub-parser handles its own grammar subset

Parse Methods Detail

ParseCapabilities()

Entry point. Handles optional outer parentheses and iterates through segments.

csharp
private MccsParseResult ParseCapabilities()
{
    // Handle optional outer parens
    // while (!IsAtEnd()) { ParseSegment() }
    // Return result with accumulated errors
}

ParseSegment()

Parses a single identifier(content) segment.

csharp
private ParsedSegment? ParseSegment()
{
    // 1. ParseIdentifier()
    // 2. Expect '('
    // 3. ParseBalancedContent()
    // 4. Expect ')'
}

ParseBalancedContent()

Extracts content between balanced parentheses, handling nested parens.

csharp
private string ParseBalancedContent()
{
    int depth = 1;
    while (depth > 0) {
        if (char == '(') depth++;
        if (char == ')') depth--;
    }
}

ParseVcpEntries()

Delegates to VcpEntryParser for the specialized VCP entry grammar.

csharp
vcp_entry ::= hex_byte [ '(' hex_list ')' ]

Examples:
- "10"           → code=0x10, values=[]
- "14(04 05 06)" → code=0x14, values=[4, 5, 6]
- "60(11 12 0F)" → code=0x60, values=[0x11, 0x12, 0x0F]

Comparison with Other Approaches

ApproachProsCons
Recursive Descent (this)Clear structure, handles nesting, extensibleMore code
Regex (DDCSharp)ConciseHard to debug, limited nesting
Mixed (original)PragmaticInconsistent, hard to maintain

Performance Characteristics

  • Time Complexity: O(n) where n = input length
  • Space Complexity: O(1) for parsing + O(m) for output where m = number of VCP codes
  • Allocations: Minimal - only for output structures

Supported Segments

SegmentDescriptionParser
prot(...)Protocol typeDirect assignment
type(...)Display type (lcd/crt)Direct assignment
model(...)Model nameDirect assignment
cmds(...)Supported commandsParseHexList
vcp(...)VCP code entriesVcpEntryParser
mccs_ver(...)MCCS versionDirect assignment
vcpname(...)Custom VCP namesVcpNameParser
windowN(...)PIP/PBP window capabilitiesWindowParser

Window Segment Format

The windowN segment (where N is 1, 2, 3, etc.) describes PIP/PBP window capabilities:

window1(type(PIP) area(25 25 1895 1175) max(640 480) min(10 10) window(10))
Sub-fieldFormatDescription
typetype(PIP) or type(PBP)Window type (Picture-in-Picture or Picture-by-Picture)
areaarea(x1 y1 x2 y2)Window area coordinates in pixels
maxmax(width height)Maximum window dimensions
minmin(width height)Minimum window dimensions
windowwindow(id)Window identifier

All sub-fields are optional; missing fields default to zero values.

Error Handling

csharp
public readonly struct ParseError
{
    public int Position { get; }    // Character position
    public string Message { get; }  // Human-readable error
}

public sealed class MccsParseResult
{
    public VcpCapabilities Capabilities { get; }
    public IReadOnlyList<ParseError> Errors { get; }
    public bool HasErrors => Errors.Count > 0;
    public bool IsValid => !HasErrors && Capabilities.SupportedVcpCodes.Count > 0;
}

Usage Example

csharp
// Parse capabilities string
var result = MccsCapabilitiesParser.Parse(capabilitiesString);

if (result.IsValid)
{
    var caps = result.Capabilities;
    Console.WriteLine($"Model: {caps.Model}");
    Console.WriteLine($"MCCS Version: {caps.MccsVersion}");
    Console.WriteLine($"VCP Codes: {caps.SupportedVcpCodes.Count}");
}

if (result.HasErrors)
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Parse error at {error.Position}: {error.Message}");
    }
}

Edge Cases Handled

  1. Missing outer parentheses (Apple Cinema Display)
  2. No spaces between hex bytes (010203 vs 01 02 03)
  3. Nested parentheses in VCP values
  4. Unknown segments (logged but not fatal)
  5. Malformed input (partial results returned)