Split PR-624 release notes
This removes the language specification and BNF descriptions from the PR release notes (they're too long for that) to a separate document in the libcom/src/as area. That should be combined into the chapter on Access Security from the AppDevGuide someday.
This commit is contained in:
@@ -13,6 +13,7 @@ EPICS Base Documentation
|
||||
|
||||
README
|
||||
RELEASE_NOTES
|
||||
ACF-Language
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
# ACF Syntax Forward Compatibility
|
||||
### ACF Syntax Forward Compatibility
|
||||
|
||||
This update modifies the Access Security Configuration File (ACF) parser to **standardize the ACF grammar
|
||||
for forward compatibility**. All ACF definitions now adhere to a consistent syntax
|
||||
format, which will allow future additions to the access security language without breaking
|
||||
existing configurations. In practice, this means the structure of ACF files is now formally
|
||||
defined and will remain stable going forward, so any new grammar features will fit
|
||||
into the same pattern. (Existing ACF files continue to work as-is under the new parser,
|
||||
This release modifies the Access Security Configuration File (ACF) parser to
|
||||
**standardize the ACF grammar for forward compatibility**.
|
||||
It does not change the syntax that was accepted by earlier versions of the parser,
|
||||
so **existing access security configuration files will not need to be modified.**
|
||||
All ACF definitions will adhere to a consistent syntax format,
|
||||
which will allow future additions to the access security language
|
||||
without breaking existing configurations.
|
||||
In practice, this means the structure of ACF files is now formally defined
|
||||
and will remain stable going forward,
|
||||
so any new grammar features will fit into the same pattern.
|
||||
(Existing ACF files continue to work as-is under the new parser,
|
||||
so no changes are required for legacy configurations or tools.).
|
||||
|
||||
**Generic ACF Syntax:** The ACF file consists of definitions for User Access Groups (UAG),
|
||||
Host Access Groups (HAG), and Access Security Groups (ASG),
|
||||
using the following general format (angle brackets denote placeholders):
|
||||
**Generic ACF Syntax:**
|
||||
The ACF file consists of definitions for User Access Groups (UAG),
|
||||
Host Access Groups (HAG),
|
||||
and Access Security Groups (ASG),
|
||||
using the following general format
|
||||
(angle brackets below denote placeholders):
|
||||
|
||||
```text
|
||||
|
||||
@@ -35,17 +43,31 @@ ASG(<name>) [{
|
||||
|
||||
```
|
||||
|
||||
Under this schema, each definition uses a keyword, a name in parentheses,
|
||||
and (optionally) a braced block of contents. This uniform structure ensures that **future keywords or sections** can be introduced
|
||||
in the same form, maintaining compatibility with the parser. For example, if a new type of condition
|
||||
or group is added in a later release, it would follow the KEYWORD(name) { ... } pattern,
|
||||
so 7.0.9-era parsers can handle or ignore it gracefully (instead of failing on unknown syntax).
|
||||
Under this schema each definition comprises a keyword,
|
||||
a name in parentheses,
|
||||
and (optionally) a braced block of contents.
|
||||
This uniform structure ensures that
|
||||
**future keywords or sections**
|
||||
can be introduced in the same form,
|
||||
maintaining compatibility with the parser.
|
||||
For example, if a new type of condition or group is added in a later release,
|
||||
it would follow the `KEYWORD(name) { ... }` pattern,
|
||||
so 7.0.10-era parsers can handle or ignore it gracefully
|
||||
instead of failing on unknown syntax.
|
||||
|
||||
**Supported Syntax in EPICS 7.0.9:** The current release defines the following specific elements within the above generic format:
|
||||
**Supported Syntax in EPICS 7.0.10:**
|
||||
The current release defines the following specific elements
|
||||
within the above generic format:
|
||||
|
||||
- **UAG** -- *User Access Group*. Defines a group of user names.
|
||||
- **HAG** -- *Host Access Group*. Defines a group of host names (or IP addresses) that clients can connect from.
|
||||
- **ASG** -- *Access Security Group*. Defines a security group which records can be assigned to. An ASG entry may contain a block with input definitions and access rules. For example:
|
||||
- **UAG** -- *User Access Group*.
|
||||
Defines a group of user names.
|
||||
- **HAG** -- *Host Access Group*.
|
||||
Defines a group of host names
|
||||
(or IP addresses) that clients can connect from.
|
||||
- **ASG** -- *Access Security Group*.
|
||||
Defines a security group which records can be assigned to.
|
||||
An ASG entry may contain a block with input definitions and access rules.
|
||||
For example:
|
||||
|
||||
```text
|
||||
ASG(MyGroup) {
|
||||
@@ -56,248 +78,61 @@ ASG(MyGroup) {
|
||||
}
|
||||
```
|
||||
|
||||
If no rules are defined for an ASG, access defaults to always allowed.
|
||||
If no rules are defined for an ASG,
|
||||
the access permissions default to always allowed.
|
||||
|
||||
- **INP<index>(<pvname>)** -- *Input link*. Declares an input process variable whose value can be used in a CALC condition.
|
||||
- **RULE(<level>, <permission> [, <logOption>]) { ... }** -- Defines an access rule for the ASG.
|
||||
- **INP<index>(<pvname>)** -- *Input link*.
|
||||
Declares an input process variable whose value can be used in a CALC condition.
|
||||
- **RULE(<level>, <permission> [, <logOption>]) { ... }** --
|
||||
Defines an access rule for the ASG.
|
||||
|
||||
Inside the curly braces of a RULE, **optional conditions** can restrict when that rule applies. All conditions that are present must be satisfied (they function as a logical AND):
|
||||
Inside the curly braces of a RULE,
|
||||
**optional conditions** can restrict when that rule applies.
|
||||
All conditions that are present must be satisfied
|
||||
(they function as a logical AND):
|
||||
|
||||
- **UAG(<name>, ...)** -- User-group condition. The rule only applies if the Channel Access client's user is a member of one of the listed UAGs.
|
||||
- **HAG(<name>, ...)** -- Host-group condition. The rule only applies if the client's host (as determined by its IP or hostname) is in one of the listed HAGs
|
||||
- **CALC("<expression>")** -- Calculation condition. The rule only applies if the given expression evaluates to true (non-zero).
|
||||
- **UAG(<name>, ...)** -- User-group condition.
|
||||
The rule only applies if the Channel Access client's user
|
||||
is a member of one of the listed UAGs.
|
||||
- **HAG(<name>, ...)** -- Host-group condition.
|
||||
The rule only applies if the client's host
|
||||
(as determined by its IP or hostname) is in one of the listed HAGs
|
||||
- **CALC("<expression>")** -- Calculation condition.
|
||||
The rule only applies if the given expression evaluates to true (non-zero).
|
||||
|
||||
**Special Semantics for RULEs:** Rules will continue to allow the prescribed access if and only if all the predicates the rule contains are satisfied.
|
||||
- If the rule contains predicates that are unknown to the parser (future functionality), then the rule will NOT not match - but no syntax error will be reported as long as the syntax is correct.
|
||||
- If the rule contains predicates that the parser does not recognise which are malformed (e.g. missing parentheses), then the rule will not match and the parser will report a syntax error.
|
||||
- In this way rules can be extended with new predicates without breaking older clients or giving those older clients elevated privileges.
|
||||
**Special Semantics for RULEs:**
|
||||
Rules will continue to allow the prescribed access if and only if
|
||||
all the predicates the rule contains are satisfied.
|
||||
- If the rule contains predicates that are unknown to the parser
|
||||
(indicating future functionality),
|
||||
then the rule will NOT not match,
|
||||
but no syntax error will be reported as long as the syntax is correct.
|
||||
- If the rule contains predicates that the parser does not recognise
|
||||
which are malformed (e.g. missing parentheses),
|
||||
then the rule will not match and the parser will report a syntax error.
|
||||
- In this way rules can be extended with new predicates
|
||||
without breaking older clients or giving those older clients elevated privileges.
|
||||
|
||||
**Special Semantics unrecognised ACF file elements:** Any new elements that are added to the ACF file will be ignored silently by a parser that does not understand them.
|
||||
- If a new element is added to the ACF file that is not understood by the parser, the parser will simply ignore it silently, without reporting an error, as long as the syntax is correct.
|
||||
- If new elements are added to the ACF file that are malformed (e.g. missing parentheses), then the parser will report a syntax error.
|
||||
- In this way new elements can be added to ACF files without breaking older clients.
|
||||
**Special Semantics for unrecognised ACF file elements:**
|
||||
Any elements that are included in an ACF file will be ignored silently
|
||||
by a parser that does not understand them.
|
||||
- If an element is seen in an ACF file that is not understood by the parser,
|
||||
the parser will simply ignore it silently,
|
||||
without reporting an error,
|
||||
as long as its syntax is correct.
|
||||
- If elements are added to the ACF file that are malformed
|
||||
(e.g. missing parentheses),
|
||||
the parser will report a syntax error.
|
||||
- Thus new elements can be added to ACF files in new EPICS releases
|
||||
without breaking older clients that loads those files.
|
||||
|
||||
In summary, **ACF forward compatibility** means that from EPICS 7.0.9 onward,
|
||||
any new access security features will use this established syntax. The parser
|
||||
will recognize new group types or rule options by the same <KEYWORD>(...) { ... }
|
||||
convention, ensuring they can be added without breaking older files or requiring a new parser.
|
||||
This change **does not require any modifications to existing ACF files or downstream tools** -- all
|
||||
legacy syntax remains valid, and the new standardized grammar simply provides a robust foundation
|
||||
for future extensions.
|
||||
|
||||
---
|
||||
|
||||
# Full Language Specification:
|
||||
|
||||
## Lexical tokens
|
||||
|
||||
**Ignored stuff**
|
||||
|
||||
- *Whitespace*: space, tab, `\r` (carriage return) -- may appear between tokens.
|
||||
|
||||
- *Newlines*: `\n` -- same as whitespace for syntax, but tracked for error messages.
|
||||
|
||||
- *Comments*: `#` ... end of line -- ignored.
|
||||
|
||||
**Terminals**
|
||||
|
||||
- `UAG` → literal string `"UAG"`
|
||||
- `HAG` → `"HAG"`
|
||||
- `ASG` → `"ASG"`
|
||||
- `RULE` → `"RULE"`
|
||||
- `CALC` → `"CALC"`
|
||||
- `INP(link)` → literal `"INP"` followed immediately by one uppercase letter `A`-`U`
|
||||
```text
|
||||
link-letter ::= "A" | "B" | ... | "U"
|
||||
INP(link) ::= "INP" link-letter
|
||||
```
|
||||
- `INT` → integer literal
|
||||
|
||||
```text
|
||||
INT ::= [ "+" | "-" ]? DIGIT+
|
||||
DIGIT ::= "0" | "1" | ... | "9"
|
||||
```
|
||||
- `FLOAT` → floating-point literal with decimal point
|
||||
```text
|
||||
FLOAT ::= [ "+" | "-" ]? DIGIT* "." DIGIT+ [ ( "e" | "E" ) [ "+" | "-" ] DIGIT+ ]?
|
||||
```
|
||||
- `STRING` → either
|
||||
- **unquoted**: one or more of
|
||||
```text
|
||||
NAMECHAR ::= letter | digit | "_" | "-" | "+" | ":" | "." | "[" | "]" | "<" | ">" | ";"
|
||||
STRING(unquoted) ::= NAMECHAR+
|
||||
```
|
||||
- - **quoted**:
|
||||
```text
|
||||
STRING(quoted) ::= '"' { STRINGCHAR | ESCAPE } '"'
|
||||
STRINGCHAR ::= any char except '"' "\" "\n"
|
||||
ESCAPE ::= "\" any-char
|
||||
```
|
||||
The surrounding quotes are stripped; escapes are kept literal at parse level.
|
||||
- Punctuation tokens (single-character terminals):
|
||||
```text
|
||||
"(" ")" "{" "}" ","
|
||||
```
|
||||
---
|
||||
|
||||
# Grammar
|
||||
|
||||
## Top level
|
||||
|
||||
```ebnf
|
||||
acf-file ::= asconfig ;
|
||||
|
||||
asconfig ::= asconfig-item { asconfig-item } ;
|
||||
|
||||
asconfig-item ::=
|
||||
uag-def
|
||||
| hag-def
|
||||
| asg-def
|
||||
| generic-top-level-item ;
|
||||
```
|
||||
### UAG / HAG groups
|
||||
|
||||
```ebnf
|
||||
uag-def ::= "UAG" uag-head [ uag-body ] ;
|
||||
|
||||
uag-head ::= "(" STRING ")" ;
|
||||
|
||||
uag-body ::= "{" uag-user-list "}" ;
|
||||
|
||||
uag-user-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
hag-def ::= "HAG" hag-head [ hag-body ] ;
|
||||
|
||||
hag-head ::= "(" STRING ")" ;
|
||||
|
||||
hag-body ::= "{" hag-host-list "}" ;
|
||||
|
||||
hag-host-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
### ASG (access security group)
|
||||
|
||||
```ebnf
|
||||
asg-def ::= "ASG" asg-head [ asg-body ] ;
|
||||
|
||||
asg-head ::= "(" STRING ")" ;
|
||||
|
||||
asg-body ::= "{" asg-body-item { asg-body-item } "}" ;
|
||||
|
||||
asg-body-item ::=
|
||||
inp-config
|
||||
| rule-config ;
|
||||
```
|
||||
|
||||
#### INP config
|
||||
|
||||
```ebnf
|
||||
inp-config ::= INP(link) "(" STRING ")" ;`
|
||||
```
|
||||
|
||||
#### RULE config
|
||||
```ebnf
|
||||
rule-config ::= "RULE" rule-head [ rule-body ] ;
|
||||
|
||||
rule-head ::=
|
||||
"(" rule-head-mandatory "," rule-log-option ")"
|
||||
| "(" rule-head-mandatory ")" ;
|
||||
|
||||
rule-head-mandatory ::= INT "," STRING ;
|
||||
|
||||
rule-log-option ::= STRING ;
|
||||
```
|
||||
```ebnf
|
||||
rule-body ::= "{" rule-list "}" ;
|
||||
|
||||
rule-list ::= rule-list-item { rule-list-item } ;
|
||||
|
||||
rule-list-item ::=
|
||||
"UAG" "(" rule-uag-list ")"
|
||||
| "HAG" "(" rule-hag-list ")"
|
||||
| "CALC" "(" STRING ")"
|
||||
| rule-generic-block-elem ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
rule-uag-list ::= STRING { "," STRING } ;
|
||||
|
||||
rule-hag-list ::= STRING { "," STRING } ;`
|
||||
```
|
||||
|
||||
### Generic / future-proof syntax
|
||||
|
||||
These are the "catch-all" constructs that are **parsed** but currently **ignored** semantically.
|
||||
|
||||
#### Keyword classes
|
||||
|
||||
These are parser-level categories used inside generic constructs:
|
||||
```ebnf
|
||||
keyword ::=
|
||||
"UAG"
|
||||
| "HAG"
|
||||
| "CALC"
|
||||
| non-rule-keyword ;
|
||||
|
||||
non-rule-keyword ::=
|
||||
"ASG"
|
||||
| "RULE"
|
||||
| INP(link) ; (* INPA..INPU *)
|
||||
```
|
||||
#### Generic head (argument list)
|
||||
|
||||
```ebnf
|
||||
generic-head ::=
|
||||
"(" ")"
|
||||
| "(" generic-element ")"
|
||||
| "(" generic-list ")" ;
|
||||
|
||||
generic-list ::= generic-element "," generic-element { "," generic-element } ;
|
||||
|
||||
generic-element ::=
|
||||
keyword
|
||||
| STRING
|
||||
| INT
|
||||
| FLOAT ;
|
||||
```
|
||||
#### Generic blocks
|
||||
|
||||
```ebnf
|
||||
generic-block ::=
|
||||
"{" generic-element "}"
|
||||
| "{" generic-list "}"
|
||||
| "{" generic-block-list "}" ;
|
||||
|
||||
generic-block-list ::= generic-block-elem { generic-block-elem } ;
|
||||
|
||||
generic-block-elem ::=
|
||||
generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
generic-block-elem-name ::= keyword | STRING ;`
|
||||
```
|
||||
#### Generic top-level items
|
||||
|
||||
These are "unknown" top-level constructs, all of which are parsed and then ignored with a warning.
|
||||
```ebnf
|
||||
generic-top-level-item ::=
|
||||
STRING generic-head generic-list-block
|
||||
| STRING generic-head generic-block
|
||||
| STRING generic-head ;
|
||||
```
|
||||
where
|
||||
```ebnf
|
||||
generic-list-block ::=
|
||||
"{" generic-element "}" "{" generic-list "}" ;
|
||||
```
|
||||
#### Generic blocks inside RULE bodies
|
||||
|
||||
These are the "future predicates" in a RULE's body; they cause the RULE to be disabled with a warning, but they **must** still parse.
|
||||
|
||||
```ebnf
|
||||
rule-generic-block-elem ::=
|
||||
rule-generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
rule-generic-block-elem-name ::= non-rule-keyword | STRING ;
|
||||
```
|
||||
In summary, **ACF forward compatibility**
|
||||
means that from EPICS 7.0.10 onward,
|
||||
any new access security features will use this established syntax.
|
||||
The parser will recognize new group types or rule options using the same
|
||||
`<KEYWORD>(...) { ... }` convention,
|
||||
ensuring they can be used in files loaded by IOCs running EPICS 7.0.10 or later
|
||||
without breaking those IOCs or requiring their parser to be modified.
|
||||
This change **does not require any modifications to existing ACF files
|
||||
or downstream tools** -- all legacy syntax remains valid,
|
||||
and the new standardized grammar provides a robust foundation for future extensions.
|
||||
|
||||
375
modules/libcom/src/as/ACF-Language.md
Normal file
375
modules/libcom/src/as/ACF-Language.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# ACF Syntax Forward Compatibility
|
||||
|
||||
EPICS 7.0.10 modified the Access Security Configuration File (ACF) parser to
|
||||
**standardize the ACF grammar for forward compatibility**.
|
||||
It did not change the syntax that was accepted by earlier versions of the parser,
|
||||
so **existing access security configuration files will not need to be modified.**
|
||||
All ACF definitions will adhere to a consistent syntax format,
|
||||
which will allow future additions to the access security language
|
||||
without breaking existing configurations.
|
||||
In practice, this means the structure of ACF files is now formally defined
|
||||
and will remain stable going forward,
|
||||
so any new grammar features will fit into the same pattern.
|
||||
(Existing ACF files continue to work as-is under the new parser,
|
||||
so no changes are required for legacy configurations or tools.).
|
||||
|
||||
**Generic ACF Syntax:**
|
||||
The ACF file consists of definitions for User Access Groups (UAG),
|
||||
Host Access Groups (HAG),
|
||||
and Access Security Groups (ASG),
|
||||
using the following general format
|
||||
(angle brackets below denote placeholders):
|
||||
|
||||
```text
|
||||
UAG(<name>) [{ <user> [, <user> ...] }]
|
||||
...
|
||||
|
||||
HAG(<name>) [{ <host> [, <host> ...] }]
|
||||
...
|
||||
|
||||
ASG(<name>) [{
|
||||
[INP<index>(<pvname>)
|
||||
...]
|
||||
|
||||
RULE(<level>, NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) {
|
||||
[UAG(<name> [, <name> ...])]
|
||||
[HAG(<name> [, <name> ...])]
|
||||
[CALC(<calculation>)]
|
||||
}
|
||||
...
|
||||
}]
|
||||
...
|
||||
```
|
||||
|
||||
Under this schema each definition comprises a keyword,
|
||||
a name in parentheses,
|
||||
and (optionally) a braced block of contents.
|
||||
This uniform structure ensures that
|
||||
**future keywords or sections**
|
||||
can be introduced in the same form,
|
||||
maintaining compatibility with the parser.
|
||||
For example, if a new type of condition or group is added in a later release,
|
||||
it would follow the `KEYWORD(name) { ... }` pattern,
|
||||
so 7.0.10-era parsers can handle or ignore it gracefully
|
||||
instead of failing on unknown syntax.
|
||||
|
||||
**Supported Syntax in EPICS 7.0.10:**
|
||||
The current release defines the following specific elements
|
||||
within the above generic format:
|
||||
|
||||
- **UAG** -- *User Access Group*.
|
||||
Defines a group of user names.
|
||||
- **HAG** -- *Host Access Group*.
|
||||
Defines a group of host names
|
||||
(or IP addresses) that clients can connect from.
|
||||
- **ASG** -- *Access Security Group*.
|
||||
Defines a security group which records can be assigned to.
|
||||
An ASG entry may contain a block with input definitions and access rules.
|
||||
For example:
|
||||
|
||||
```text
|
||||
ASG(MyGroup) {
|
||||
INPA(myPV1)
|
||||
INPB(myPV2)
|
||||
RULE(1, WRITE) { ... }
|
||||
RULE(1, READ) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
If no rules are defined for an ASG,
|
||||
the access permissions default to always allowed.
|
||||
|
||||
- **INP<index>(<pvname>)** -- *Input link*.
|
||||
Declares an input process variable whose value can be used in a CALC condition.
|
||||
- **RULE(<level>, <permission> [, <logOption>]) { ... }** --
|
||||
Defines an access rule for the ASG.
|
||||
|
||||
Inside the curly braces of a RULE,
|
||||
**optional conditions** can restrict when that rule applies.
|
||||
All conditions that are present must be satisfied
|
||||
(they function as a logical AND):
|
||||
|
||||
- **UAG(<name>, ...)** -- User-group condition.
|
||||
The rule only applies if the Channel Access client's user
|
||||
is a member of one of the listed UAGs.
|
||||
- **HAG(<name>, ...)** -- Host-group condition.
|
||||
The rule only applies if the client's host
|
||||
(as determined by its IP or hostname) is in one of the listed HAGs
|
||||
- **CALC("<expression>")** -- Calculation condition.
|
||||
The rule only applies if the given expression evaluates to true (non-zero).
|
||||
|
||||
**Special Semantics for RULEs:**
|
||||
Rules will continue to allow the prescribed access if and only if
|
||||
all the predicates the rule contains are satisfied.
|
||||
- If the rule contains predicates that are unknown to the parser
|
||||
(indicating future functionality),
|
||||
then the rule will NOT not match,
|
||||
but no syntax error will be reported as long as the syntax is correct.
|
||||
- If the rule contains predicates that the parser does not recognise
|
||||
which are malformed (e.g. missing parentheses),
|
||||
then the rule will not match and the parser will report a syntax error.
|
||||
- In this way rules can be extended with new predicates
|
||||
without breaking older clients or giving those older clients elevated privileges.
|
||||
|
||||
**Special Semantics for unrecognised ACF file elements:**
|
||||
Any elements that are included in an ACF file will be ignored silently
|
||||
by a parser that does not understand them.
|
||||
- If an element is seen in an ACF file that is not understood by the parser,
|
||||
the parser will simply ignore it silently,
|
||||
without reporting an error,
|
||||
as long as its syntax is correct.
|
||||
- If elements are added to the ACF file that are malformed
|
||||
(e.g. missing parentheses),
|
||||
the parser will report a syntax error.
|
||||
- Thus new elements can be added to ACF files in new EPICS releases
|
||||
without breaking older clients that loads those files.
|
||||
|
||||
In summary, **ACF forward compatibility**
|
||||
means that from EPICS 7.0.10 onward,
|
||||
any new access security features will use this established syntax.
|
||||
The parser will recognize new group types or rule options using the same
|
||||
`<KEYWORD>(...) { ... }` convention,
|
||||
ensuring they can be used in files loaded by IOCs running EPICS 7.0.10 or later
|
||||
without being rejected by those IOCs or requiring their parser to be modified.
|
||||
This change **does not require any modifications to existing ACF files
|
||||
or downstream tools** -- all legacy syntax remains valid,
|
||||
and the new standardized grammar provides a robust foundation for future extensions.
|
||||
|
||||
---
|
||||
|
||||
# Full Language Specification for Access Security Configuration Files
|
||||
|
||||
## Lexical tokens
|
||||
|
||||
**Ignored stuff**
|
||||
|
||||
- *Whitespace*: space, tab, `\r` (carriage return) -- may appear between tokens.
|
||||
|
||||
- *Newlines*: `\n` -- same as whitespace for syntax, but tracked for error messages.
|
||||
|
||||
- *Comments*: `#` ... end of line -- ignored.
|
||||
|
||||
**Terminals**
|
||||
|
||||
- `UAG` → literal string `"UAG"`
|
||||
- `HAG` → `"HAG"`
|
||||
- `ASG` → `"ASG"`
|
||||
- `RULE` → `"RULE"`
|
||||
- `CALC` → `"CALC"`
|
||||
- `INP(link)` → literal `"INP"` followed immediately by one uppercase letter `A`-`U`
|
||||
|
||||
```text
|
||||
link-letter ::= "A" | "B" | ... | "U"
|
||||
INP(link) ::= "INP" link-letter
|
||||
```
|
||||
|
||||
- `INT` → integer literal
|
||||
|
||||
```text
|
||||
INT ::= [ "+" | "-" ]? DIGIT+
|
||||
DIGIT ::= "0" | "1" | ... | "9"
|
||||
```
|
||||
|
||||
- `FLOAT` → floating-point literal with decimal point
|
||||
|
||||
```text
|
||||
FLOAT ::= [ "+" | "-" ]? DIGIT* "." DIGIT+ [ ( "e" | "E" ) [ "+" | "-" ] DIGIT+ ]?
|
||||
```
|
||||
|
||||
- `STRING` → either
|
||||
- **Unquoted**: One or more of
|
||||
|
||||
```text
|
||||
NAMECHAR ::= letter | digit | "_" | "-" | "+" | ":" | "." | "[" | "]" | "<" | ">" | ";"
|
||||
STRING(unquoted) ::= NAMECHAR+
|
||||
```
|
||||
|
||||
- **Quoted**: Surrounding quotes are stripped;
|
||||
escapes are kept literal at parse level.
|
||||
|
||||
```text
|
||||
STRING(quoted) ::= '"' { STRINGCHAR | ESCAPE } '"'
|
||||
STRINGCHAR ::= any char except '"' "\" "\n"
|
||||
ESCAPE ::= "\" any-char
|
||||
```
|
||||
|
||||
- Punctuation tokens (single-character terminals):
|
||||
|
||||
```text
|
||||
"(" ")" "{" "}" ","
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Grammar
|
||||
|
||||
### Top level
|
||||
|
||||
```ebnf
|
||||
acf-file ::= asconfig ;
|
||||
|
||||
asconfig ::= asconfig-item { asconfig-item } ;
|
||||
|
||||
asconfig-item ::=
|
||||
uag-def
|
||||
| hag-def
|
||||
| asg-def
|
||||
| generic-top-level-item ;
|
||||
```
|
||||
|
||||
#### UAG / HAG groups
|
||||
|
||||
```ebnf
|
||||
uag-def ::= "UAG" uag-head [ uag-body ] ;
|
||||
|
||||
uag-head ::= "(" STRING ")" ;
|
||||
|
||||
uag-body ::= "{" uag-user-list "}" ;
|
||||
|
||||
uag-user-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
hag-def ::= "HAG" hag-head [ hag-body ] ;
|
||||
|
||||
hag-head ::= "(" STRING ")" ;
|
||||
|
||||
hag-body ::= "{" hag-host-list "}" ;
|
||||
|
||||
hag-host-list ::= STRING { "," STRING } ;
|
||||
```
|
||||
|
||||
#### ASG (access security group)
|
||||
|
||||
```ebnf
|
||||
asg-def ::= "ASG" asg-head [ asg-body ] ;
|
||||
|
||||
asg-head ::= "(" STRING ")" ;
|
||||
|
||||
asg-body ::= "{" asg-body-item { asg-body-item } "}" ;
|
||||
|
||||
asg-body-item ::=
|
||||
inp-config
|
||||
| rule-config ;
|
||||
```
|
||||
|
||||
##### INP config
|
||||
|
||||
```ebnf
|
||||
inp-config ::= INP(link) "(" STRING ")" ;`
|
||||
```
|
||||
|
||||
##### RULE config
|
||||
|
||||
```ebnf
|
||||
rule-config ::= "RULE" rule-head [ rule-body ] ;
|
||||
|
||||
rule-head ::=
|
||||
"(" rule-head-mandatory "," rule-log-option ")"
|
||||
| "(" rule-head-mandatory ")" ;
|
||||
|
||||
rule-head-mandatory ::= INT "," STRING ;
|
||||
|
||||
rule-log-option ::= STRING ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
rule-body ::= "{" rule-list "}" ;
|
||||
|
||||
rule-list ::= rule-list-item { rule-list-item } ;
|
||||
|
||||
rule-list-item ::=
|
||||
"UAG" "(" rule-uag-list ")"
|
||||
| "HAG" "(" rule-hag-list ")"
|
||||
| "CALC" "(" STRING ")"
|
||||
| rule-generic-block-elem ;
|
||||
```
|
||||
|
||||
```ebnf
|
||||
rule-uag-list ::= STRING { "," STRING } ;
|
||||
|
||||
rule-hag-list ::= STRING { "," STRING } ;`
|
||||
```
|
||||
|
||||
#### Generic / future-proof syntax
|
||||
|
||||
These are the "catch-all" constructs that are **parsed** but currently **ignored** semantically.
|
||||
|
||||
##### Keyword classes
|
||||
|
||||
These are parser-level categories used inside generic constructs:
|
||||
|
||||
```ebnf
|
||||
keyword ::=
|
||||
"UAG"
|
||||
| "HAG"
|
||||
| "CALC"
|
||||
| non-rule-keyword ;
|
||||
|
||||
non-rule-keyword ::=
|
||||
"ASG"
|
||||
| "RULE"
|
||||
| INP(link) ; (* INPA..INPU *)
|
||||
```
|
||||
|
||||
##### Generic head (argument list)
|
||||
|
||||
```ebnf
|
||||
generic-head ::=
|
||||
"(" ")"
|
||||
| "(" generic-element ")"
|
||||
| "(" generic-list ")" ;
|
||||
|
||||
generic-list ::= generic-element "," generic-element { "," generic-element } ;
|
||||
|
||||
generic-element ::=
|
||||
keyword
|
||||
| STRING
|
||||
| INT
|
||||
| FLOAT ;
|
||||
```
|
||||
|
||||
##### Generic blocks
|
||||
|
||||
```ebnf
|
||||
generic-block ::=
|
||||
"{" generic-element "}"
|
||||
| "{" generic-list "}"
|
||||
| "{" generic-block-list "}" ;
|
||||
|
||||
generic-block-list ::= generic-block-elem { generic-block-elem } ;
|
||||
|
||||
generic-block-elem ::=
|
||||
generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
generic-block-elem-name ::= keyword | STRING ;
|
||||
```
|
||||
|
||||
##### Generic top-level items
|
||||
|
||||
These are "unknown" top-level constructs, all of which are parsed and then ignored with a warning.
|
||||
|
||||
```ebnf
|
||||
generic-top-level-item ::=
|
||||
STRING generic-head generic-list-block
|
||||
| STRING generic-head generic-block
|
||||
| STRING generic-head ;
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
```ebnf
|
||||
generic-list-block ::=
|
||||
"{" generic-element "}" "{" generic-list "}" ;
|
||||
```
|
||||
|
||||
##### Generic blocks inside RULE bodies
|
||||
|
||||
These are the "future predicates" in a RULE's body; they cause the RULE to be disabled with a warning, but they **must** still parse.
|
||||
|
||||
```ebnf
|
||||
rule-generic-block-elem ::=
|
||||
rule-generic-block-elem-name generic-head [ generic-block ] ;
|
||||
|
||||
rule-generic-block-elem-name ::= non-rule-keyword | STRING ;
|
||||
```
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
SRC_DIRS += $(LIBCOM)/as
|
||||
|
||||
DOCS += ACF-Language.md
|
||||
|
||||
INC += asLib.h
|
||||
INC += asTrapWrite.h
|
||||
|
||||
|
||||
Reference in New Issue
Block a user