Extending OptParse
This page is the procedural part of the development guide: what to do when you add a new value parser or parser family.
Adding A New Value Parser
The shortest path is:
- add a new file under
src/parsers/valueparsers/ - define a concrete error struct
<: AbstractParseErrorwith an@enumcode field - define
render_error(io::IO, err::YourError)for the error type - define the concrete value parser struct
<: AbstractValueParser{T, E} - define its call overload
(::YourValueParser)(input::String)::ParseResult{T, E} - include it from
src/parsers/valueparsers/valueparsers.jl - add exported constructor functions and docstrings in
src/parsers/parser.jl - add display support in
src/display/parser_show.jlif needed - add unit tests
- add it to
docs/src/reference.mdif it is public
Expected shape
Use existing value parser files as templates, especially:
string.jlinteger.jlfloat.jl
Typical pieces:
@enum MyValErrCode::UInt8 begin
MYVAL_SomeError
end
struct MyValError <: AbstractParseError
code::MyValErrCode
token::String
end
function render_error(io::IO, err::MyValError)
...
end
struct MyVal{T} <: AbstractValueParser{T, MyValError}
metavar::String
...
end
function (p::MyVal{T})(input::String)::ParseResult{T, MyValError} where {T}
...
endConstructor placement
Public constructor functions belong in src/parsers/parser.jl, not in the leaf file.
That file is the public API layer for value parsers and should contain:
- the public constructor names
- positional
metavaroverloads when appropriate - docstrings
Positional metavar style
The current preferred API is:
str("FILE")
integer("PORT")
flt("RATIO")
choice("MODE", values)
path("FILE")The metavar = ... keyword still exists at the lower level, but docstrings and examples should prefer the positional form.
Value parser checklist
Before considering a value parser done, verify:
- parse returns
ParseResult{T, E} - a concrete error struct
<: AbstractParseErrorexists for the family render_error(io, err)renders it correctly- the public constructor is exported if intended to be public
showoutput is readable- docstrings exist
- reference docs include the constructor
- unit tests cover success, failure, and
@test_opt
Adding A New Parser Family
The shortest path is:
- decide whether it is a primitive, constructor, or modifier
- add the concrete family file under the appropriate
src/parsers/...subdirectory - define a family-specific state alias
- define an error struct
<: AbstractParseErrorwith@enumcodes and arender_errormethod - define the concrete parser struct
<: AbstractParser{T, E, S, P, R} - implement
parse - implement
complete - include it from the family include file
- make sure child parser fields stay concrete and parametric
- add the public constructor function and docstring in
src/parsers/parser.jl - add
show_children/printnodesupport if needed - add tests
- add docs, reference entries, and examples if public
Start from a state alias
Always define a parser-family-specific state alias first.
Examples:
const GateState = ParseResult{Bool, ArgGateError}
const OptionState{T, E} = ParseResult{T, E}
const CommandState{S} = Option{Option{S}}
const MultipleState{S} = Vector{S}This usually clarifies the rest of the implementation immediately.
Tight signatures
Use the family alias in method signatures:
function parse(p::ArgGate{Bool, ArgGateError, GateState}, ctx::Context{GateState})::InnerParseResult{GateState, ArgGateError}Do not leave parser family methods broadly typed unless there is a very strong reason.
Public constructor placement
Public constructor functions belong in src/parsers/parser.jl.
That file is the user-facing API layer for parser families:
- exported names
- docstrings
- curried convenience overloads where needed
Parser-family checklist
Before considering a parser family done, verify:
- the state alias is explicit
parseandcompletesignatures reflect the real invariant state type- family-specific errors render correctly
- child parser fields stay concrete and parametric
- the public constructor exists and is exported if public
showoutput is useful- docs/examples/reference mention the new parser if public
- tests cover both behavior and inference
Display And Docstring Conventions
Display extension points
The display interface has three extension points:
Base.show(io, p)— compact inline representation (required)show_children(p)— returns children for tree display, ornothingfor leaves (optional)printnode(io, p)— tree header label, defaults toshow(optional)
Public names should match displayed names
If the public constructor is command(...), the parser pretty-printer should show command(...), not an older or internal alias.
Likewise:
flag(...)should display asflag(...)switch(...)should display asswitch(...)sequence(...)should display assequence(...)
One subtle exception exists right now:
flag(...)is implemented asdefault(switch(...), false)- the pretty-printer special-cases this shape so public
flag(...)still displays asflag(...)
Reference docs must mention exported bindings explicitly
Documenter will complain if:
- an exported name has a docstring
- but the reference page does not include it in an
@docsblock
So when adding a public constructor, always update:
docs/src/reference.md