KCL Spec
Lexical rules
Keywords and reserved words
The following are the keywords of the KCL:
True False None Undefined import
and or in is not
as if else elif for
schema mixin protocol check assert
all any map filter lambda
rule
The following are reserved words for the KCL:
pass return validate rule flow
def del raise except try
finally while from with yield
global nonlocal struct class final
Line comment
# a comment
Operators
+ - * ** / // %
<< >> & | ^ < >
~ <= >= == != =
+= -= *= **= /= //= %=
<<= >>= &= ^=
Delimiters
( ) [ ] { }
, : . ; @
Operator precedence
The following list of operators is ordered from highest to lowest:
Operator | Description |
---|---|
** | Exponentiation (highest priority) |
+x -x ~x | Positive, negative, bitwise NOT |
* / % // | Multiplication, division, floor division and remainder |
+ - | Addition and subtraction |
<< >> | Left and right shifts |
& | Bitwise AND |
^ | Bitwise XOR |
| | Bitwise OR |
in , not in , is , is not , < , <= , > , >= , != , == | Comparisons, including membership and identity operators |
not | Boolean NOT |
and | Boolean AND |
or | Boolean OR |
if – else | Conditional expression = |
= , += , -= , *= , /= , %= , &= , = , ^= , \*\*= , //= , <<= , >>= | Assign |
Grammar
KCL uses Python's LarkParser tool to describe the grammar, and the specification rules are as follows:
//////////// KCL grammar ////////////
start: (NEWLINE | statement)*
statement: simple_stmt | compound_stmt
simple_stmt: (assign_stmt | unification_stmt | expr_stmt | assert_stmt | import_stmt | type_alias_stmt) NEWLINE
compound_stmt: if_stmt | schema_stmt | rule_stmt
//////////// import_stmt ////////////
import_stmt: IMPORT dot_name (AS NAME)?
dot_name: (leading_dots identifier) | identifier
leading_dots: DOT+
/////////// assert_stmt ////////////
assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)?
//////////// if_stmt ////////////
if_stmt: IF test COLON execution_block (ELIF test COLON execution_block)* (ELSE COLON execution_block)?
execution_block: if_simple_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT
if_simple_stmt: (simple_assign_stmt | unification_stmt | expr_stmt | assert_stmt) NEWLINE
//////////// assign_stmt ////////////
assign_stmt: identifier [COLON type] (ASSIGN identifier)* ASSIGN test
| identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE
| COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT
| COMP_SHIFT_RIGHT) test
simple_assign_stmt: identifier ASSIGN test
| identifier (COMP_PLUS | COMP_MINUS | COMP_MULTIPLY | COMP_DOUBLE_STAR | COMP_DIVIDE
| COMP_DOUBLE_DIVIDE | COMP_MOD | COMP_AND | COMP_OR | COMP_XOR | COMP_SHIFT_LEFT
| COMP_SHIFT_RIGHT) test
//////////// unification_stmt ////////////
unification_stmt: identifier COLON schema_expr
//////////// schema_stmt ////////////
schema_stmt: [decorators] (SCHEMA|MIXIN|PROTOCOL) NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [schema_body]
schema_arguments: schema_argument (COMMA schema_argument)*
schema_argument: NAME [COLON type] [ASSIGN test]
schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt|schema_init_stmt|schema_index_signature)* [check_block] _DEDENT
schema_attribute_stmt: attribute_stmt NEWLINE
attribute_stmt: [decorators] identifier [QUESTION] COLON type [(ASSIGN|COMP_OR) test]
schema_init_stmt: if_simple_stmt | if_stmt
schema_index_signature: LEFT_BRACKETS [NAME COLON] [ELLIPSIS] basic_type RIGHT_BRACKETS COLON type [ASSIGN test] NEWLINE
//////////// rule_stmt ////////////
rule_stmt: [decorators] RULE NAME [LEFT_BRACKETS [schema_arguments] RIGHT_BRACKETS] [LEFT_PARENTHESES identifier (COMMA identifier)* RIGHT_PARENTHESES] [for_host] COLON NEWLINE [rule_body]
rule_body: _INDENT (string NEWLINE)* check_expr+ _DEDENT
for_host: FOR identifier
/////////// decorators ////////////
decorators: (AT decorator_expr NEWLINE)+
decorator_expr: identifier [call_suffix]
//////////// type ////////////
type: type_element (OR type_element)*
type_element: schema_type | basic_type | compound_type | literal_type
schema_type: identifier
basic_type: STRING_TYPE | INT_TYPE | FLOAT_TYPE | BOOL_TYPE | ANY_TYPE
compound_type: list_type | dict_type
list_type: LEFT_BRACKETS (type)? RIGHT_BRACKETS
dict_type: LEFT_BRACE (type)? COLON (type)? RIGHT_BRACE
literal_type: string | number | TRUE | FALSE
//////////// type alias ////////////
type_alias_stmt: TYPE NAME ASSIGN type
//////////// check_stmt ////////////
check_block: CHECK COLON NEWLINE _INDENT check_expr+ _DEDENT
check_expr: simple_expr [IF simple_expr] [COMMA primary_expr] NEWLINE
//////////// mixin_stmt ////////////
mixin_stmt: MIXIN LEFT_BRACKETS [mixins | multiline_mixins] RIGHT_BRACKETS NEWLINE
multiline_mixins: NEWLINE _INDENT mixins NEWLINE _DEDENT
mixins: identifier (COMMA (NEWLINE mixins | identifier))*
//////////// expression_stmt ////////////
expr_stmt: testlist_expr
testlist_expr: test (COMMA test)*
test: if_expr | simple_expr
if_expr: simple_expr IF simple_expr ELSE test
simple_expr: unary_expr | binary_expr | primary_expr
unary_expr: un_op simple_expr
binary_expr: simple_expr bin_op simple_expr
bin_op: L_OR | L_AND
| OR | XOR | AND
| SHIFT_LEFT | SHIFT_RIGHT
| PLUS | MINUS | MULTIPLY | DIVIDE | MOD | DOUBLE_DIVIDE
| DOUBLE_STAR
| EQUAL_TO | NOT_EQUAL_TO
| LESS_THAN | GREATER_THAN | LESS_THAN_OR_EQUAL_TO | GREATER_THAN_OR_EQUAL_TO
| IN | L_NOT IN | IS | IS L_NOT | L_NOT | AS
un_op: L_NOT | PLUS | MINUS | NOT
primary_expr: identifier call_suffix | operand | primary_expr select_suffix | primary_expr call_suffix | primary_expr slice_suffix
operand: identifier | number | string | constant | quant_expr | list_expr | list_comp | config_expr | dict_comp | schema_expr | lambda_expr | LEFT_PARENTHESES test RIGHT_PARENTHESES
select_suffix: [QUESTION] DOT NAME
call_suffix: LEFT_PARENTHESES [arguments [COMMA]] RIGHT_PARENTHESES
slice_suffix: [QUESTION] LEFT_BRACKETS (test | [test] COLON [test] [COLON [test]]) RIGHT_BRACKETS
arguments: argument (COMMA argument)*
argument: test | NAME ASSIGN test | MULTIPLY test | DOUBLE_STAR test
//////////// operand ////////////
identifier: NAME (DOT NAME)*
quant_expr: quant_op [ identifier COMMA ] identifier IN quant_target LEFT_BRACE (simple_expr [IF simple_expr] | NEWLINE _INDENT simple_expr [IF simple_expr] NEWLINE _DEDENT)? RIGHT_BRACE
quant_target: string | identifier | list_expr | list_comp | config_expr | dict_comp
quant_op: ALL | ANY | FILTER | MAP
list_expr: LEFT_BRACKETS [list_items | NEWLINE [_INDENT list_items _DEDENT]] RIGHT_BRACKETS
list_items: list_item ((COMMA [NEWLINE] | [NEWLINE]) list_item)* [COMMA] [NEWLINE]
list_item: test | star_expr | if_item
list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE _INDENT list_item comp_clause+ _DEDENT) RIGHT_BRACKETS
dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE _INDENT entry comp_clause+ _DEDENT) RIGHT_BRACE
entry: test (COLON | ASSIGN | COMP_PLUS) test
comp_clause: FOR loop_variables [COMMA] IN simple_expr [NEWLINE] [IF test [NEWLINE]]
if_entry: IF test COLON if_entry_exec_block (ELIF test COLON if_entry_exec_block)* (ELSE COLON if_entry_exec_block)?
if_entry_exec_block: (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) [NEWLINE] | NEWLINE _INDENT (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry) ((COMMA [NEWLINE] | [NEWLINE]) (test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry))* [COMMA] [NEWLINE] _DEDENT
if_item: IF test COLON if_item_exec_block (ELIF test COLON if_item_exec_block)* (ELSE COLON if_item_exec_block)?
if_item_exec_block: list_item [NEWLINE] | NEWLINE _INDENT list_item ((COMMA [NEWLINE] | NEWLINE) list_item)* [COMMA] [NEWLINE] _DEDENT
star_expr: MULTIPLY test
double_star_expr: DOUBLE_STAR test
loop_variables: primary_expr (COMMA primary_expr)*
schema_expr: identifier (LEFT_PARENTHESES [arguments] RIGHT_PARENTHESES)? config_expr
config_expr: LEFT_BRACE [config_entries | NEWLINE [_INDENT config_entries _DEDENT]] RIGHT_BRACE
config_entries: config_entry ((COMMA [NEWLINE] | [NEWLINE]) config_entry)* [COMMA] [NEWLINE]
config_entry: test (COLON | ASSIGN | COMP_PLUS) test | double_star_expr | if_entry
//////////// lambda_expr ////////////
lambda_expr: LAMBDA [schema_arguments] [RIGHT_ARROW type] LEFT_BRACE [expr_stmt | NEWLINE _INDENT schema_init_stmt+ _DEDENT] RIGHT_BRACE
//////////// misc ////////////
number: DEC_NUMBER [multiplier] | HEX_NUMBER | BIN_NUMBER | OCT_NUMBER | FLOAT_NUMBER
multiplier: SI_N_L | SI_U_L | SI_M_L | SI_K_L | SI_K | SI_M | SI_G | SI_T | SI_P
| SI_K_IEC | SI_M_IEC | SI_G_IEC | SI_T_IEC | SI_P_IEC
string: STRING | LONG_STRING
constant : TRUE | FALSE | NONE | UNDEFINED
// Tokens
ASSIGN: "="
COLON: ":"
SEMI_COLON: ";"
COMMA: ","
QUESTION: "?"
ELLIPSIS: "..."
RIGHT_ARROW: "->"
LEFT_PARENTHESES: "("
RIGHT_PARENTHESES: ")"
LEFT_BRACKETS: "["
RIGHT_BRACKETS: "]"
LEFT_BRACE: "{"
RIGHT_BRACE: "}"
PLUS: "+"
MINUS: "-"
MULTIPLY: "*"
DIVIDE: "/"
MOD: "%"
DOT: "."
AND: "&"
OR: "|"
XOR: "^"
NOT: "~"
LESS_THAN: "<"
GREATER_THAN: ">"
EQUAL_TO: "=="
NOT_EQUAL_TO: "!="
GREATER_THAN_OR_EQUAL_TO: ">="
LESS_THAN_OR_EQUAL_TO: "<="
DOUBLE_STAR: "**"
DOUBLE_DIVIDE: "//"
SHIFT_LEFT: "<<"
SHIFT_RIGHT: ">>"
AT: "@"
COMP_PLUS: "+="
COMP_MINUS: "-="
COMP_MULTIPLY: "*="
COMP_DIVIDE: "/="
COMP_MOD: "%="
COMP_AND: "&="
COMP_OR: "|="
COMP_XOR: "^="
COMP_DOUBLE_STAR: "**="
COMP_DOUBLE_DIVIDE: "//="
COMP_SHIFT_LEFT: "<<="
COMP_SHIFT_RIGHT: ">>="
// Special tokens
IMPORT: "import"
AS: "as"
RULE: "rule"
SCHEMA: "schema"
MIXIN: "mixin"
PROTOCOL: "protocol"
CHECK: "check"
FOR: "for"
ASSERT: "assert"
IF: "if"
ELIF: "elif"
ELSE: "else"
L_OR: "or"
L_AND: "and"
L_NOT: "not"
IN: "in"
IS: "is"
LAMBDA: "lambda"
ALL: "all"
ANY: "any"
FILTER: "filter"
MAP: "map"
TYPE: "type"
ANY_TYPE: "any"
STRING_TYPE: "str"
INT_TYPE: "int"
FLOAT_TYPE: "float"
BOOL_TYPE: "bool"
// Constant tokens
TRUE: "True"
FALSE: "False"
NONE: "None"
UNDEFINED: "Undefined"
// Binary prefix
SI_N_L: "n"
SI_U_L: "u"
SI_M_L: "m"
SI_K_L: "k"
SI_K: "K"
SI_M: "M"
SI_G: "G"
SI_T: "T"
SI_P: "P"
SI_K_IEC: "Ki"
SI_M_IEC: "Mi"
SI_G_IEC: "Gi"
SI_T_IEC: "Ti"
SI_P_IEC: "Pi"
IEC: "i"
NAME: /\$?[a-zA-Z_]\w*/
COMMENT: /#[^\n]*/
NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+
STRING: /r?("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i
LONG_STRING: /r?(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is
DEC_NUMBER: /\-?0|\-?[1-9]\d*/i
HEX_NUMBER.2: /\-?0[xX][0-9a-fA-F]+/i
OCT_NUMBER.2: /\-?0[oO]?[0-7]+/i
BIN_NUMBER.2 : /\-?0[bB][0-1]+/i
FLOAT_NUMBER.2: /(([-+]?\d+\.\d*|\.\d+)(e[-+]?\d+)?|\d+(e[-+]?\d+))/i
%ignore /[\t \f]+/ // WS
%ignore /\\[\t \f]*\r?\n/ // LINE_CONT
%declare _INDENT _DEDENT