diff --git a/docs/reference/lang/spec/error.md b/docs/reference/lang/spec/error.md index 2a58b553..65420a2f 100644 --- a/docs/reference/lang/spec/error.md +++ b/docs/reference/lang/spec/error.md @@ -17,7 +17,7 @@ schema is violated. The syntax of the `assert` statement is the following. ```bnf -assert_stmt: 'assert' test ['if' test] [',' test] +assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? ``` In the basic form, an `assert` statement evaluates an expression. If the diff --git a/docs/reference/lang/spec/kcl-spec.md b/docs/reference/lang/spec/kcl-spec.md index e7cf8da3..798ba840 100644 --- a/docs/reference/lang/spec/kcl-spec.md +++ b/docs/reference/lang/spec/kcl-spec.md @@ -119,7 +119,7 @@ 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] +attribute_stmt: [decorators] (identifier | STRING) [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 @@ -135,8 +135,9 @@ decorator_expr: identifier [call_suffix] //////////// type //////////// type: type_element (OR type_element)* -type_element: schema_type | basic_type | compound_type | literal_type +type_element: schema_type | function_type | basic_type | compound_type | literal_type schema_type: identifier +function_type: LEFT_PARENTHESES [type_element (COMMA type_element)*] RIGHT_PARENTHESES [RIGHT_ARROW type_element] 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 @@ -191,11 +192,11 @@ 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_expr: LEFT_BRACKETS [list_items | NEWLINE [list_items]] 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 +list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE list_item comp_clause) RIGHT_BRACKETS +dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE entry comp_clause+) 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)? @@ -207,7 +208,7 @@ 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_expr: LEFT_BRACE [config_entries | NEWLINE [config_entries]] 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 @@ -220,8 +221,8 @@ 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 +// Tokens ASSIGN: "=" COLON: ":" SEMI_COLON: ";" @@ -332,10 +333,10 @@ STRING: /r?("(?!"").*?(? None Serialize a KCL object `data` to a JSON formatted str and write it into the file `filename`. + - validate(data: any) -> bool + Validate whether the given string is a valid JSON. - yaml - encode(data: any, sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str Serialize a KCL object `data` to a YAML formatted str. + - encode_all(data: [any], sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str + Serialize a sequence of KCL objects into a YAML stream str. - decode(value: str) -> any Deserialize `value` (a string instance containing a YAML document) to a KCL object. + - decode_all(value: str) -> [any] - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None Serialize a KCL object `data` to a YAML formatted str and write it into the file `filename`. + Parse all YAML documents in a stream and produce corresponding KCL objects. + - validate(value: str) -> str + Validate whether the given string is a valid YAML or YAML stream document. - net - split_host_port(ip_end_point: str) -> List[str] Split the 'host' and 'port' from the ip end point. diff --git a/docs/reference/lang/spec/schema.md b/docs/reference/lang/spec/schema.md index c3d09f65..74784d0c 100644 --- a/docs/reference/lang/spec/schema.md +++ b/docs/reference/lang/spec/schema.md @@ -15,7 +15,7 @@ A schema is a language element to define a type of configuration data. To define a schema, the syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | schema_index_signature | statement)* [check_block] _DEDENT ``` diff --git a/docs/reference/lang/spec/statements.md b/docs/reference/lang/spec/statements.md index b09488b9..179410b5 100644 --- a/docs/reference/lang/spec/statements.md +++ b/docs/reference/lang/spec/statements.md @@ -30,8 +30,8 @@ A small statement is comprised of a single logical line. Multiple statements in Generally, assign_stmt is divided into assignment and augmented assignment. The syntax is the following: ```bnf -assign_stmt: target_primary ("=" target_primary)* "=" test | target_primary augassign test -augassign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "or" | "and" +assign_stmt: target_primary (":" type) ("=" target_primary)* "=" test | target_primary aug_assign test +aug_assign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" target_primary: identifier | target_primary DOT identifier ``` @@ -61,7 +61,7 @@ _x -= 1 _filename += ".k" ``` -There is no concept of in-place modification in KCL. The `augassign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. +There is no concept of in-place modification in KCL. The `aug_assign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. In particular, in KCL, the `|=` symbol represents the **union** operation, which is defined as follows: @@ -117,7 +117,7 @@ Assert statements are a convenient way to insert debugging assertions into KCL c The syntax is the following: ``` -assert_stmt: ASSERT test ("," test)? +assert_stmt: ASSERT test ("if" test)? ("," test)? ``` The conditional expression in assert will be evaluated and get a boolean. Report an error if returning a `False`. @@ -178,7 +178,7 @@ else: Schema statements are used to define a type of configuration data. The syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | statement)* [check_block] _DEDENT ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/error.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/error.md index 2a58b553..65420a2f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/error.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/error.md @@ -17,7 +17,7 @@ schema is violated. The syntax of the `assert` statement is the following. ```bnf -assert_stmt: 'assert' test ['if' test] [',' test] +assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? ``` In the basic form, an `assert` statement evaluates an expression. If the diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/kcl-spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/kcl-spec.md index e7cf8da3..798ba840 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/kcl-spec.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/kcl-spec.md @@ -119,7 +119,7 @@ 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] +attribute_stmt: [decorators] (identifier | STRING) [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 @@ -135,8 +135,9 @@ decorator_expr: identifier [call_suffix] //////////// type //////////// type: type_element (OR type_element)* -type_element: schema_type | basic_type | compound_type | literal_type +type_element: schema_type | function_type | basic_type | compound_type | literal_type schema_type: identifier +function_type: LEFT_PARENTHESES [type_element (COMMA type_element)*] RIGHT_PARENTHESES [RIGHT_ARROW type_element] 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 @@ -191,11 +192,11 @@ 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_expr: LEFT_BRACKETS [list_items | NEWLINE [list_items]] 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 +list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE list_item comp_clause) RIGHT_BRACKETS +dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE entry comp_clause+) 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)? @@ -207,7 +208,7 @@ 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_expr: LEFT_BRACE [config_entries | NEWLINE [config_entries]] 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 @@ -220,8 +221,8 @@ 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 +// Tokens ASSIGN: "=" COLON: ":" SEMI_COLON: ";" @@ -332,10 +333,10 @@ STRING: /r?("(?!"").*?(? [as ] -``` - -The rule to search with the module name is very simple: - -- **Step 1**: Searches the module name from the **standard system modules**, then **plugins modules**. - - See **standard system modules** and **plugins modules** for more details. If matched, the module is imported. Otherwise, continue to **Step 2**. -- **Step 2**. Whether a module name starts with a `.` is checked. If yes, the name is a so-called relative pathname, and we go to **Step 5**. Otherwise, continue to **Step 3**. -- **Step 3**: If the module name does not start with any `.`, then the compiler searches the nearest `root path` directory from this directory to the parent, and find the module according to the name just from the `root path`. If no `root path` is found, find the module according to the name from the folder the `.k` file including this `import` statement exists. - - **root path**: the directory contains a `kcl.mod` file. If matched, the module is imported. Otherwise, continue to **Step 4**. -- **Step 4**: Then the compiler checks if the name is the name of any library module that requires explicit loading. If matched, the library module is imported. Otherwise, continue to **Step 6**. -- **Step 5**. For relative importing, find the module according to the name from the folder the `.k` file including this `import` statement exists. Interpret leading dots using the following rule: -- One dot: Ignore. -- Tow or more dots: Suppose there are `n` leading dots, then the searching starts at `n - 1` levels above this folder. If matched, the module is imported. Otherwise, continue to **Step 6**. -- **Step 6**. Module not found, report an error. - -Do case-sensitive search when the operating system allows. If case-sensitive search is not allowed, search directories before regular files. - -In KCL, the `from <> import <>` is unsupported, and relative import is performed with the `import <>` syntax. - -### Uniqueness of Module - -Each module has a unique location path in its scope, so that a module or package could be located with a unique location path, such as `a.b.c`. - -Searching by location path should be supported by the kcl compiler, which needs to provide corresponding searching features through the command line and api form. - -## Standard System Packages - -KCL supports a few standard system modules. The following is the full list of these standard system modules: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackageMember
datetimetoday
now
ticks
date
mathceil
exp
expm1
factorial
floor
gcd
isfinite
isinf
isnan
log
log1p
log2
log10
modf
pow
sqrt
regexreplace
match
compile
findall
search
split
unitsn
u
m
k
K
M
G
T
P
Ki
Mi
Gi
Ti
Pi
to_n
to_u
to_m
to_K
to_M
to_G
to_T
to_P
to_Ki
to_Mi
to_Gi
to_Ti
to_Pi
jsonencode
decode
dump_to_file
yamlencode
decode
dump_to_file
netsplit_host_port
join_host_port
fqdn
parse_IP
to_IP4
to_IP16
IP_string
is_IPv4
is_IP
is_loopback_IP
is_multicast_IP
is_interface_local_multicast_IP
is_link_local_multicast_IP
is_link_local_unicast_IP
is_global_unicast_IP
is_unspecified_IP
base64encode
decode
cryptomd5
sha1
sha224
sha256
sha384
sha512
- -- datetime - - ticks() -> float - Return the current time in seconds since the Epoch. Fractions of a second may be present if the system clock provides them. - - date() -> str - return the `%Y-%m-%d %H:%M:%S` format date. - - now() -> str - return the local time. e.g. `'Sat Jun 06 16:26:11 1998'` - - today() -> str - return the `%Y-%m-%d %H:%M:%S.%{ticks}` format date. -- math - - ceil(x) -> int - Return the ceiling of x as an Integral. This is the smallest integer >= x. - - factorial(x) -> int - Return x!. Raise a error if x is negative or non-integral. - - floor(x) -> int - Return the floor of x as an Integral. This is the largest integer <= x. - - gcd(a: int, b: int) -> int - Return the greatest common divisor of x and y - - isfinite(x) -> bool - Return True if x is neither an infinity nor a NaN, and False otherwise. - - isinf(x) -> bool - Return True if x is a positive or negative infinity, and False otherwise. - - isnan(x) -> bool - Return True if x is a NaN (not a number), and False otherwise. - - modf(x) -> Listfloat, float] - Return the fractional and integer parts of x. Both results carry the sign of x and are floats. - - exp(x) -> float - Return e raised to the power of x. - - expm1(x) -> float - Return exp(x)-1. This function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x. - - log(x, base=2.71828182845904523536028747135266250) -> float - Return the logarithm of x to the base e. - - log1p(x) -> float - Return the natural logarithm of 1+x (base e). The result is computed in a way which is accurate for x near zero. - - log2(x) -> float - Return the base 2 logarithm of x. - - log10(x) -> float - Return the base 10 logarithm of x. - - pow(x, y) -> float - Return x\*\*y (x to the power of y). - - sqrt(x) -> float - Return the square root of x. -- regex - - replace(string: str, pattern: str, replace: str, count=0) -> str - Return the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in string by the replacement. - - match(string: str, pattern: str) -> bool - Try to apply the pattern at the start of the string, returning a bool value True if any match was found, or False if no match was found. - - compile(pattern: str) -> bool - Compile a regular expression pattern, returning a bool value denoting whether the pattern is valid. - - findall(string: str, pattern: str) -> List[str] - Return a list of all non-overlapping matches in the string. - - search(string: str, pattern: str) -> bool - Scan through string looking for a match to the pattern, returning a bool value True if any match was found, or False if no match was found. - - split(string: str, pattern: str, maxsplit=0) -> List[str] - Scan through string looking for a match to the pattern, returning a Match object, or None if no match was found. -- units - - Unit constants - - Fixed point: `n`, `u`, `m`, `k`, `K`, `G`, `T` and `P`. - - Power of 2: `Ki`, `Mi`, `Gi`, `Ti` and `Pi`. - - Functions - - to_n(num: int) -> str - Int literal to string with `n` suffix - - to_u(num: int) -> str - Int literal to string with `u` suffix - - to_m(num: int) -> str - Int literal to string with `m` suffix - - to_K(num: int) -> str - Int literal to string with `K` suffix - - to_M(num: int) -> str - Int literal to string with `M` suffix - - to_G(num: int) -> str - Int literal to string with `G` suffix - - to_T(num: int) -> str - Int literal to string with `T` suffix - - to_P(num: int) -> str - Int literal to string with `P` suffix - - to_Ki(num: int) -> str - Int literal to string with `Ki` suffix - - to_Mi(num: int) -> str - Int literal to string with `Mi` suffix - - to_Gi(num: int) -> str - Int literal to string with `Gi` suffix - - to_Ti(num: int) -> str - Int literal to string with `Ti` suffix - - to_Pi(num: int) -> str - Int literal to string with `Pi` suffix -- json - - encode(data: any, sort_keys: bool = False, indent: int = None, ignore_private: bool = False, ignore_none: bool = False) -> str - Serialize a KCL object `data` to a JSON formatted str. - - decode(value: str) -> any - Deserialize `value` (a string instance containing a JSON document) to a KCL object. - - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None - Serialize a KCL object `data` to a JSON formatted str and write it into the file `filename`. -- yaml - - encode(data: any, sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str - Serialize a KCL object `data` to a YAML formatted str. - - decode(value: str) -> any - Deserialize `value` (a string instance containing a YAML document) to a KCL object. - - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None - Serialize a KCL object `data` to a YAML formatted str and write it into the file `filename`. -- net - - split_host_port(ip_end_point: str) -> List[str] - Split the 'host' and 'port' from the ip end point. - - join_host_port(host, port) -> str - Merge the 'host' and 'port'. - - fqdn(name: str = '') -> str - Return Fully Qualified Domain Name (FQDN). - - parse_IP(ip) -> str - Parse 'ip' to a real IP address - - to_IP4(ip) -> str - Get the IP4 form of 'ip'. - - to_IP16(ip) -> int - Get the IP16 form of 'ip'. - - IP_string(ip: str | int) -> str - Get the IP string. - - is_IPv4(ip: str) -> bool - Whether 'ip' is a IPv4 one. - - is_IP(ip: str) -> bool - Whether ip is a valid ip address. - - is_loopback_IP(ip: str) -> bool - Whether 'ip' is a loopback one. - - is_multicast_IP(ip: str) -> bool - Whether 'ip' is a multicast one. - - is_interface_local_multicast_IP(ip: str) -> bool - Whether 'ip' is a interface, local and multicast one. - - is_link_local_multicast_IP(ip: str) -> bool - Whether 'ip' is a link local and multicast one. - - is_link_local_unicast_IP(ip: str) -> bool - Whether 'ip' is a link local and unicast one. - - is_global_unicast_IP(ip: str) -> bool - Whether 'ip' is a global and unicast one. - - is_unspecified_IP(ip: str) -> bool - Whether 'ip' is a unspecified one. -- base64 - - encode(value: str, encoding: str = "utf-8") -> str - Encode the string `value` using the codec registered for encoding. - - decode(value: str, encoding: str = "utf-8") -> str - Decode the string `value` using the codec registered for encoding. -- crypto - - md5(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `MD5` and the codec registered for encoding. - - sha1(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA1` and the codec registered for encoding. - - sha224(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA224` and the codec registered for encoding. - - sha256(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA256` and the codec registered for encoding. - - sha384(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA384` and the codec registered for encoding. - - sha512(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA512` and the codec registered for encoding. -- manifests - - `yaml_stream(values: [any], opts: {str:} = {sort_keys = False, ignore_private = True, ignore_none = False, sep = "---"}`. This function is used to serialize the KCL object list into YAML output with the `---` separator - -### The Built-in System Package - -KCL provides a list of built-in system modules, which are loaded automatically and can be directly used without providing any module name. For example, `print` is a widely used built-in module. - -The following is the full list of these built-in system modules: - -- print() - - The print function. -- multiplyof(a, b) - - Check if the modular result of a and b is 0 -- isunique(inval) - - Check if a list has duplicated elements -- len(inval) - Return the length of a value -- abs(x) - Return the absolute value of the argument. -- all(iterable) - Return True if bool(x) is True for all values x in the iterable. If the iterable is empty, return True. -- any(iterable) - Return True if bool(x) is True for any x in the iterable. If the iterable is empty, return False. -- bin(number) - Return the binary representation of an integer. -- hex(number) - Return the hexadecimal representation of an integer. -- oct(number) - Return the octal representation of an integer. -- ord(c) -> int - Return the Unicode code point for a one-character string. -- sorted(iterable) - Return a new list containing all items from the iterable in ascending order. A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order. -- range(start, end, step=1) - Return the range of a value with start, end and step parameter. -- min(iterable) - With a single iterable argument, return its smallest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the smallest argument. -- max(iterable) - With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument. -- sum(iterable, start) - Return the sum of a 'start' value (default: 0) plus an iterable of numbers. When the iterable is empty, return the start value. This function is intended specifically for use with numeric values and may reject non-numeric types. -- pow(x, y, z) - Equivalent to `x**y` (with two arguments) or `x**y % z` (with three arguments). Some types, such as ints, are able to use a more efficient algorithm when invoked using the three argument form. -- round(number, ndigits) - Round a number to a given precision in decimal digits. The return value is an integer if ndigits is omitted or None. Otherwise the return value has the same type as the number. ndigits may be negative. -- typeof(x: any, \*, full_name: bool = False) -> str - Return the type of the value 'x' at runtime. When the 'full_name' is 'True', return the full package type name such as `pkg.schema`. -### Plugin Modules +See 'union' in `expressions` spec for more details. -KCL compiler needs to provide the ability to dynamically expand and load plugin modules without modifying the compiler itself. KCL compiler needs to support flexible pluggable module extension mechanism, so that KCL users can use more abundant built-in function capabilities to simplify writing. +#### Override -KCL compiler needs to ensure the stability and safety of the expansion mechanism, without affecting the core of the compiler. +Pattern: `identifier = E` -Searching extended plugin module is performed after the standard system module. The standard system module has a higher priority in naming. If it exists a standard or built-in system module with the same name, the extended plugin module will be ignored. +The value of the expression `E` will override the element value. -Importing and using the extended plugin module should be consistent with the standard or built-in system module. +Examples: -### Replacing Standard System Packages +```python +a = A { + # override {c:4} to the element b, suppose b is a schema with an int type attribute c. + b = { + c: 4 + } +} +``` -Replacing standard system modules is not allowed. +Unlike union, the override operation will reassign the element with a brand new value. +For basic type value, `union` and `override` have equivalent effects. -## Examples +Note: -We show more module features through an example. +- Especially, we can "delete" its content by overriding the element to `Undefined`, such as `{ a = Undefined }`. -Suppose we have the following directories and files: +#### Insert +Pattern: `identifier += E` +Insert only works for list type `identifier`. + +List `E` will be inserted just after the specified index of the list `identifier`, and the following elements after the index will be automatically shifted. + +Examples: + +```python +a = A { + # insert {c:4} to the end position(just after index=1), suppose b is a list of schema with an int type attribute c. + b += { + c: 4 + } +} ``` - . - ├── mod1.k - ├── mod2.k - ├── pkg1 - │   ├── def1.k - │   ├── def2.k - │   └── def3init.k - └── pkg2 - ├── file2.k - └── subpkg3 - └── file3.k + +If no index is specified, the last index will be used. + +The type of 'E' must be compatible with the type of list. See `types` for more details. + +#### Index Signature + +Index signatures can be defined in the KCL schema, and it means that the key-value constraints of the index signature can be used to construct a dict with the schema type, or additional checks can be added to the relaxed schema attributes to enhance the KCL type and semantic checks. + +- Use the form `[{attr_alias}: {key_type}]: {value_type}` to define an index signature in the schema, and `{attr_alias}` can be omitted. + +```python +schema Map: + """ + Map is a relaxed schema with a key of str type and a value of str type + """ + [str]: str # `{attr_alias}` can be omitted. + +data = Map { + key1 = "value1" + key2 = "value2" +} ``` -From the structure we can see that `pkg1` and `pkg2` are two packages, `subpkg3` is a subpackage of `pkg2`, and `mod1.k` and `mod2.k` are regular modules. +- Mandatory all attributes of the schema key and value types -### Importing a Standard System Package +```python +schema Person: + name: str + age: int # error, conflicts with the index signature definition `[str]: str` + [str]: str # The values of all attributes of the schema can only be strings +``` -The following statement can import the standard system module `math` +- Mandatory all attribute key and value types are defined in the schema, which is equivalent to restricting all attribute types except the relaxed attributes. ```python -import math +schema Person: + name: str + age: int + [...str]: str # Except for the `name` and `age` attributes, the key type of all other attributes of the schema must be `str`, and the value type must also be `str`. ``` -This is the only way to import a standard system module. After importing a standard system module, functions, variables and schemas defined in it can be used. For example, the following statement uses the `log10` function -defined in `math` +- Define the index signature attribute alias and use it with the check block. + +```python +schema Data: + [dataName: str]: str + check: + dataName in ["Alice", "Bob", "John"] + +data = Data { + Alice = "10" + Bob = "12" + Jonn = "8" # error Jonn not in ["Alice", "Bob", "John"] +} +``` ```python -a = math.log10(100) # a is 2 after computation. +import regex + +schema DataMap: + [attr: str]: str + check: + regex.match(attr, r'^[-._a-zA-Z0-9]+$') + +data = DataMap { + key1 = "value1" + "foo.bar" = "value2" # check error +} ``` -### Importing a Regular Module +### Schema Context + +The schema definition space can be regarded as a separate function context. + +Init statement could be defined inside the schema, the syntax is the following: -In `mod1.k`, we can import `mod2` using one of the following syntaxes. +```bnf +statement: small_stmt NEWLINE | if_stmt +``` + +The following is an example: ```python -import mod2 +schema Person: + firstName: str = "John" + lastName: str + # fullName is generated by firstName and lastName in a separate init statement + fullName: str = firstName + ' ' + lastName + +JohnDoe = Person { + lastName = "Doe" +} ``` +The result is a **dict**: + ```python -import .mod2 +{ + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' +} ``` -The difference is that in the first syntax, the KCL compiler will first try to check if `mod2` matches any of the standard system modules' name. Since it does not match any standard system module's name, the statement will check the directory where `mod1.k` resists in, like what the second statement does. +If statement, expr statement and assert statement are supported as a schema init +statement. See more in statement spec. -Suppose in `mod2.k` there is a definition of a variable:: +- The attributes must be defined first, including inherited ones, and then used in the init statement. +- Statements in the schema context will be executed sequentially. +- The value of attributes referenced in the init statement will be evaluated at runtime. + See the **Configuration Definition** section for the assignment rules of non-referenced attributes. For example, `"fullName"` in Person is generated by `"firstName"` and `"lastName"` evaluated at runtime, in which firstName is 'John', and lastName is "Doe". + +The immutability of attributes in the schema context follows the same rules as the immutability of global variables: ```python -a = 100 +schema Person: + age: int = 1 # Immutable attribute + _name: str = "Alice" # Mutable attribute + + age = 10 # Error + _name = "Bob" # Ok +``` + +#### Arguments + +Schema context can also have arguments. The following is an example. + +```python +schema Person[separator]: + firstName: str = "John" + lastName: str + fullName: str = firstName + separator + lastName + +JohnDoe = Person('_') { + lastName = "Doe" +} +``` + +The example is similar to the previous one, except that the separator character used in +the `"fullName"` member is passed in as an argument. The way to perform a schema generation +when the schema has an initialization function with arguments is demonstrated in the code. + +### Check Block + +Optionally, a check block can be added to a schema definition to allow +additional checking to be performed. + +The syntax is the following: + +```bnf +check_block: "check" ":" NEWLINE _INDENT check_expr+ _DEDENT +check_expr: test (IF test)? [":" primary_expr] NEWLINE ``` -After importing `mod2`, we can access `a` in `mod1.k` using the following syntax +In terms of grammatical definition, a check block consists of a list of conditional expressions. The following is an example: ```python -b = mod2.a +schema employee(person): + bankCard: int + gender: str + + check: + len(str(bankCard)) == 16 + gender in ['male', 'female'], "The gender {} is unsupported".format(gender) ``` -### Importing a Package +The ability of KCL check expressions covers the abilities that can be defined by OpenAPI spec and is aligned with the ability of logical expressions. We consider further aligning the syntax with `CEL` spec. +Whether to support `lambda expressions` is still under discussion. + +Summary: + +- A check block consists of one or more logical **expressions**. +- When defining a configuration, the expressions in the check block are evaluated + in any order. If any of the expression is `False`, an error is reported. +- A custom error message can be provided after an expression. + +### Specifying Types + +Optionally, the type of any member of a schema can be specified. As previous examples have shown. + +A member can be of a basic type, such as a string (`str`), a floating-point number (`float`), a fixed-point number (`int`) or a boolean number (`bool`). + +A member can also be of a dictionary generated from another schema. In such a case, the name of the other schema is used as the type name. + +A member can also be a list or an ordinary dict: + +- A list with unspecified type of elements is `[]`. +- A list with elements of type `t` is `[t]`. Here `t` is another type. +- A dict with keys of type `kt` and values of type `vt` is `{kt:vt}`. +- `kt`, `vt` or both of them can be missing, like a list with unspecified type of elements. + +The followings are some more examples: -In `mod1.k`, we can import `pkg1` using one of the following syntaxes. +- A list of lists of strings: `[[str]]`. +- A dict of keys with the type string and unspecified value types: `{str:}`. + +A member can be a **union type** defined by `|`, such as `a | b`, which means the type of the member could be a or b. + +A union type can include types of `int`, `str`, `float`, `bool`, `list` and `dict` and support type nesting e.g. `{str:str|int}` and `[[int|str]|str|float]`, etc. + +Examples: ```python -import pkg1 +schema x: + p: int | str # p could be defined as a int or string ``` +### Immutability + +KCL pursues strict immutability of schema attributes. It's generally followed the rules: + +- For the attributes of the basic type, such as string, int and float, it's allowed to be reassigned + through the init statement in **schema context** or by the **configuration definition**. +- For the attributes of list, dict and schema type, it's allowed to be reassigned only by the init statement in **schema context**. The content of it is allowed to be operated in **schema context** or by the **configuration definition**. +- Any other attempt to reassign or modify schema attribute will report an error. + +#### Assign by Value + +When using a schema variable to assign the value to another variable, we can only get a deep copy of its value, not a pointer or reference. That is, modifying the assigned value will not change the assigned schema variable. + ```python -import .pkg1 +schema Person: + name: str + +person = { + name = "Alice" +} +personCopy = person # 'personCopy' is a deep copy of 'person' and modifying 'personCopy' will not affect 'person' ``` -The difference is that in the first syntax, the KCL compiler will first try to check if `pkg1` matches any of the standard system modules' name. Since it does not match any standard system module's name, the statement will check the directory where `mod1.k` resists in, like what the second statement does. +### Union Operator + +For list, dict and schema, we can union delta to existing data. For example: + +```python +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name = { + firstName = "John" + } -We can use similar statements to import `pkg2`. Note that importing `pkg2` will not import `subpkg3`. + # union a schema and a dict + name: Name { + lastName = "Doe" + } -The name of the package is the name of the imported module. +person = Person {} +``` -Suppose in `file2.k` that is inside `pkg2` there is a definition to variable `foo` +The result is a **dict**: ```python -foo = 100 +{ + 'person': { + 'name': { + 'firstName': 'Jhon', + 'lastName': 'Doe' + } + } +} ``` -This variable can be used in `mod1.k` after importing `pkg2` like the following +### Other Operators + +Except for `assignment` and `union assignment`, it's not support other operators on schema type data. +Report an error if trying to use other operators on schema type data. + +### Deprecated + +The schema attribute can be marked as deprecated once it's considered invalid. ```python -bar = pkg2.foo +schema Person: + @deprecated(version="1.1.0", reason="use fullName instead", strict=True) + name: str + ... # Omitted contents + +person = Person { + # report an error on configing a deprecated attribute + name = "name" +} ``` -### Importing a Subpackage +- Deprecated attributes cannot be configured under any circumstances. Report an error or warning once the attribute is assigned. +- Define the expired version of the attribute through **version**, and define the reason for the attribute expired through **reason**. +- When strict is true, the attribute assignment will cause an error, otherwise it will report a warning and ignore the attribute assignment. -To import `subpkg3` from `mod1.k`, one of the following statements can be used. +### Composition + +The composition is a common way to define complex structures. KCL provides simplified means for the configuration definition of combined structures. + +Assuming we have the following schemas, which is defined by a combination of multiple schemas. ```python -import pkg2.subpkg3 +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + name: str + persons: [Person] ``` +To config a group: + ```python -import .pkg2.subpkg3 +group = Group { + name = "group" + persons = [{ + name = { + firstName = "John" + lastName = "Doe" + } + age = 24 + }] +} ``` -The behaviors of these statements are identical. +- Top-level schema name is required to config a schema. +- The schema of the attributes in the schema can be omitted. -The name of the subpackage is the name of the imported module. +Multi-level nested schemas will make the configuration verbose. KCL supports defining attributes in the schema through `selector expression`. The selector form is **x.y.z**, see the following example: -Suppose in `file3.k` that is inside `subpkg3` there is a definition to variable `foo` +```python +group = Group { + name = "group" + persons = [{ + name.firstName = "John" + name.lastName = "Doe" + age = 24 + }] +} +``` + +- Selector can be used to represent attribute in a schema + +### Inheritance + +Inheritance is an effective means to define a hierarchical structure definition, and KCL supports limited **single inheritance** of the schema. ```python -foo = 100 +schema Person: + firstName: str + lastName: str + +# schema Scholar inherits schema Person +schema Scholar(Person): + fullName: str = firstName + '_' + lastName + subject: str + +JohnDoe = Scholar { + firstName = "John", + lastName = "Doe", + subject = "CS" +} ``` -This variable can be used in `mod1.k` after importing `subpkg3` like the following +The result is a **dict**: ```python -bar = subpkg3.foo +{ + 'JohnDoe': { + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' + 'subject': 'CS' + } +} ``` -### Relative Importing +Each schema can be treated as a separated function context. Statements, including attribute statements and init statements, in the context of schemas will be evaluated from base schema to subschema according to the inheritance order. Each schema context is evaluated only once sequentially. The same goes for expressions in the check block. In the example, firstName and lastName are configured in the context of Person schema, and fullName is formed by splicing firstName and lastName in the context of Scholar schema. + +The default value can be modified in each schema. Value defined in **Configuration Definition** has a higher priority than the default value. Attributes with default values in any schema context ​​will eventually be unioned by configuration data. References to attributes in the schema context statements will use the value with unioned configuration data on evaluating at runtime. For example: -Relative importing is useful when there is code trying to import modules that does not exist recursively inside the current directory. +```python +schema a: + x = 1 + y = x * 2 -For example, the following statements, if written in `file3.k`, can be used to import `pkg2`, `pkg1` and `mod2` respectively. +schema b(a): + x = 2 + +v = a { + x = 3 +} + +``` + +The result is a **dict**: ```python -import ...pkg2 # Go two levels up then import pkg2 -import ...pkg1 # Go two levels up then import pkg1 -import ...mod2 # Go two levels up then import mod2 +{ + 'v': { + 'x': 3 + 'y': 6 + } +} ``` -### Importing from a Root Path +Notes: + +- Report an error if inheriting more than one base schema. +- The type of the base schema attribute cannot be modified in the subschema. +- Report an error if inheriting a **mixin**. +- Report an error when a circular dependency occurs. -Suppose we have a `kcl.mod` file in the directory to mark it as a root path, then we have the following files: +Limitations: +Since inheritance will derive some complex demands, we are cautious about these complex demands. There are still some restrictions on inheritance, and it's still under discussion. + +- KCL provides limited and deterministic polymorphism support, more complex and flexible polymorphism support, such as **self**, **super** keywords, are temporarily not included in the schema definition. +- Currently, KCL only supports the polymorphism of the inherited attributes of the schema, and does not support the polymorphism of the expressions in the check block. +- For the case of multiple levels of schema inheritance, the schema arguments can only be passed to the last level of sub-schema. + +### Mixin + +In addition to **composition** and **inheritance**, KCL supports declarative reuse of schema code through the **mixin** mechanism. To use a mixin, we only need to declare the **mixin** in the schema definition. + +The **mixin** syntax is the following: + +```bnf +//////////// mixin_stmt //////////// +mixin_stmt: "mixin" "[" [mixins | multiline_mixins] "]" "\n" +multiline_mixins: "\n" _INDENT mixins "\n" _DEDENT +mixins: operand_name ("," ("\n" mixins | operand_name))* ``` - . - |── kcl.mod - ├── mod1.k - ├── mod2.k - ├── pkg1 - │   ├── def1.k - │   ├── def2.k - │   └── def3init.k - └── pkg2 - ├── file2.k - └── subpkg3 - └── file3.k + +Here is a simple example: + +```python +schema Person: + mixin [FullNameMixin] + firstName: str = "default" + lastName: str + +schema FullNameMixin: + fullName: str = "{} {}".format(firstName, lastName) + +JohnDoe = Person { + firstName = "John" + lastName = "Doe" +} ``` -In `pkg1` `def1.k`, we can import `pkg2.subpkg3` `file3` using the following syntaxes. +The result is a **dict**: ```python -import pkg2.subpkg3.file3 +{ + 'JohnDoe': { + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' + } +} ``` -Importing from the root path is very convenient when the code is trying to import modules from a directory needs to look up multiple directories above this directory. At also, it is helpful to organize a large number of files in a root directory. +Multiple mixins can be added to a single schema, and mixins context will be evaluated after the host schema context at runtime. In the inheritance scenario, the mixin context can be regarded as a part of the host schema context, and the overall evaluation of schema context order is not affected. + +Notes: + +- The name of **mixin** schema must end with 'Mixin', otherwise an error will be reported. +- The attributes referenced in the **mixin** must be defined in the **mixin** itself or host schema, otherwise an error will be reported. + +### Protocol + +In addition to schema, an additional type definition method `protocol` is provided in KCL, and its properties are as follows: -### Importing a Module Inside a Package +- In a protocol, only attributes and their types can be defined, complex logic and check expressions cannot be written, and mixins cannot be used. +- A protocol can only inherit or refer to other protocols, but cannot inherit or refer to other schemas. -Note that `subpkg3` is only implemented with one file `file3.k`. The file can be regarded as a regular module and imported directly. +We can use **protocol** to add an optional host type to the dynamically inserted **mixin**. -In `mod1.k`, the importing statement would be:: +The **mixin** can define its host type through the `for` keyword, and internally it will query the type corresponding to the attribute from the host type. ```python -import pkg2.subpkg3.file3 +protocol DataProtocol: # A mixin host type + data: str + +mixin DataMixin for DataProtocol: # Using the `for` keyword to define a mixin host type + x: int = data # The type of `data` is `str`, which is from `data` of `DataProtocol` +``` + +In `DataMixin`, the `data` attribute is obtained according to the `DataProtocol` host type as `str` type, and then a type error will occur when the value is assigned to `x` of type `int`: + +```python +protocol DataProtocol: + data: str + +mixin DataMixin for DataProtocol: + x: int = data # Error: expect int, got str + x: str = data # Error: can't change schema field type of 'x' from int to str ``` -Different from importing `subpkg3`, now the name of the module is `file3`. We can access the variable `foo` defined in this module with the following -statement +Please note that the host type **protocol** can only be used for **mixin** definitions (the suffix name is `Mixin`), otherwise an error will be reported. ```python -bar = file3.foo +protocol DataProtocol: + data: str + +schema Data for DataProtocol: # Error: only schema mixin can inherit from protocol + x: str = data +``` + +### Schema Context Evaluation + +The schema definition is composed of attribute statements, configuration data, init statements, mixins, and checks. In a separate schema context, the evaluation top-down order is as follows: + +``` +|------------------------------------------| +| attribute defaulting | +|------------------------------------------| +| configuration union | +|------------------------------------------| +| attribute templating | +|------------------------------------------| +| statements in declaration order | +|------------------------------------------| +| mixins in declaration order | +|------------------------------------------| +| check expressions in any order | +|------------------------------------------| +``` + +In the case of schema inheritance, each schema context is evaluated from the base schema in the order of inheritance, and each context is evaluated only once. +Suppose there are schemas a, b, and c, where c inherits b and b inherits a. Schema contexts will be evaluated in top-down order as: + +``` +|-----------------| +| schema a | +|-----------------| +| schema b | +|-----------------| +| schema c | +|-----------------| ``` -### Precedence of Importing +### Members -When an import statement specifies a package to import, the virtual machine first looks for a directory named according to the import statement in the file system. +Built-in function and members of schema -If such a directory is not found, the virtual machine looks for a single file module. +- instances() + Return the list of existing instances of a schema. -For example, when the statement `import a.b.c` appears, the virtual machine first looks for the directory `a/b/c` from the directory of the current file. If `a/b/c` is not found, the virtual machine looks for a file named `a/b/c.k`. If the file is also absent, an error is reported. +### Irrelevant Order Calculation -### Package Implemented with Multiple Files +The irrelevant order calculation in the schema indicates the reference relationship between the internal attributes of the schema. For example, when we declare an expression of the form `a = b + 1`, the calculation of the value of `a` depends on the calculation of the value of `b`. When the compiler calculate the value of `a` and the value of `a` depends on the value of `b`, the compiler will choose to first calculate the value of `b`, and then calculate the value of a according to the expression `a = b + 1`, which is slightly different from the calculation method of traditional procedural language the difference. -Package `pkg1` is implemented with multiple KCL files. +Since the calculation of values in the schema is based on dependencies, just like a directed acyclic graph traverses each node in the graph according to the order of topological sorting, the order of declaration of attributes in the schema is not so important, so the feature is called the irrelevant order calculation. -Multiple files can be used to define variables, schemas and functions, and they can access names defined in other files of this package. +Please note that there can be no circular references between different schema attribute values. -For example, suppose `def1.k` defines a variable `foo`, `def2.k` defines `bar`, and `def3init.k` defines a variable `baz`, when `pkg1` is imported by `mod1.k`, all these variable can be used +We can see this feature through the following examples. ```python -import pkg1 -a = pkg1.foo + pkg1.bar + pkg1.baz +schema Person: + name?: str + age: int = _age + + _age = 10 + + if name == "Son": + _age = 18 + +schema Son(Person): + name: str = "Son" + +person = Person {} +son = Son {} +``` + +The output is + +```yaml +person: + name: null + age: 10 +son: + name: Son + age: 18 ``` -Inside a module, names defined in a file can be accessed in another file without further importing. For example, suppose `bar` in `def2.k` would invoke `foo` defined in `def1.k`, it can directly use `foo` like the following +Besides, we can achieve KCL polymorphism such as ```python -bar = foo + 1 +schema Person: + name?: str + _age: int = _age + + _age = 10 + if name == "Son": + _age = 18 + elif name == "SonConf": + _age = 24 + +schema Son(Person): + name: str = "Son" + +person = Person() {} +son = Son() { + name = "SonConf" +} ``` + +The output is + +```yaml +person: + name: null + age: 10 +son: + name: SonConf + age: 24 +``` + +More examples: + +```python +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int = _value + + if n <= 2: + _value = 1 + else: + _value = (Fib {n = n1}).value + (Fib {n = n2}).value + +fib8 = (Fib {n = 8}).value +``` + +The output is + +```yaml +fib8: 21 +``` + +As in the above examples, we can see that in the schema, we only need to simply specify the dependency between attributes, and the compiler will automatically calculate the value based on the dependency, which can help us save a lot of boilerplate code and reduce configuration difficulty of writing. diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/schema.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/schema.md index c3d09f65..74784d0c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/schema.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/schema.md @@ -15,7 +15,7 @@ A schema is a language element to define a type of configuration data. To define a schema, the syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | schema_index_signature | statement)* [check_block] _DEDENT ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/statements.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/statements.md index b09488b9..179410b5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/statements.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/reference/lang/spec/statements.md @@ -30,8 +30,8 @@ A small statement is comprised of a single logical line. Multiple statements in Generally, assign_stmt is divided into assignment and augmented assignment. The syntax is the following: ```bnf -assign_stmt: target_primary ("=" target_primary)* "=" test | target_primary augassign test -augassign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "or" | "and" +assign_stmt: target_primary (":" type) ("=" target_primary)* "=" test | target_primary aug_assign test +aug_assign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" target_primary: identifier | target_primary DOT identifier ``` @@ -61,7 +61,7 @@ _x -= 1 _filename += ".k" ``` -There is no concept of in-place modification in KCL. The `augassign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. +There is no concept of in-place modification in KCL. The `aug_assign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. In particular, in KCL, the `|=` symbol represents the **union** operation, which is defined as follows: @@ -117,7 +117,7 @@ Assert statements are a convenient way to insert debugging assertions into KCL c The syntax is the following: ``` -assert_stmt: ASSERT test ("," test)? +assert_stmt: ASSERT test ("if" test)? ("," test)? ``` The conditional expression in assert will be evaluated and get a boolean. Report an error if returning a `False`. @@ -178,7 +178,7 @@ else: Schema statements are used to define a type of configuration data. The syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | statement)* [check_block] _DEDENT ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/error.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/error.md index 2a58b553..65420a2f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/error.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/error.md @@ -17,7 +17,7 @@ schema is violated. The syntax of the `assert` statement is the following. ```bnf -assert_stmt: 'assert' test ['if' test] [',' test] +assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? ``` In the basic form, an `assert` statement evaluates an expression. If the diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/kcl-spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/kcl-spec.md index e7cf8da3..798ba840 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/kcl-spec.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/kcl-spec.md @@ -119,7 +119,7 @@ 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] +attribute_stmt: [decorators] (identifier | STRING) [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 @@ -135,8 +135,9 @@ decorator_expr: identifier [call_suffix] //////////// type //////////// type: type_element (OR type_element)* -type_element: schema_type | basic_type | compound_type | literal_type +type_element: schema_type | function_type | basic_type | compound_type | literal_type schema_type: identifier +function_type: LEFT_PARENTHESES [type_element (COMMA type_element)*] RIGHT_PARENTHESES [RIGHT_ARROW type_element] 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 @@ -191,11 +192,11 @@ 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_expr: LEFT_BRACKETS [list_items | NEWLINE [list_items]] 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 +list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE list_item comp_clause) RIGHT_BRACKETS +dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE entry comp_clause+) 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)? @@ -207,7 +208,7 @@ 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_expr: LEFT_BRACE [config_entries | NEWLINE [config_entries]] 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 @@ -220,8 +221,8 @@ 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 +// Tokens ASSIGN: "=" COLON: ":" SEMI_COLON: ";" @@ -332,10 +333,10 @@ STRING: /r?("(?!"").*?(? [as ] -``` - -The rule to search with the module name is very simple: - -- **Step 1**: Searches the module name from the **standard system modules**, then **plugins modules**. - - See **standard system modules** and **plugins modules** for more details. If matched, the module is imported. Otherwise, continue to **Step 2**. -- **Step 2**. Whether a module name starts with a `.` is checked. If yes, the name is a so-called relative pathname, and we go to **Step 5**. Otherwise, continue to **Step 3**. -- **Step 3**: If the module name does not start with any `.`, then the compiler searches the nearest `root path` directory from this directory to the parent, and find the module according to the name just from the `root path`. If no `root path` is found, find the module according to the name from the folder the `.k` file including this `import` statement exists. - - **root path**: the directory contains a `kcl.mod` file. If matched, the module is imported. Otherwise, continue to **Step 4**. -- **Step 4**: Then the compiler checks if the name is the name of any library module that requires explicit loading. If matched, the library module is imported. Otherwise, continue to **Step 6**. -- **Step 5**. For relative importing, find the module according to the name from the folder the `.k` file including this `import` statement exists. Interpret leading dots using the following rule: -- One dot: Ignore. -- Tow or more dots: Suppose there are `n` leading dots, then the searching starts at `n - 1` levels above this folder. If matched, the module is imported. Otherwise, continue to **Step 6**. -- **Step 6**. Module not found, report an error. - -Do case-sensitive search when the operating system allows. If case-sensitive search is not allowed, search directories before regular files. - -In KCL, the `from <> import <>` is unsupported, and relative import is performed with the `import <>` syntax. - -### Uniqueness of Module - -Each module has a unique location path in its scope, so that a module or package could be located with a unique location path, such as `a.b.c`. - -Searching by location path should be supported by the kcl compiler, which needs to provide corresponding searching features through the command line and api form. - -## Standard System Packages - -KCL supports a few standard system modules. The following is the full list of these standard system modules: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackageMember
datetimetoday
now
ticks
date
mathceil
exp
expm1
factorial
floor
gcd
isfinite
isinf
isnan
log
log1p
log2
log10
modf
pow
sqrt
regexreplace
match
compile
findall
search
split
unitsn
u
m
k
K
M
G
T
P
Ki
Mi
Gi
Ti
Pi
to_n
to_u
to_m
to_K
to_M
to_G
to_T
to_P
to_Ki
to_Mi
to_Gi
to_Ti
to_Pi
jsonencode
decode
dump_to_file
yamlencode
decode
dump_to_file
netsplit_host_port
join_host_port
fqdn
parse_IP
to_IP4
to_IP16
IP_string
is_IPv4
is_IP
is_loopback_IP
is_multicast_IP
is_interface_local_multicast_IP
is_link_local_multicast_IP
is_link_local_unicast_IP
is_global_unicast_IP
is_unspecified_IP
base64encode
decode
cryptomd5
sha1
sha224
sha256
sha384
sha512
- -- datetime - - ticks() -> float - Return the current time in seconds since the Epoch. Fractions of a second may be present if the system clock provides them. - - date() -> str - return the `%Y-%m-%d %H:%M:%S` format date. - - now() -> str - return the local time. e.g. `'Sat Jun 06 16:26:11 1998'` - - today() -> str - return the `%Y-%m-%d %H:%M:%S.%{ticks}` format date. -- math - - ceil(x) -> int - Return the ceiling of x as an Integral. This is the smallest integer >= x. - - factorial(x) -> int - Return x!. Raise a error if x is negative or non-integral. - - floor(x) -> int - Return the floor of x as an Integral. This is the largest integer <= x. - - gcd(a: int, b: int) -> int - Return the greatest common divisor of x and y - - isfinite(x) -> bool - Return True if x is neither an infinity nor a NaN, and False otherwise. - - isinf(x) -> bool - Return True if x is a positive or negative infinity, and False otherwise. - - isnan(x) -> bool - Return True if x is a NaN (not a number), and False otherwise. - - modf(x) -> Listfloat, float] - Return the fractional and integer parts of x. Both results carry the sign of x and are floats. - - exp(x) -> float - Return e raised to the power of x. - - expm1(x) -> float - Return exp(x)-1. This function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x. - - log(x, base=2.71828182845904523536028747135266250) -> float - Return the logarithm of x to the base e. - - log1p(x) -> float - Return the natural logarithm of 1+x (base e). The result is computed in a way which is accurate for x near zero. - - log2(x) -> float - Return the base 2 logarithm of x. - - log10(x) -> float - Return the base 10 logarithm of x. - - pow(x, y) -> float - Return x\*\*y (x to the power of y). - - sqrt(x) -> float - Return the square root of x. -- regex - - replace(string: str, pattern: str, replace: str, count=0) -> str - Return the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in string by the replacement. - - match(string: str, pattern: str) -> bool - Try to apply the pattern at the start of the string, returning a bool value True if any match was found, or False if no match was found. - - compile(pattern: str) -> bool - Compile a regular expression pattern, returning a bool value denoting whether the pattern is valid. - - findall(string: str, pattern: str) -> List[str] - Return a list of all non-overlapping matches in the string. - - search(string: str, pattern: str) -> bool - Scan through string looking for a match to the pattern, returning a bool value True if any match was found, or False if no match was found. - - split(string: str, pattern: str, maxsplit=0) -> List[str] - Scan through string looking for a match to the pattern, returning a Match object, or None if no match was found. -- units - - Unit constants - - Fixed point: `n`, `u`, `m`, `k`, `K`, `G`, `T` and `P`. - - Power of 2: `Ki`, `Mi`, `Gi`, `Ti` and `Pi`. - - Functions - - to_n(num: int) -> str - Int literal to string with `n` suffix - - to_u(num: int) -> str - Int literal to string with `u` suffix - - to_m(num: int) -> str - Int literal to string with `m` suffix - - to_K(num: int) -> str - Int literal to string with `K` suffix - - to_M(num: int) -> str - Int literal to string with `M` suffix - - to_G(num: int) -> str - Int literal to string with `G` suffix - - to_T(num: int) -> str - Int literal to string with `T` suffix - - to_P(num: int) -> str - Int literal to string with `P` suffix - - to_Ki(num: int) -> str - Int literal to string with `Ki` suffix - - to_Mi(num: int) -> str - Int literal to string with `Mi` suffix - - to_Gi(num: int) -> str - Int literal to string with `Gi` suffix - - to_Ti(num: int) -> str - Int literal to string with `Ti` suffix - - to_Pi(num: int) -> str - Int literal to string with `Pi` suffix -- json - - encode(data: any, sort_keys: bool = False, indent: int = None, ignore_private: bool = False, ignore_none: bool = False) -> str - Serialize a KCL object `data` to a JSON formatted str. - - decode(value: str) -> any - Deserialize `value` (a string instance containing a JSON document) to a KCL object. - - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None - Serialize a KCL object `data` to a JSON formatted str and write it into the file `filename`. -- yaml - - encode(data: any, sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str - Serialize a KCL object `data` to a YAML formatted str. - - decode(value: str) -> any - Deserialize `value` (a string instance containing a YAML document) to a KCL object. - - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None - Serialize a KCL object `data` to a YAML formatted str and write it into the file `filename`. -- net - - split_host_port(ip_end_point: str) -> List[str] - Split the 'host' and 'port' from the ip end point. - - join_host_port(host, port) -> str - Merge the 'host' and 'port'. - - fqdn(name: str = '') -> str - Return Fully Qualified Domain Name (FQDN). - - parse_IP(ip) -> str - Parse 'ip' to a real IP address - - to_IP4(ip) -> str - Get the IP4 form of 'ip'. - - to_IP16(ip) -> int - Get the IP16 form of 'ip'. - - IP_string(ip: str | int) -> str - Get the IP string. - - is_IPv4(ip: str) -> bool - Whether 'ip' is a IPv4 one. - - is_IP(ip: str) -> bool - Whether ip is a valid ip address. - - is_loopback_IP(ip: str) -> bool - Whether 'ip' is a loopback one. - - is_multicast_IP(ip: str) -> bool - Whether 'ip' is a multicast one. - - is_interface_local_multicast_IP(ip: str) -> bool - Whether 'ip' is a interface, local and multicast one. - - is_link_local_multicast_IP(ip: str) -> bool - Whether 'ip' is a link local and multicast one. - - is_link_local_unicast_IP(ip: str) -> bool - Whether 'ip' is a link local and unicast one. - - is_global_unicast_IP(ip: str) -> bool - Whether 'ip' is a global and unicast one. - - is_unspecified_IP(ip: str) -> bool - Whether 'ip' is a unspecified one. -- base64 - - encode(value: str, encoding: str = "utf-8") -> str - Encode the string `value` using the codec registered for encoding. - - decode(value: str, encoding: str = "utf-8") -> str - Decode the string `value` using the codec registered for encoding. -- crypto - - md5(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `MD5` and the codec registered for encoding. - - sha1(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA1` and the codec registered for encoding. - - sha224(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA224` and the codec registered for encoding. - - sha256(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA256` and the codec registered for encoding. - - sha384(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA384` and the codec registered for encoding. - - sha512(value: str, encoding: str = "utf-8") -> str - Encrypt the string `value` using `SHA512` and the codec registered for encoding. -- manifests - - `yaml_stream(values: [any], opts: {str:} = {sort_keys = False, ignore_private = True, ignore_none = False, sep = "---"}`. This function is used to serialize the KCL object list into YAML output with the `---` separator - -### The Built-in System Package - -KCL provides a list of built-in system modules, which are loaded automatically and can be directly used without providing any module name. For example, `print` is a widely used built-in module. - -The following is the full list of these built-in system modules: - -- print() - - The print function. -- multiplyof(a, b) - - Check if the modular result of a and b is 0 -- isunique(inval) - - Check if a list has duplicated elements -- len(inval) - Return the length of a value -- abs(x) - Return the absolute value of the argument. -- all(iterable) - Return True if bool(x) is True for all values x in the iterable. If the iterable is empty, return True. -- any(iterable) - Return True if bool(x) is True for any x in the iterable. If the iterable is empty, return False. -- bin(number) - Return the binary representation of an integer. -- hex(number) - Return the hexadecimal representation of an integer. -- oct(number) - Return the octal representation of an integer. -- ord(c) -> int - Return the Unicode code point for a one-character string. -- sorted(iterable) - Return a new list containing all items from the iterable in ascending order. A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order. -- range(start, end, step=1) - Return the range of a value with start, end and step parameter. -- min(iterable) - With a single iterable argument, return its smallest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the smallest argument. -- max(iterable) - With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument. -- sum(iterable, start) - Return the sum of a 'start' value (default: 0) plus an iterable of numbers. When the iterable is empty, return the start value. This function is intended specifically for use with numeric values and may reject non-numeric types. -- pow(x, y, z) - Equivalent to `x**y` (with two arguments) or `x**y % z` (with three arguments). Some types, such as ints, are able to use a more efficient algorithm when invoked using the three argument form. -- round(number, ndigits) - Round a number to a given precision in decimal digits. The return value is an integer if ndigits is omitted or None. Otherwise the return value has the same type as the number. ndigits may be negative. -- typeof(x: any, \*, full_name: bool = False) -> str - Return the type of the value 'x' at runtime. When the 'full_name' is 'True', return the full package type name such as `pkg.schema`. -### Plugin Modules +See 'union' in `expressions` spec for more details. -KCL compiler needs to provide the ability to dynamically expand and load plugin modules without modifying the compiler itself. KCL compiler needs to support flexible pluggable module extension mechanism, so that KCL users can use more abundant built-in function capabilities to simplify writing. +#### Override -KCL compiler needs to ensure the stability and safety of the expansion mechanism, without affecting the core of the compiler. +Pattern: `identifier = E` -Searching extended plugin module is performed after the standard system module. The standard system module has a higher priority in naming. If it exists a standard or built-in system module with the same name, the extended plugin module will be ignored. +The value of the expression `E` will override the element value. -Importing and using the extended plugin module should be consistent with the standard or built-in system module. +Examples: -### Replacing Standard System Packages +```python +a = A { + # override {c:4} to the element b, suppose b is a schema with an int type attribute c. + b = { + c: 4 + } +} +``` -Replacing standard system modules is not allowed. +Unlike union, the override operation will reassign the element with a brand new value. +For basic type value, `union` and `override` have equivalent effects. -## Examples +Note: -We show more module features through an example. +- Especially, we can "delete" its content by overriding the element to `Undefined`, such as `{ a = Undefined }`. -Suppose we have the following directories and files: +#### Insert +Pattern: `identifier += E` +Insert only works for list type `identifier`. + +List `E` will be inserted just after the specified index of the list `identifier`, and the following elements after the index will be automatically shifted. + +Examples: + +```python +a = A { + # insert {c:4} to the end position(just after index=1), suppose b is a list of schema with an int type attribute c. + b += { + c: 4 + } +} ``` - . - ├── mod1.k - ├── mod2.k - ├── pkg1 - │   ├── def1.k - │   ├── def2.k - │   └── def3init.k - └── pkg2 - ├── file2.k - └── subpkg3 - └── file3.k + +If no index is specified, the last index will be used. + +The type of 'E' must be compatible with the type of list. See `types` for more details. + +#### Index Signature + +Index signatures can be defined in the KCL schema, and it means that the key-value constraints of the index signature can be used to construct a dict with the schema type, or additional checks can be added to the relaxed schema attributes to enhance the KCL type and semantic checks. + +- Use the form `[{attr_alias}: {key_type}]: {value_type}` to define an index signature in the schema, and `{attr_alias}` can be omitted. + +```python +schema Map: + """ + Map is a relaxed schema with a key of str type and a value of str type + """ + [str]: str # `{attr_alias}` can be omitted. + +data = Map { + key1 = "value1" + key2 = "value2" +} ``` -From the structure we can see that `pkg1` and `pkg2` are two packages, `subpkg3` is a subpackage of `pkg2`, and `mod1.k` and `mod2.k` are regular modules. +- Mandatory all attributes of the schema key and value types -### Importing a Standard System Package +```python +schema Person: + name: str + age: int # error, conflicts with the index signature definition `[str]: str` + [str]: str # The values of all attributes of the schema can only be strings +``` -The following statement can import the standard system module `math` +- Mandatory all attribute key and value types are defined in the schema, which is equivalent to restricting all attribute types except the relaxed attributes. ```python -import math +schema Person: + name: str + age: int + [...str]: str # Except for the `name` and `age` attributes, the key type of all other attributes of the schema must be `str`, and the value type must also be `str`. ``` -This is the only way to import a standard system module. After importing a standard system module, functions, variables and schemas defined in it can be used. For example, the following statement uses the `log10` function -defined in `math` +- Define the index signature attribute alias and use it with the check block. + +```python +schema Data: + [dataName: str]: str + check: + dataName in ["Alice", "Bob", "John"] + +data = Data { + Alice = "10" + Bob = "12" + Jonn = "8" # error Jonn not in ["Alice", "Bob", "John"] +} +``` ```python -a = math.log10(100) # a is 2 after computation. +import regex + +schema DataMap: + [attr: str]: str + check: + regex.match(attr, r'^[-._a-zA-Z0-9]+$') + +data = DataMap { + key1 = "value1" + "foo.bar" = "value2" # check error +} ``` -### Importing a Regular Module +### Schema Context + +The schema definition space can be regarded as a separate function context. + +Init statement could be defined inside the schema, the syntax is the following: -In `mod1.k`, we can import `mod2` using one of the following syntaxes. +```bnf +statement: small_stmt NEWLINE | if_stmt +``` + +The following is an example: ```python -import mod2 +schema Person: + firstName: str = "John" + lastName: str + # fullName is generated by firstName and lastName in a separate init statement + fullName: str = firstName + ' ' + lastName + +JohnDoe = Person { + lastName = "Doe" +} ``` +The result is a **dict**: + ```python -import .mod2 +{ + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' +} ``` -The difference is that in the first syntax, the KCL compiler will first try to check if `mod2` matches any of the standard system modules' name. Since it does not match any standard system module's name, the statement will check the directory where `mod1.k` resists in, like what the second statement does. +If statement, expr statement and assert statement are supported as a schema init +statement. See more in statement spec. -Suppose in `mod2.k` there is a definition of a variable:: +- The attributes must be defined first, including inherited ones, and then used in the init statement. +- Statements in the schema context will be executed sequentially. +- The value of attributes referenced in the init statement will be evaluated at runtime. + See the **Configuration Definition** section for the assignment rules of non-referenced attributes. For example, `"fullName"` in Person is generated by `"firstName"` and `"lastName"` evaluated at runtime, in which firstName is 'John', and lastName is "Doe". + +The immutability of attributes in the schema context follows the same rules as the immutability of global variables: ```python -a = 100 +schema Person: + age: int = 1 # Immutable attribute + _name: str = "Alice" # Mutable attribute + + age = 10 # Error + _name = "Bob" # Ok +``` + +#### Arguments + +Schema context can also have arguments. The following is an example. + +```python +schema Person[separator]: + firstName: str = "John" + lastName: str + fullName: str = firstName + separator + lastName + +JohnDoe = Person('_') { + lastName = "Doe" +} +``` + +The example is similar to the previous one, except that the separator character used in +the `"fullName"` member is passed in as an argument. The way to perform a schema generation +when the schema has an initialization function with arguments is demonstrated in the code. + +### Check Block + +Optionally, a check block can be added to a schema definition to allow +additional checking to be performed. + +The syntax is the following: + +```bnf +check_block: "check" ":" NEWLINE _INDENT check_expr+ _DEDENT +check_expr: test (IF test)? [":" primary_expr] NEWLINE ``` -After importing `mod2`, we can access `a` in `mod1.k` using the following syntax +In terms of grammatical definition, a check block consists of a list of conditional expressions. The following is an example: ```python -b = mod2.a +schema employee(person): + bankCard: int + gender: str + + check: + len(str(bankCard)) == 16 + gender in ['male', 'female'], "The gender {} is unsupported".format(gender) ``` -### Importing a Package +The ability of KCL check expressions covers the abilities that can be defined by OpenAPI spec and is aligned with the ability of logical expressions. We consider further aligning the syntax with `CEL` spec. +Whether to support `lambda expressions` is still under discussion. + +Summary: + +- A check block consists of one or more logical **expressions**. +- When defining a configuration, the expressions in the check block are evaluated + in any order. If any of the expression is `False`, an error is reported. +- A custom error message can be provided after an expression. + +### Specifying Types + +Optionally, the type of any member of a schema can be specified. As previous examples have shown. + +A member can be of a basic type, such as a string (`str`), a floating-point number (`float`), a fixed-point number (`int`) or a boolean number (`bool`). + +A member can also be of a dictionary generated from another schema. In such a case, the name of the other schema is used as the type name. + +A member can also be a list or an ordinary dict: + +- A list with unspecified type of elements is `[]`. +- A list with elements of type `t` is `[t]`. Here `t` is another type. +- A dict with keys of type `kt` and values of type `vt` is `{kt:vt}`. +- `kt`, `vt` or both of them can be missing, like a list with unspecified type of elements. + +The followings are some more examples: -In `mod1.k`, we can import `pkg1` using one of the following syntaxes. +- A list of lists of strings: `[[str]]`. +- A dict of keys with the type string and unspecified value types: `{str:}`. + +A member can be a **union type** defined by `|`, such as `a | b`, which means the type of the member could be a or b. + +A union type can include types of `int`, `str`, `float`, `bool`, `list` and `dict` and support type nesting e.g. `{str:str|int}` and `[[int|str]|str|float]`, etc. + +Examples: ```python -import pkg1 +schema x: + p: int | str # p could be defined as a int or string ``` +### Immutability + +KCL pursues strict immutability of schema attributes. It's generally followed the rules: + +- For the attributes of the basic type, such as string, int and float, it's allowed to be reassigned + through the init statement in **schema context** or by the **configuration definition**. +- For the attributes of list, dict and schema type, it's allowed to be reassigned only by the init statement in **schema context**. The content of it is allowed to be operated in **schema context** or by the **configuration definition**. +- Any other attempt to reassign or modify schema attribute will report an error. + +#### Assign by Value + +When using a schema variable to assign the value to another variable, we can only get a deep copy of its value, not a pointer or reference. That is, modifying the assigned value will not change the assigned schema variable. + ```python -import .pkg1 +schema Person: + name: str + +person = { + name = "Alice" +} +personCopy = person # 'personCopy' is a deep copy of 'person' and modifying 'personCopy' will not affect 'person' ``` -The difference is that in the first syntax, the KCL compiler will first try to check if `pkg1` matches any of the standard system modules' name. Since it does not match any standard system module's name, the statement will check the directory where `mod1.k` resists in, like what the second statement does. +### Union Operator + +For list, dict and schema, we can union delta to existing data. For example: + +```python +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name = { + firstName = "John" + } -We can use similar statements to import `pkg2`. Note that importing `pkg2` will not import `subpkg3`. + # union a schema and a dict + name: Name { + lastName = "Doe" + } -The name of the package is the name of the imported module. +person = Person {} +``` -Suppose in `file2.k` that is inside `pkg2` there is a definition to variable `foo` +The result is a **dict**: ```python -foo = 100 +{ + 'person': { + 'name': { + 'firstName': 'Jhon', + 'lastName': 'Doe' + } + } +} ``` -This variable can be used in `mod1.k` after importing `pkg2` like the following +### Other Operators + +Except for `assignment` and `union assignment`, it's not support other operators on schema type data. +Report an error if trying to use other operators on schema type data. + +### Deprecated + +The schema attribute can be marked as deprecated once it's considered invalid. ```python -bar = pkg2.foo +schema Person: + @deprecated(version="1.1.0", reason="use fullName instead", strict=True) + name: str + ... # Omitted contents + +person = Person { + # report an error on configing a deprecated attribute + name = "name" +} ``` -### Importing a Subpackage +- Deprecated attributes cannot be configured under any circumstances. Report an error or warning once the attribute is assigned. +- Define the expired version of the attribute through **version**, and define the reason for the attribute expired through **reason**. +- When strict is true, the attribute assignment will cause an error, otherwise it will report a warning and ignore the attribute assignment. -To import `subpkg3` from `mod1.k`, one of the following statements can be used. +### Composition + +The composition is a common way to define complex structures. KCL provides simplified means for the configuration definition of combined structures. + +Assuming we have the following schemas, which is defined by a combination of multiple schemas. ```python -import pkg2.subpkg3 +schema Name: + firstName: str + lastName: str + +schema Person: + name: Name + age: int + +schema Group: + name: str + persons: [Person] ``` +To config a group: + ```python -import .pkg2.subpkg3 +group = Group { + name = "group" + persons = [{ + name = { + firstName = "John" + lastName = "Doe" + } + age = 24 + }] +} ``` -The behaviors of these statements are identical. +- Top-level schema name is required to config a schema. +- The schema of the attributes in the schema can be omitted. -The name of the subpackage is the name of the imported module. +Multi-level nested schemas will make the configuration verbose. KCL supports defining attributes in the schema through `selector expression`. The selector form is **x.y.z**, see the following example: -Suppose in `file3.k` that is inside `subpkg3` there is a definition to variable `foo` +```python +group = Group { + name = "group" + persons = [{ + name.firstName = "John" + name.lastName = "Doe" + age = 24 + }] +} +``` + +- Selector can be used to represent attribute in a schema + +### Inheritance + +Inheritance is an effective means to define a hierarchical structure definition, and KCL supports limited **single inheritance** of the schema. ```python -foo = 100 +schema Person: + firstName: str + lastName: str + +# schema Scholar inherits schema Person +schema Scholar(Person): + fullName: str = firstName + '_' + lastName + subject: str + +JohnDoe = Scholar { + firstName = "John", + lastName = "Doe", + subject = "CS" +} ``` -This variable can be used in `mod1.k` after importing `subpkg3` like the following +The result is a **dict**: ```python -bar = subpkg3.foo +{ + 'JohnDoe': { + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' + 'subject': 'CS' + } +} ``` -### Relative Importing +Each schema can be treated as a separated function context. Statements, including attribute statements and init statements, in the context of schemas will be evaluated from base schema to subschema according to the inheritance order. Each schema context is evaluated only once sequentially. The same goes for expressions in the check block. In the example, firstName and lastName are configured in the context of Person schema, and fullName is formed by splicing firstName and lastName in the context of Scholar schema. + +The default value can be modified in each schema. Value defined in **Configuration Definition** has a higher priority than the default value. Attributes with default values in any schema context ​​will eventually be unioned by configuration data. References to attributes in the schema context statements will use the value with unioned configuration data on evaluating at runtime. For example: -Relative importing is useful when there is code trying to import modules that does not exist recursively inside the current directory. +```python +schema a: + x = 1 + y = x * 2 -For example, the following statements, if written in `file3.k`, can be used to import `pkg2`, `pkg1` and `mod2` respectively. +schema b(a): + x = 2 + +v = a { + x = 3 +} + +``` + +The result is a **dict**: ```python -import ...pkg2 # Go two levels up then import pkg2 -import ...pkg1 # Go two levels up then import pkg1 -import ...mod2 # Go two levels up then import mod2 +{ + 'v': { + 'x': 3 + 'y': 6 + } +} ``` -### Importing from a Root Path +Notes: + +- Report an error if inheriting more than one base schema. +- The type of the base schema attribute cannot be modified in the subschema. +- Report an error if inheriting a **mixin**. +- Report an error when a circular dependency occurs. -Suppose we have a `kcl.mod` file in the directory to mark it as a root path, then we have the following files: +Limitations: +Since inheritance will derive some complex demands, we are cautious about these complex demands. There are still some restrictions on inheritance, and it's still under discussion. + +- KCL provides limited and deterministic polymorphism support, more complex and flexible polymorphism support, such as **self**, **super** keywords, are temporarily not included in the schema definition. +- Currently, KCL only supports the polymorphism of the inherited attributes of the schema, and does not support the polymorphism of the expressions in the check block. +- For the case of multiple levels of schema inheritance, the schema arguments can only be passed to the last level of sub-schema. + +### Mixin + +In addition to **composition** and **inheritance**, KCL supports declarative reuse of schema code through the **mixin** mechanism. To use a mixin, we only need to declare the **mixin** in the schema definition. + +The **mixin** syntax is the following: + +```bnf +//////////// mixin_stmt //////////// +mixin_stmt: "mixin" "[" [mixins | multiline_mixins] "]" "\n" +multiline_mixins: "\n" _INDENT mixins "\n" _DEDENT +mixins: operand_name ("," ("\n" mixins | operand_name))* ``` - . - |── kcl.mod - ├── mod1.k - ├── mod2.k - ├── pkg1 - │   ├── def1.k - │   ├── def2.k - │   └── def3init.k - └── pkg2 - ├── file2.k - └── subpkg3 - └── file3.k + +Here is a simple example: + +```python +schema Person: + mixin [FullNameMixin] + firstName: str = "default" + lastName: str + +schema FullNameMixin: + fullName: str = "{} {}".format(firstName, lastName) + +JohnDoe = Person { + firstName = "John" + lastName = "Doe" +} ``` -In `pkg1` `def1.k`, we can import `pkg2.subpkg3` `file3` using the following syntaxes. +The result is a **dict**: ```python -import pkg2.subpkg3.file3 +{ + 'JohnDoe': { + 'firstName': 'John' + 'lastName': 'Doe' + 'fullName': 'John Doe' + } +} ``` -Importing from the root path is very convenient when the code is trying to import modules from a directory needs to look up multiple directories above this directory. At also, it is helpful to organize a large number of files in a root directory. +Multiple mixins can be added to a single schema, and mixins context will be evaluated after the host schema context at runtime. In the inheritance scenario, the mixin context can be regarded as a part of the host schema context, and the overall evaluation of schema context order is not affected. + +Notes: + +- The name of **mixin** schema must end with 'Mixin', otherwise an error will be reported. +- The attributes referenced in the **mixin** must be defined in the **mixin** itself or host schema, otherwise an error will be reported. + +### Protocol + +In addition to schema, an additional type definition method `protocol` is provided in KCL, and its properties are as follows: -### Importing a Module Inside a Package +- In a protocol, only attributes and their types can be defined, complex logic and check expressions cannot be written, and mixins cannot be used. +- A protocol can only inherit or refer to other protocols, but cannot inherit or refer to other schemas. -Note that `subpkg3` is only implemented with one file `file3.k`. The file can be regarded as a regular module and imported directly. +We can use **protocol** to add an optional host type to the dynamically inserted **mixin**. -In `mod1.k`, the importing statement would be:: +The **mixin** can define its host type through the `for` keyword, and internally it will query the type corresponding to the attribute from the host type. ```python -import pkg2.subpkg3.file3 +protocol DataProtocol: # A mixin host type + data: str + +mixin DataMixin for DataProtocol: # Using the `for` keyword to define a mixin host type + x: int = data # The type of `data` is `str`, which is from `data` of `DataProtocol` +``` + +In `DataMixin`, the `data` attribute is obtained according to the `DataProtocol` host type as `str` type, and then a type error will occur when the value is assigned to `x` of type `int`: + +```python +protocol DataProtocol: + data: str + +mixin DataMixin for DataProtocol: + x: int = data # Error: expect int, got str + x: str = data # Error: can't change schema field type of 'x' from int to str ``` -Different from importing `subpkg3`, now the name of the module is `file3`. We can access the variable `foo` defined in this module with the following -statement +Please note that the host type **protocol** can only be used for **mixin** definitions (the suffix name is `Mixin`), otherwise an error will be reported. ```python -bar = file3.foo +protocol DataProtocol: + data: str + +schema Data for DataProtocol: # Error: only schema mixin can inherit from protocol + x: str = data +``` + +### Schema Context Evaluation + +The schema definition is composed of attribute statements, configuration data, init statements, mixins, and checks. In a separate schema context, the evaluation top-down order is as follows: + +``` +|------------------------------------------| +| attribute defaulting | +|------------------------------------------| +| configuration union | +|------------------------------------------| +| attribute templating | +|------------------------------------------| +| statements in declaration order | +|------------------------------------------| +| mixins in declaration order | +|------------------------------------------| +| check expressions in any order | +|------------------------------------------| +``` + +In the case of schema inheritance, each schema context is evaluated from the base schema in the order of inheritance, and each context is evaluated only once. +Suppose there are schemas a, b, and c, where c inherits b and b inherits a. Schema contexts will be evaluated in top-down order as: + +``` +|-----------------| +| schema a | +|-----------------| +| schema b | +|-----------------| +| schema c | +|-----------------| ``` -### Precedence of Importing +### Members -When an import statement specifies a package to import, the virtual machine first looks for a directory named according to the import statement in the file system. +Built-in function and members of schema -If such a directory is not found, the virtual machine looks for a single file module. +- instances() + Return the list of existing instances of a schema. -For example, when the statement `import a.b.c` appears, the virtual machine first looks for the directory `a/b/c` from the directory of the current file. If `a/b/c` is not found, the virtual machine looks for a file named `a/b/c.k`. If the file is also absent, an error is reported. +### Irrelevant Order Calculation -### Package Implemented with Multiple Files +The irrelevant order calculation in the schema indicates the reference relationship between the internal attributes of the schema. For example, when we declare an expression of the form `a = b + 1`, the calculation of the value of `a` depends on the calculation of the value of `b`. When the compiler calculate the value of `a` and the value of `a` depends on the value of `b`, the compiler will choose to first calculate the value of `b`, and then calculate the value of a according to the expression `a = b + 1`, which is slightly different from the calculation method of traditional procedural language the difference. -Package `pkg1` is implemented with multiple KCL files. +Since the calculation of values in the schema is based on dependencies, just like a directed acyclic graph traverses each node in the graph according to the order of topological sorting, the order of declaration of attributes in the schema is not so important, so the feature is called the irrelevant order calculation. -Multiple files can be used to define variables, schemas and functions, and they can access names defined in other files of this package. +Please note that there can be no circular references between different schema attribute values. -For example, suppose `def1.k` defines a variable `foo`, `def2.k` defines `bar`, and `def3init.k` defines a variable `baz`, when `pkg1` is imported by `mod1.k`, all these variable can be used +We can see this feature through the following examples. ```python -import pkg1 -a = pkg1.foo + pkg1.bar + pkg1.baz +schema Person: + name?: str + age: int = _age + + _age = 10 + + if name == "Son": + _age = 18 + +schema Son(Person): + name: str = "Son" + +person = Person {} +son = Son {} +``` + +The output is + +```yaml +person: + name: null + age: 10 +son: + name: Son + age: 18 ``` -Inside a module, names defined in a file can be accessed in another file without further importing. For example, suppose `bar` in `def2.k` would invoke `foo` defined in `def1.k`, it can directly use `foo` like the following +Besides, we can achieve KCL polymorphism such as ```python -bar = foo + 1 +schema Person: + name?: str + _age: int = _age + + _age = 10 + if name == "Son": + _age = 18 + elif name == "SonConf": + _age = 24 + +schema Son(Person): + name: str = "Son" + +person = Person() {} +son = Son() { + name = "SonConf" +} ``` + +The output is + +```yaml +person: + name: null + age: 10 +son: + name: SonConf + age: 24 +``` + +More examples: + +```python +schema Fib: + n1: int = n - 1 + n2: int = n1 - 1 + n: int + value: int = _value + + if n <= 2: + _value = 1 + else: + _value = (Fib {n = n1}).value + (Fib {n = n2}).value + +fib8 = (Fib {n = 8}).value +``` + +The output is + +```yaml +fib8: 21 +``` + +As in the above examples, we can see that in the schema, we only need to simply specify the dependency between attributes, and the compiler will automatically calculate the value based on the dependency, which can help us save a lot of boilerplate code and reduce configuration difficulty of writing. diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/schema.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/schema.md index c3d09f65..74784d0c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/schema.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/schema.md @@ -15,7 +15,7 @@ A schema is a language element to define a type of configuration data. To define a schema, the syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | schema_index_signature | statement)* [check_block] _DEDENT ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/statements.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/statements.md index b09488b9..179410b5 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/statements.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.7.0/reference/lang/spec/statements.md @@ -30,8 +30,8 @@ A small statement is comprised of a single logical line. Multiple statements in Generally, assign_stmt is divided into assignment and augmented assignment. The syntax is the following: ```bnf -assign_stmt: target_primary ("=" target_primary)* "=" test | target_primary augassign test -augassign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "or" | "and" +assign_stmt: target_primary (":" type) ("=" target_primary)* "=" test | target_primary aug_assign test +aug_assign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" target_primary: identifier | target_primary DOT identifier ``` @@ -61,7 +61,7 @@ _x -= 1 _filename += ".k" ``` -There is no concept of in-place modification in KCL. The `augassign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. +There is no concept of in-place modification in KCL. The `aug_assign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. In particular, in KCL, the `|=` symbol represents the **union** operation, which is defined as follows: @@ -117,7 +117,7 @@ Assert statements are a convenient way to insert debugging assertions into KCL c The syntax is the following: ``` -assert_stmt: ASSERT test ("," test)? +assert_stmt: ASSERT test ("if" test)? ("," test)? ``` The conditional expression in assert will be evaluated and get a boolean. Report an error if returning a `False`. @@ -178,7 +178,7 @@ else: Schema statements are used to define a type of configuration data. The syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | statement)* [check_block] _DEDENT ``` diff --git a/versioned_docs/version-0.7.0/reference/lang/spec/error.md b/versioned_docs/version-0.7.0/reference/lang/spec/error.md index 2a58b553..65420a2f 100644 --- a/versioned_docs/version-0.7.0/reference/lang/spec/error.md +++ b/versioned_docs/version-0.7.0/reference/lang/spec/error.md @@ -17,7 +17,7 @@ schema is violated. The syntax of the `assert` statement is the following. ```bnf -assert_stmt: 'assert' test ['if' test] [',' test] +assert_stmt: ASSERT simple_expr (IF simple_expr)? (COMMA test)? ``` In the basic form, an `assert` statement evaluates an expression. If the diff --git a/versioned_docs/version-0.7.0/reference/lang/spec/kcl-spec.md b/versioned_docs/version-0.7.0/reference/lang/spec/kcl-spec.md index e7cf8da3..798ba840 100644 --- a/versioned_docs/version-0.7.0/reference/lang/spec/kcl-spec.md +++ b/versioned_docs/version-0.7.0/reference/lang/spec/kcl-spec.md @@ -119,7 +119,7 @@ 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] +attribute_stmt: [decorators] (identifier | STRING) [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 @@ -135,8 +135,9 @@ decorator_expr: identifier [call_suffix] //////////// type //////////// type: type_element (OR type_element)* -type_element: schema_type | basic_type | compound_type | literal_type +type_element: schema_type | function_type | basic_type | compound_type | literal_type schema_type: identifier +function_type: LEFT_PARENTHESES [type_element (COMMA type_element)*] RIGHT_PARENTHESES [RIGHT_ARROW type_element] 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 @@ -191,11 +192,11 @@ 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_expr: LEFT_BRACKETS [list_items | NEWLINE [list_items]] 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 +list_comp: LEFT_BRACKETS (list_item comp_clause+ | NEWLINE list_item comp_clause) RIGHT_BRACKETS +dict_comp: LEFT_BRACE (entry comp_clause+ | NEWLINE entry comp_clause+) 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)? @@ -207,7 +208,7 @@ 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_expr: LEFT_BRACE [config_entries | NEWLINE [config_entries]] 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 @@ -220,8 +221,8 @@ 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 +// Tokens ASSIGN: "=" COLON: ":" SEMI_COLON: ";" @@ -332,10 +333,10 @@ STRING: /r?("(?!"").*?(? None Serialize a KCL object `data` to a JSON formatted str and write it into the file `filename`. + - validate(data: any) -> bool + Validate whether the given string is a valid JSON. - yaml - encode(data: any, sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str Serialize a KCL object `data` to a YAML formatted str. + - encode_all(data: [any], sort_keys: bool = False, ignore_private: bool = False, ignore_none: bool = False) -> str + Serialize a sequence of KCL objects into a YAML stream str. - decode(value: str) -> any Deserialize `value` (a string instance containing a YAML document) to a KCL object. + - decode_all(value: str) -> [any] - dump_to_file(data: any, filename: str, ignore_private: bool = False, ignore_none: bool = False) -> None Serialize a KCL object `data` to a YAML formatted str and write it into the file `filename`. + Parse all YAML documents in a stream and produce corresponding KCL objects. + - validate(value: str) -> str + Validate whether the given string is a valid YAML or YAML stream document. - net - split_host_port(ip_end_point: str) -> List[str] Split the 'host' and 'port' from the ip end point. diff --git a/versioned_docs/version-0.7.0/reference/lang/spec/schema.md b/versioned_docs/version-0.7.0/reference/lang/spec/schema.md index c3d09f65..74784d0c 100644 --- a/versioned_docs/version-0.7.0/reference/lang/spec/schema.md +++ b/versioned_docs/version-0.7.0/reference/lang/spec/schema.md @@ -15,7 +15,7 @@ A schema is a language element to define a type of configuration data. To define a schema, the syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | schema_index_signature | statement)* [check_block] _DEDENT ``` diff --git a/versioned_docs/version-0.7.0/reference/lang/spec/statements.md b/versioned_docs/version-0.7.0/reference/lang/spec/statements.md index b09488b9..179410b5 100644 --- a/versioned_docs/version-0.7.0/reference/lang/spec/statements.md +++ b/versioned_docs/version-0.7.0/reference/lang/spec/statements.md @@ -30,8 +30,8 @@ A small statement is comprised of a single logical line. Multiple statements in Generally, assign_stmt is divided into assignment and augmented assignment. The syntax is the following: ```bnf -assign_stmt: target_primary ("=" target_primary)* "=" test | target_primary augassign test -augassign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "or" | "and" +assign_stmt: target_primary (":" type) ("=" target_primary)* "=" test | target_primary aug_assign test +aug_assign: "+=" | "-=" | "*=" | "**=" | "/=" | "//=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" target_primary: identifier | target_primary DOT identifier ``` @@ -61,7 +61,7 @@ _x -= 1 _filename += ".k" ``` -There is no concept of in-place modification in KCL. The `augassign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. +There is no concept of in-place modification in KCL. The `aug_assign` statement will modify a copy of the **target_primary** and assign the copy to **target_primary**. In particular, in KCL, the `|=` symbol represents the **union** operation, which is defined as follows: @@ -117,7 +117,7 @@ Assert statements are a convenient way to insert debugging assertions into KCL c The syntax is the following: ``` -assert_stmt: ASSERT test ("," test)? +assert_stmt: ASSERT test ("if" test)? ("," test)? ``` The conditional expression in assert will be evaluated and get a boolean. Report an error if returning a `False`. @@ -178,7 +178,7 @@ else: Schema statements are used to define a type of configuration data. The syntax is the following: ```bnf -schema_stmt: [decorators] "schema" ["relaxed"] identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] +schema_stmt: [decorators] "schema" identifier ["[" [arguments] "]"] ["(" operand_name ")"] ":" NEWLINE [schema_body] schema_body: _INDENT (string NEWLINE)* [mixin_stmt] (schema_attribute_stmt | statement)* [check_block] _DEDENT ```