-
Notifications
You must be signed in to change notification settings - Fork 0
Syntax
JNode's syntax system parses, validates, and auto-completes command-line arguments using declarative Argument objects and Syntax trees, all before a Command's execute() is called.
The syntax system (org.jnode.shell.syntax) is a declarative argument-parsing framework for shell commands. Unlike conventional String[] args parsing, it uses Java objects to describe expected arguments, and a parser (MuParser) to validate and populate them. This provides:
- Automatic validation — arguments are type-checked and constrained before execute() runs
- Tab-completion — argument subtypes provide completions based on their domain (files, integers, etc.)
- Help text — argument descriptions are used to generate usage messages
- Multiple syntaxes — a command can support multiple argument patterns via SyntaxBundle
| Class / File | Role |
|---|---|
shell/src/shell/org/jnode/shell/syntax/Argument.java |
Base class for typed argument holders (FileArgument, StringArgument, FlagArgument, etc.) |
shell/src/shell/org/jnode/shell/syntax/Syntax.java |
Abstract syntax tree node (SequenceSyntax, OptionSyntax, RepeatSyntax, etc.) |
shell/src/shell/org/jnode/shell/syntax/SyntaxBundle.java |
Container holding one or more Syntax alternatives for a command alias |
shell/src/shell/org/jnode/shell/syntax/SyntaxManager.java |
Service that maps command aliases to SyntaxBundles |
shell/src/shell/org/jnode/shell/syntax/MuParser.java |
Backtracking parser that validates tokens against a MuSyntax tree |
shell/src/shell/org/jnode/shell/syntax/MuSyntax.java |
Compiled "micro" syntax tree used by MuParser |
shell/src/shell/org/jnode/shell/syntax/ArgumentBundle.java |
Container holding Argument instances for a command |
Commands declare their arguments by instantiating Argument subclasses:
public class MyCommand extends AbstractCommand {
private final StringArgument nameArg = new StringArgument("name", Argument.MANDATORY, "Your name");
private final FileArgument fileArg = new FileArgument("file", Argument.EXISTING, "Input file");
private final FlagArgument verboseFlag = new FlagArgument("verbose", Argument.OPTIONAL, "Enable verbose output");
public MyCommand() {
super("My command description");
registerArguments(nameArg, fileArg, verboseFlag);
}
public void execute() throws Exception {
// nameArg.getValue(), fileArg.getValues(), verboseFlag.isSet() are populated
// validation already passed at this point
}
}When a command is invoked, SyntaxManager looks up its SyntaxBundle. The Syntax.prepare() method compiles each Syntax into a MuSyntax tree:
- SequenceSyntax — sequences of elements (argument, symbol, sub-syntax)
-
OptionSyntax — option patterns like
-o,--output <arg> -
RepeatSyntax — repetition (
*,+) -
AlternativesSyntax — multiple alternative patterns (e.g.,
cp src destvscp -r src dest) - GroupSyntax — group with label for labeling purposes
MuParser performs backtracking parse against the MuSyntax:
- Reads tokens from CommandLine
- Matches symbols (
MuSymbol) or arguments (MuArgument) - For MuArgument, calls
Argument.accept(token, flags)which validates and stores the value - On failure, backtracks and tries alternatives
- On success, all Arguments in the bundle are populated before execute() is called
Tab-completion uses the same MuParser with a CompletionInfo collector. When parsing reaches a partial token and needs an argument:
- MuParser calls
Argument.complete(completions, partial, flags) - The Argument subtype provides domain-specific completions (e.g., FileArgument lists files in current directory)
- Completions are added to CompletionInfo and presented to the user
Arguments use bitwise flags to constrain behavior:
| Flag | Meaning |
|---|---|
MANDATORY |
Must be supplied; validation fails if missing |
OPTIONAL |
May be omitted (default if neither set) |
SINGLE |
At most one value (default if neither set) |
MULTIPLE |
Zero or more values allowed |
EXISTING |
Value must denote an existing entity (file, device, etc.) |
NONEXISTENT |
Value must NOT exist (for create operations) |
Flags can be combined: Argument.MANDATORY | Argument.MULTIPLE requires at least one value.
The syntax package provides many Argument subclasses:
-
Value types:
StringArgument,IntegerArgument,LongArgument -
Files:
FileArgument(validates file existence, type) -
Network:
HostNameArgument,PortNumberArgument,URLArgument -
System:
ThreadNameArgument,PropertyNameArgument,PluginArgument -
Special:
FlagArgument(boolean presence),EnumArgument(enum values),MappedArgument
Syntaxes can be declared declaratively in plugin descriptors for non-native commands:
<extension point="org.jnode.shell.syntaxes">
<argument-bundle alias="mycmd">
<argument label="name" class="org.jnode.shell.syntax.StringArgument" flags="MANDATORY"/>
</argument-bundle>
</extension>- Labels must be unique within an ArgumentBundle; the parser uses labels to match Syntax nodes to Argument instances
-
Backtracking modifies state — MuParser tracks which Arguments were modified and calls
undoLastValue()when backtracking; do not rely on side effects during validation - Step limit — MuParser has a DEFAULT_STEP_LIMIT of 10000 to prevent pathological grammars from hanging; complex commands with many alternatives may need adjustment
- EXISTING/NONEXISTENT are not logical negations — they check different properties; a file can be both non-existent (for creation) and non-accessible (for read)
-
Option syntax is just syntax —
OptionSyntaxcompiles to a MuSequence containing MuSymbol and MuArgument; you can achieve the same with manual symbol/argument sequences -
Completion is partial-parse — when completing, MuParser explores all alternatives that could match the partial token; arguments providing completions must avoid suggestions that would fail validation in
doAccept() - Child syntaxes inherit parent labels — labels from child Syntax nodes must match Argument labels in the bundle, even across module boundaries
- Shell-Syntax — Shell syntax parsing infrastructure, MuParser, SyntaxManager, and registration
- Shell-Commands — Parent hub page for the command framework
- Plugin-System — How commands and syntaxes are registered via extensions
- Code-Conventions — Best practices for writing JNode commands