forked from extern/nushell
Feature/def signature with comments (#2905)
* Put parse_definition related funcs into own module * Add failing lexer test * Implement Parsing of definition signature This commit applied changes how the signature of a function is parsed. Before there was a little bit of "quick-and-dirty" string-matching/parsing involved. Now, a signature is a little bit more properly parsed. The grammar of a definition signature understood by these parsing-functions is as follows: `[ (parameter | flag | <eol>)* ]` where parameter is: `name (<:> type)? (<,> | <eol> | (#Comment <eol>))?` flag is: `--name (-shortform)? (<:> type)? (<,> | <eol> | (#Comment <eol>))?` (Note: After the last item no <,> has to come.) Note: It is now possible to pass comments to flags and parameters Example: [ d:int # The required d parameter --x (-x):string # The all powerful x flag --y (-y):int # The accompanying y flag ] (Sadly there seems to be a bug (Or is this expected behaviour?) in the lexer, because of which `--x(-x)` would be treated as one baseline token and is therefore not correctly recognized as 2. For now a space has to be inserted) During the implementation of the module, 2 question arose: Should flag/parameter names be allowed to be type names? Example case: ```shell def f [ string ] { echo $string } ``` Currently an error is thrown * Fix clippy lints * Remove wrong comment * Add spacing * Add Cargo.lock
This commit is contained in:
parent
481c6d4511
commit
3e6e3a207c
157
Cargo.lock
generated
157
Cargo.lock
generated
@ -129,8 +129,8 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423"
|
checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -255,8 +255,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
|
checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -301,8 +301,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
|
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1064,9 +1064,9 @@ dependencies = [
|
|||||||
"matches",
|
"matches",
|
||||||
"phf",
|
"phf",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"smallvec 1.6.0",
|
"smallvec 1.6.0",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1075,8 +1075,8 @@ version = "0.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
|
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1200,8 +1200,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
|
checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_is_enum_variant"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0ac8859845146979953797f03cc5b282fb4396891807cdb3d04929a88418197"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"quote 0.3.15",
|
||||||
|
"syn 0.11.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1211,8 +1222,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
|
checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1496,8 +1507,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1754,8 +1765,8 @@ checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1904,8 +1915,8 @@ checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1997,6 +2008,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heim"
|
name = "heim"
|
||||||
version = "0.1.0-rc.1"
|
version = "0.1.0-rc.1"
|
||||||
@ -2216,8 +2236,8 @@ dependencies = [
|
|||||||
"mac",
|
"mac",
|
||||||
"markup5ever",
|
"markup5ever",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3263,11 +3283,14 @@ dependencies = [
|
|||||||
"bigdecimal",
|
"bigdecimal",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"derive-new",
|
"derive-new",
|
||||||
|
"derive_is_enum_variant",
|
||||||
|
"dunce",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-source",
|
"nu-source",
|
||||||
|
"nu-test-support",
|
||||||
"num-bigint 0.3.1",
|
"num-bigint 0.3.1",
|
||||||
"num-traits 0.2.14",
|
"num-traits 0.2.14",
|
||||||
"serde 1.0.118",
|
"serde 1.0.118",
|
||||||
@ -4035,8 +4058,8 @@ dependencies = [
|
|||||||
"phf_shared",
|
"phf_shared",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4073,8 +4096,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4084,8 +4107,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b7bcc46b8f73443d15bc1c5fecbb315718491fa9187fa483f0e359323cde8b3a"
|
checksum = "b7bcc46b8f73443d15bc1c5fecbb315718491fa9187fa483f0e359323cde8b3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4214,8 +4237,8 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-error-attr",
|
"proc-macro-error-attr",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4226,7 +4249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4248,7 +4271,7 @@ version = "1.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4329,10 +4352,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f"
|
checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "0.3.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@ -4675,9 +4704,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ebc6a6c2785d73d8f0157d10a40223bbf0210f18aecb261d39b96802f9ccc69d"
|
checksum = "ebc6a6c2785d73d8f0157d10a40223bbf0210f18aecb261d39b96802f9ccc69d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4966,8 +4995,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5250,7 +5279,7 @@ dependencies = [
|
|||||||
"phf_generator",
|
"phf_generator",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5311,6 +5340,17 @@ dependencies = [
|
|||||||
"sxd-document",
|
"sxd-document",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "0.11.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||||
|
dependencies = [
|
||||||
|
"quote 0.3.15",
|
||||||
|
"synom",
|
||||||
|
"unicode-xid 0.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.57"
|
version = "1.0.57"
|
||||||
@ -5318,8 +5358,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
|
checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"unicode-xid",
|
"unicode-xid 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synom"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5329,9 +5378,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"unicode-xid",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5459,8 +5508,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5956,6 +6005,12 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -6155,8 +6210,8 @@ dependencies = [
|
|||||||
"lazy_static 1.4.0",
|
"lazy_static 1.4.0",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6194,7 +6249,7 @@ version = "0.2.69"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
|
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6205,8 +6260,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote 1.0.8",
|
||||||
"syn",
|
"syn 1.0.57",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -18,10 +18,13 @@ num-bigint = {version = "0.3.1", features = ["serde"]}
|
|||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
serde = "1.0.118"
|
serde = "1.0.118"
|
||||||
shellexpand = "2.1.0"
|
shellexpand = "2.1.0"
|
||||||
|
derive_is_enum_variant = "0.1.1"
|
||||||
|
|
||||||
nu-errors = {version = "0.25.1", path = "../nu-errors"}
|
nu-errors = {version = "0.25.1", path = "../nu-errors"}
|
||||||
nu-protocol = {version = "0.25.1", path = "../nu-protocol"}
|
nu-protocol = {version = "0.25.1", path = "../nu-protocol"}
|
||||||
nu-source = {version = "0.25.1", path = "../nu-source"}
|
nu-source = {version = "0.25.1", path = "../nu-source"}
|
||||||
|
nu-test-support = {version = "0.25.1", path = "../nu-test-support"}
|
||||||
|
dunce = "1.0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
stable = []
|
stable = []
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::iter::Peekable;
|
|
||||||
use std::str::CharIndices;
|
use std::str::CharIndices;
|
||||||
|
use std::{fmt, iter::Peekable};
|
||||||
|
|
||||||
use nu_source::{Span, Spanned, SpannedItem};
|
use nu_source::{Span, Spanned, SpannedItem};
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, is_enum_variant)]
|
||||||
pub enum TokenContents {
|
pub enum TokenContents {
|
||||||
/// A baseline token is an atomic chunk of source code. This means that the
|
/// A baseline token is an atomic chunk of source code. This means that the
|
||||||
/// token contains the entirety of string literals, as well as the entirety
|
/// token contains the entirety of string literals, as well as the entirety
|
||||||
@ -34,6 +34,28 @@ pub enum TokenContents {
|
|||||||
EOL,
|
EOL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TokenContents {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
TokenContents::Baseline(base) => {
|
||||||
|
write!(f, "{}", base)
|
||||||
|
}
|
||||||
|
TokenContents::Comment(comm) => {
|
||||||
|
write!(f, "#{}", comm)
|
||||||
|
}
|
||||||
|
TokenContents::Pipe => {
|
||||||
|
write!(f, "|")
|
||||||
|
}
|
||||||
|
TokenContents::Semicolon => {
|
||||||
|
write!(f, ";")
|
||||||
|
}
|
||||||
|
TokenContents::EOL => {
|
||||||
|
write!(f, "\\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A `LiteCommand` is a list of words that will get meaning when processed by
|
/// A `LiteCommand` is a list of words that will get meaning when processed by
|
||||||
/// the parser.
|
/// the parser.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -657,6 +679,19 @@ mod tests {
|
|||||||
mod bare {
|
mod bare {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[ignore = "result is Token::baseline(\"--flag(-f)\")"]
|
||||||
|
#[test]
|
||||||
|
fn lex_flag() {
|
||||||
|
let input = "--flag(-f)";
|
||||||
|
|
||||||
|
let (result, err) = lex(input, 0);
|
||||||
|
|
||||||
|
assert_eq!("", format!("{:?}", result));
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_eq!(result[0].span, span(0, 6));
|
||||||
|
assert_eq!(result[1].span, span(7, 9));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_1() {
|
fn simple_1() {
|
||||||
let input = "foo bar baz";
|
let input = "foo bar baz";
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate derive_is_enum_variant;
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod lex;
|
mod lex;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
@ -5,19 +5,29 @@ use log::trace;
|
|||||||
use nu_errors::{ArgumentError, ParseError};
|
use nu_errors::{ArgumentError, ParseError};
|
||||||
use nu_protocol::hir::{
|
use nu_protocol::hir::{
|
||||||
self, Binary, Block, ClassifiedCommand, Expression, ExternalRedirection, Flag, FlagKind, Group,
|
self, Binary, Block, ClassifiedCommand, Expression, ExternalRedirection, Flag, FlagKind, Group,
|
||||||
InternalCommand, Literal, Member, NamedArguments, Operator, Pipeline, RangeOperator,
|
InternalCommand, Member, NamedArguments, Operator, Pipeline, RangeOperator, SpannedExpression,
|
||||||
SpannedExpression, Unit,
|
Unit,
|
||||||
};
|
};
|
||||||
use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPathMember};
|
use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPathMember};
|
||||||
use nu_source::{Span, Spanned, SpannedItem};
|
use nu_source::{Span, Spanned, SpannedItem};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
|
||||||
//use crate::errors::{ParseError, ParseResult};
|
|
||||||
use crate::lex::{block, lex, LiteBlock, LiteCommand, LitePipeline};
|
use crate::lex::{block, lex, LiteBlock, LiteCommand, LitePipeline};
|
||||||
use crate::path::expand_path;
|
use crate::path::expand_path;
|
||||||
use crate::scope::ParserScope;
|
use crate::scope::ParserScope;
|
||||||
use bigdecimal::BigDecimal;
|
use bigdecimal::BigDecimal;
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
def::{parse_definition, parse_definition_prototype},
|
||||||
|
util::trim_quotes,
|
||||||
|
util::verify_and_strip,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod def;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
pub use self::util::garbage;
|
||||||
|
|
||||||
/// Parses a simple column path, one without a variable (implied or explicit) at the head
|
/// Parses a simple column path, one without a variable (implied or explicit) at the head
|
||||||
pub fn parse_simple_column_path(
|
pub fn parse_simple_column_path(
|
||||||
lite_arg: &Spanned<String>,
|
lite_arg: &Spanned<String>,
|
||||||
@ -198,17 +208,6 @@ pub fn parse_full_column_path(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_quotes(input: &str) -> String {
|
|
||||||
let mut chars = input.chars();
|
|
||||||
|
|
||||||
match (chars.next(), chars.next_back()) {
|
|
||||||
(Some('\''), Some('\'')) => chars.collect(),
|
|
||||||
(Some('"'), Some('"')) => chars.collect(),
|
|
||||||
(Some('`'), Some('`')) => chars.collect(),
|
|
||||||
_ => input.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a numeric range
|
/// Parse a numeric range
|
||||||
fn parse_range(
|
fn parse_range(
|
||||||
lite_arg: &Spanned<String>,
|
lite_arg: &Spanned<String>,
|
||||||
@ -674,28 +673,6 @@ fn parse_list(
|
|||||||
(output, error)
|
(output, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_and_strip(
|
|
||||||
contents: &Spanned<String>,
|
|
||||||
left: char,
|
|
||||||
right: char,
|
|
||||||
) -> (String, Option<ParseError>) {
|
|
||||||
let mut chars = contents.item.chars();
|
|
||||||
|
|
||||||
match (chars.next(), chars.next_back()) {
|
|
||||||
(Some(l), Some(r)) if l == left && r == right => {
|
|
||||||
let output: String = chars.collect();
|
|
||||||
(output, None)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
String::new(),
|
|
||||||
Some(ParseError::mismatch(
|
|
||||||
format!("value in {} {}", left, right),
|
|
||||||
contents.clone(),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_table(
|
fn parse_table(
|
||||||
lite_block: &LiteBlock,
|
lite_block: &LiteBlock,
|
||||||
scope: &dyn ParserScope,
|
scope: &dyn ParserScope,
|
||||||
@ -2034,200 +2011,6 @@ fn parse_alias(call: &LiteCommand, scope: &dyn ParserScope) -> Option<ParseError
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_signature(
|
|
||||||
name: &str,
|
|
||||||
s: &Spanned<String>,
|
|
||||||
scope: &dyn ParserScope,
|
|
||||||
) -> (Signature, Option<ParseError>) {
|
|
||||||
let mut err = None;
|
|
||||||
|
|
||||||
let (preparsed_params, error) = parse_arg(SyntaxShape::Table, scope, s);
|
|
||||||
if err.is_none() {
|
|
||||||
err = error;
|
|
||||||
}
|
|
||||||
let mut signature = Signature::new(name);
|
|
||||||
|
|
||||||
if let SpannedExpression {
|
|
||||||
expr: Expression::List(preparsed_params),
|
|
||||||
..
|
|
||||||
} = preparsed_params
|
|
||||||
{
|
|
||||||
for preparsed_param in preparsed_params.iter() {
|
|
||||||
match &preparsed_param.expr {
|
|
||||||
Expression::Literal(Literal::String(st)) => {
|
|
||||||
let parts: Vec<_> = st.split(':').collect();
|
|
||||||
if parts.len() == 1 {
|
|
||||||
if parts[0].starts_with("--") {
|
|
||||||
// Flag
|
|
||||||
let flagname = parts[0][2..].to_string();
|
|
||||||
signature
|
|
||||||
.named
|
|
||||||
.insert(flagname, (NamedType::Switch(None), String::new()));
|
|
||||||
} else {
|
|
||||||
// Positional
|
|
||||||
signature.positional.push((
|
|
||||||
PositionalType::Mandatory(parts[0].to_string(), SyntaxShape::Any),
|
|
||||||
String::new(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else if parts.len() == 2 {
|
|
||||||
if parts[0].starts_with("--") {
|
|
||||||
// Flag
|
|
||||||
let flagname = parts[0][2..].to_string();
|
|
||||||
let shape = match parts[1] {
|
|
||||||
"int" => SyntaxShape::Int,
|
|
||||||
"string" => SyntaxShape::String,
|
|
||||||
"path" => SyntaxShape::FilePath,
|
|
||||||
"table" => SyntaxShape::Table,
|
|
||||||
"unit" => SyntaxShape::Unit,
|
|
||||||
"number" => SyntaxShape::Number,
|
|
||||||
"pattern" => SyntaxShape::GlobPattern,
|
|
||||||
"range" => SyntaxShape::Range,
|
|
||||||
"block" => SyntaxShape::Block,
|
|
||||||
"any" => SyntaxShape::Any,
|
|
||||||
_ => {
|
|
||||||
if err.is_none() {
|
|
||||||
err = Some(ParseError::mismatch(
|
|
||||||
"params with known types",
|
|
||||||
s.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
SyntaxShape::Any
|
|
||||||
}
|
|
||||||
};
|
|
||||||
signature.named.insert(
|
|
||||||
flagname,
|
|
||||||
(NamedType::Optional(None, shape), String::new()),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Positional
|
|
||||||
let name = parts[0].to_string();
|
|
||||||
let shape = match parts[1] {
|
|
||||||
"int" => SyntaxShape::Int,
|
|
||||||
"string" => SyntaxShape::String,
|
|
||||||
"path" => SyntaxShape::FilePath,
|
|
||||||
"table" => SyntaxShape::Table,
|
|
||||||
"unit" => SyntaxShape::Unit,
|
|
||||||
"number" => SyntaxShape::Number,
|
|
||||||
"pattern" => SyntaxShape::GlobPattern,
|
|
||||||
"range" => SyntaxShape::Range,
|
|
||||||
"block" => SyntaxShape::Block,
|
|
||||||
"any" => SyntaxShape::Any,
|
|
||||||
_ => {
|
|
||||||
if err.is_none() {
|
|
||||||
err = Some(ParseError::mismatch(
|
|
||||||
"params with known types",
|
|
||||||
s.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
SyntaxShape::Any
|
|
||||||
}
|
|
||||||
};
|
|
||||||
signature
|
|
||||||
.positional
|
|
||||||
.push((PositionalType::Mandatory(name, shape), String::new()));
|
|
||||||
}
|
|
||||||
} else if err.is_none() {
|
|
||||||
err = Some(ParseError::mismatch("param with type", s.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if err.is_none() {
|
|
||||||
err = Some(ParseError::mismatch("parameter", s.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(signature, err)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
signature,
|
|
||||||
Some(ParseError::mismatch("parameters", s.clone())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn parse_definition(call: &LiteCommand, scope: &dyn ParserScope) -> Option<ParseError> {
|
|
||||||
// A this point, we've already handled the prototype and put it into scope
|
|
||||||
// So our main goal here is to parse the block now that the names and
|
|
||||||
// prototypes of adjacent commands are also available
|
|
||||||
|
|
||||||
if call.parts.len() == 4 {
|
|
||||||
if call.parts.len() != 4 {
|
|
||||||
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if call.parts[0].item != "def" {
|
|
||||||
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = trim_quotes(&call.parts[1].item);
|
|
||||||
let (mut signature, err) = parse_signature(&name, &call.parts[2], scope);
|
|
||||||
|
|
||||||
//Add commands comments to signature usage
|
|
||||||
signature.usage = call.comments_joined();
|
|
||||||
|
|
||||||
if err.is_some() {
|
|
||||||
return err;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut chars = call.parts[3].chars();
|
|
||||||
match (chars.next(), chars.next_back()) {
|
|
||||||
(Some('{'), Some('}')) => {
|
|
||||||
// We have a literal block
|
|
||||||
let string: String = chars.collect();
|
|
||||||
|
|
||||||
scope.enter_scope();
|
|
||||||
|
|
||||||
let (tokens, err) = lex(&string, call.parts[3].span.start() + 1);
|
|
||||||
if err.is_some() {
|
|
||||||
return err;
|
|
||||||
};
|
|
||||||
let (lite_block, err) = block(tokens);
|
|
||||||
if err.is_some() {
|
|
||||||
return err;
|
|
||||||
};
|
|
||||||
|
|
||||||
let (mut block, err) = classify_block(&lite_block, scope);
|
|
||||||
scope.exit_scope();
|
|
||||||
|
|
||||||
block.params = signature;
|
|
||||||
block.params.name = name;
|
|
||||||
|
|
||||||
scope.add_definition(block);
|
|
||||||
|
|
||||||
err
|
|
||||||
}
|
|
||||||
_ => Some(ParseError::mismatch("body", call.parts[3].clone())),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(ParseError::internal_error(
|
|
||||||
"need a block".to_string().spanned(call.span()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_definition_prototype(call: &LiteCommand, scope: &dyn ParserScope) -> Option<ParseError> {
|
|
||||||
let mut err = None;
|
|
||||||
|
|
||||||
if call.parts.len() != 4 {
|
|
||||||
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if call.parts[0].item != "def" {
|
|
||||||
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = trim_quotes(&call.parts[1].item);
|
|
||||||
let (signature, error) = parse_signature(&name, &call.parts[2], scope);
|
|
||||||
if err.is_none() {
|
|
||||||
err = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.add_definition(Block::new(signature, vec![], IndexMap::new(), call.span()));
|
|
||||||
|
|
||||||
err
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn classify_block(
|
pub fn classify_block(
|
||||||
lite_block: &LiteBlock,
|
lite_block: &LiteBlock,
|
||||||
scope: &dyn ParserScope,
|
scope: &dyn ParserScope,
|
||||||
@ -2350,11 +2133,6 @@ pub fn parse(
|
|||||||
classify_block(&lite_block, scope)
|
classify_block(&lite_block, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Easy shorthand function to create a garbage expression at the given span
|
|
||||||
pub fn garbage(span: Span) -> SpannedExpression {
|
|
||||||
SpannedExpression::new(Expression::Garbage, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unit_parse_byte_units() -> Result<(), ParseError> {
|
fn unit_parse_byte_units() -> Result<(), ParseError> {
|
||||||
struct TestCase {
|
struct TestCase {
|
||||||
|
100
crates/nu-parser/src/parse/def.rs
Normal file
100
crates/nu-parser/src/parse/def.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use crate::parse::{classify_block, util::trim_quotes};
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu_errors::ParseError;
|
||||||
|
use nu_protocol::hir::Block;
|
||||||
|
use nu_source::SpannedItem;
|
||||||
|
|
||||||
|
//use crate::errors::{ParseError, ParseResult};
|
||||||
|
use crate::lex::{block, lex, LiteCommand};
|
||||||
|
|
||||||
|
use crate::ParserScope;
|
||||||
|
|
||||||
|
use self::param_flag_list::parse_signature;
|
||||||
|
|
||||||
|
mod param_flag_list;
|
||||||
|
|
||||||
|
pub(crate) fn parse_definition(call: &LiteCommand, scope: &dyn ParserScope) -> Option<ParseError> {
|
||||||
|
// A this point, we've already handled the prototype and put it into scope;
|
||||||
|
// So our main goal here is to parse the block now that the names and
|
||||||
|
// prototypes of adjacent commands are also available
|
||||||
|
|
||||||
|
if call.parts.len() == 4 {
|
||||||
|
if call.parts.len() != 4 {
|
||||||
|
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.parts[0].item != "def" {
|
||||||
|
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = trim_quotes(&call.parts[1].item);
|
||||||
|
let (mut signature, err) = parse_signature(&name, &call.parts[2]);
|
||||||
|
|
||||||
|
//Add commands comments to signature usage
|
||||||
|
signature.usage = call.comments_joined();
|
||||||
|
|
||||||
|
if err.is_some() {
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut chars = call.parts[3].chars();
|
||||||
|
match (chars.next(), chars.next_back()) {
|
||||||
|
(Some('{'), Some('}')) => {
|
||||||
|
// We have a literal block
|
||||||
|
let string: String = chars.collect();
|
||||||
|
|
||||||
|
scope.enter_scope();
|
||||||
|
|
||||||
|
let (tokens, err) = lex(&string, call.parts[3].span.start() + 1);
|
||||||
|
if err.is_some() {
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
let (lite_block, err) = block(tokens);
|
||||||
|
if err.is_some() {
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut block, err) = classify_block(&lite_block, scope);
|
||||||
|
scope.exit_scope();
|
||||||
|
|
||||||
|
block.params = signature;
|
||||||
|
block.params.name = name;
|
||||||
|
|
||||||
|
scope.add_definition(block);
|
||||||
|
|
||||||
|
err
|
||||||
|
}
|
||||||
|
_ => Some(ParseError::mismatch("body", call.parts[3].clone())),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(ParseError::internal_error(
|
||||||
|
"need a block".to_string().spanned(call.span()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_definition_prototype(
|
||||||
|
call: &LiteCommand,
|
||||||
|
scope: &dyn ParserScope,
|
||||||
|
) -> Option<ParseError> {
|
||||||
|
let mut err = None;
|
||||||
|
|
||||||
|
if call.parts.len() != 4 {
|
||||||
|
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.parts[0].item != "def" {
|
||||||
|
return Some(ParseError::mismatch("definition", call.parts[0].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = trim_quotes(&call.parts[1].item);
|
||||||
|
let (signature, error) = parse_signature(&name, &call.parts[2]);
|
||||||
|
if err.is_none() {
|
||||||
|
err = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.add_definition(Block::new(signature, vec![], IndexMap::new(), call.span()));
|
||||||
|
|
||||||
|
err
|
||||||
|
}
|
859
crates/nu-parser/src/parse/def/param_flag_list.rs
Normal file
859
crates/nu-parser/src/parse/def/param_flag_list.rs
Normal file
@ -0,0 +1,859 @@
|
|||||||
|
///This module contains functions to parse the parameter and flag list (signature) of a
|
||||||
|
///definition
|
||||||
|
///Such a signature can be of the following format:
|
||||||
|
/// [ (parameter | flag | <eol>)* ]
|
||||||
|
///Where
|
||||||
|
///parameter is:
|
||||||
|
/// name (<:> type)? (<,> | <eol> | (#Comment <eol>))?
|
||||||
|
///flag is:
|
||||||
|
/// --name (-shortform)? (<:> type)? (<,> | <eol> | (#Comment <eol>))?
|
||||||
|
///
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
lex::{lex, Token, TokenContents},
|
||||||
|
parse::util::token_to_spanned_string,
|
||||||
|
};
|
||||||
|
use nu_errors::ParseError;
|
||||||
|
use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape};
|
||||||
|
use nu_source::{Span, Spanned, SpannedItem};
|
||||||
|
|
||||||
|
pub fn parse_signature(
|
||||||
|
name: &str,
|
||||||
|
signature_vec: &Spanned<String>,
|
||||||
|
) -> (Signature, Option<ParseError>) {
|
||||||
|
let mut err = None;
|
||||||
|
|
||||||
|
let mut chars = signature_vec.chars();
|
||||||
|
|
||||||
|
match (chars.next(), chars.next_back()) {
|
||||||
|
(Some('['), Some(']')) => {}
|
||||||
|
_ => {
|
||||||
|
err = err.or_else(|| {
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"definition signature",
|
||||||
|
signature_vec.clone(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let string: String = chars.collect();
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"signature vec span start: {}",
|
||||||
|
signature_vec.span.start() + 1
|
||||||
|
);
|
||||||
|
let (tokens, error) = lex(&string, signature_vec.span.start() + 1);
|
||||||
|
err = err.or(error);
|
||||||
|
|
||||||
|
//After normal lexing, tokens also need to be split on ',' and ':'
|
||||||
|
let tokens = lex_split_baseline_tokens_on(tokens, &[',', ':']);
|
||||||
|
|
||||||
|
let mut parameters = vec![];
|
||||||
|
let mut flags = vec![];
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while i < tokens.len() {
|
||||||
|
if tokens[i].contents.is_eol() {
|
||||||
|
//Skip leading eol
|
||||||
|
i += 1;
|
||||||
|
} else if is_flag(&tokens[i]) {
|
||||||
|
let (flag, advanced_by, error) = parse_flag(&tokens[i..], signature_vec);
|
||||||
|
err = err.or(error);
|
||||||
|
i += advanced_by;
|
||||||
|
flags.push(flag);
|
||||||
|
} else {
|
||||||
|
let (parameter, advanced_by, error) = parse_parameter(&tokens[i..], signature_vec);
|
||||||
|
err = err.or(error);
|
||||||
|
i += advanced_by;
|
||||||
|
parameters.push(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature = to_signature(name, parameters, flags);
|
||||||
|
debug!("Signature: {:?}", signature);
|
||||||
|
|
||||||
|
(signature, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_parameter(
|
||||||
|
tokens: &[Token],
|
||||||
|
tokens_as_str: &Spanned<String>,
|
||||||
|
) -> (Parameter, usize, Option<ParseError>) {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
//TODO fix span
|
||||||
|
return (
|
||||||
|
Parameter::error(),
|
||||||
|
0,
|
||||||
|
Some(ParseError::unexpected_eof("parameter", tokens_as_str.span)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut err: Option<ParseError> = None;
|
||||||
|
//1 because name = tokens[0]
|
||||||
|
let mut i = 1;
|
||||||
|
|
||||||
|
let (name, error) = parse_param_name(&tokens[0]);
|
||||||
|
err = err.or(error);
|
||||||
|
|
||||||
|
let (type_, advanced_by, error) = parse_optional_type(&tokens[i..]);
|
||||||
|
let type_ = type_.unwrap_or(SyntaxShape::Any);
|
||||||
|
err = err.or(error);
|
||||||
|
i += advanced_by;
|
||||||
|
|
||||||
|
let (comment_text, advanced_by, error) = parse_signature_item_end(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
err = err.or(error);
|
||||||
|
|
||||||
|
let parameter = Parameter::new(
|
||||||
|
PositionalType::mandatory(&name.item, type_),
|
||||||
|
comment_text,
|
||||||
|
name.span,
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Parsed parameter: {} with shape {:?}",
|
||||||
|
parameter.pos_type.name(),
|
||||||
|
parameter.pos_type.syntax_type()
|
||||||
|
);
|
||||||
|
|
||||||
|
(parameter, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_flag(
|
||||||
|
tokens: &[Token],
|
||||||
|
tokens_as_str: &Spanned<String>,
|
||||||
|
) -> (Flag, usize, Option<ParseError>) {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
return (
|
||||||
|
Flag::error(),
|
||||||
|
0,
|
||||||
|
Some(ParseError::unexpected_eof("parameter", tokens_as_str.span)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut err: Option<ParseError> = None;
|
||||||
|
//1 because name = tokens[0]
|
||||||
|
let mut i = 1;
|
||||||
|
|
||||||
|
let (name, error) = parse_flag_name(&tokens[0]);
|
||||||
|
err = err.or(error);
|
||||||
|
|
||||||
|
let (shortform, advanced_by, error) = parse_flag_optional_shortform(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
err = err.or(error);
|
||||||
|
let shortform = shortform.map(|c| c.item);
|
||||||
|
|
||||||
|
let (type_, advanced_by, error) = parse_optional_type(&tokens[i..]);
|
||||||
|
let type_ = type_.unwrap_or(SyntaxShape::Any);
|
||||||
|
err = err.or(error);
|
||||||
|
i += advanced_by;
|
||||||
|
|
||||||
|
let (comment, advanced_by, error) = parse_signature_item_end(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
err = err.or(error);
|
||||||
|
|
||||||
|
//TODO Fixup span
|
||||||
|
let flag = Flag::new(
|
||||||
|
name.item.clone(),
|
||||||
|
NamedType::Optional(shortform, type_),
|
||||||
|
comment,
|
||||||
|
name.span,
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!("Parsed flag: {:?}", flag);
|
||||||
|
(flag, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type(type_: &Spanned<String>) -> (SyntaxShape, Option<ParseError>) {
|
||||||
|
debug!("Parsing type {:?}", type_);
|
||||||
|
match type_.item.as_str() {
|
||||||
|
"int" => (SyntaxShape::Int, None),
|
||||||
|
"string" => (SyntaxShape::String, None),
|
||||||
|
"path" => (SyntaxShape::FilePath, None),
|
||||||
|
"table" => (SyntaxShape::Table, None),
|
||||||
|
"unit" => (SyntaxShape::Unit, None),
|
||||||
|
"number" => (SyntaxShape::Number, None),
|
||||||
|
"pattern" => (SyntaxShape::GlobPattern, None),
|
||||||
|
"range" => (SyntaxShape::Range, None),
|
||||||
|
"block" => (SyntaxShape::Block, None),
|
||||||
|
"any" => (SyntaxShape::Any, None),
|
||||||
|
_ => (
|
||||||
|
SyntaxShape::Any,
|
||||||
|
Some(ParseError::mismatch("type", type_.clone())),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type_token(type_: &Token) -> (SyntaxShape, Option<ParseError>) {
|
||||||
|
match &type_.contents {
|
||||||
|
TokenContents::Baseline(type_str) => parse_type(&type_str.clone().spanned(type_.span)),
|
||||||
|
_ => (
|
||||||
|
SyntaxShape::Any,
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"type",
|
||||||
|
type_.contents.to_string().spanned(type_.span),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_param_name(token: &Token) -> (Spanned<String>, Option<ParseError>) {
|
||||||
|
match &token.contents {
|
||||||
|
TokenContents::Baseline(name) => {
|
||||||
|
//Make sure user didn't enter type
|
||||||
|
//REVIEW Should type names be allowed to be parameter names:
|
||||||
|
//Example case:
|
||||||
|
//def f [ string ] { echo $string }
|
||||||
|
//Currently an error is thrown
|
||||||
|
let name = name.clone().spanned(token.span);
|
||||||
|
let (_, err) = parse_type(&name);
|
||||||
|
if err.is_some() {
|
||||||
|
//Okay not a type. Just return name
|
||||||
|
(name, None)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"parameter name",
|
||||||
|
token_to_spanned_string(token),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
"Internal Error".to_string().spanned(token.span),
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"parameter name",
|
||||||
|
token_to_spanned_string(token),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_optional_comment(tokens: &[Token]) -> (Option<String>, usize) {
|
||||||
|
let mut comment_text = None;
|
||||||
|
let mut i: usize = 0;
|
||||||
|
if i < tokens.len() {
|
||||||
|
if let TokenContents::Comment(comment) = &tokens[i].contents {
|
||||||
|
comment_text = Some(comment.trim().to_string());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(comment_text, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_optional_type(tokens: &[Token]) -> (Option<SyntaxShape>, usize, Option<ParseError>) {
|
||||||
|
fn is_double_point(token: &Token) -> bool {
|
||||||
|
match &token.contents {
|
||||||
|
TokenContents::Baseline(base) => base == ":",
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut err = None;
|
||||||
|
let mut type_ = None;
|
||||||
|
let mut i: usize = 0;
|
||||||
|
//Check if a type has to follow
|
||||||
|
if i < tokens.len() && is_double_point(&tokens[i]) {
|
||||||
|
//Type has to follow
|
||||||
|
if i + 1 == tokens.len() {
|
||||||
|
err = err.or_else(|| Some(ParseError::unexpected_eof("type", tokens[i].span)));
|
||||||
|
} else {
|
||||||
|
//Jump over <:>
|
||||||
|
i += 1;
|
||||||
|
let (shape, error) = parse_type_token(&tokens[i]);
|
||||||
|
err = err.or(error);
|
||||||
|
type_ = Some(shape);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(type_, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Parses the end of a flag or a parameter
|
||||||
|
/// ((<,> | <eol>) | (#Comment <eol>)
|
||||||
|
fn parse_signature_item_end(tokens: &[Token]) -> (Option<String>, usize, Option<ParseError>) {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
//If no more tokens, parameter/flag doesn't need ',' or comment to be properly finished
|
||||||
|
return (None, 0, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let err = None;
|
||||||
|
let (parsed_comma, advanced_by) = parse_comma(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
let (comment, advanced_by) = parse_optional_comment(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
let (parsed_eol, advanced_by) = parse_eol(&tokens[i..]);
|
||||||
|
i += advanced_by;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Parsed comma {} and parsed eol {}",
|
||||||
|
parsed_comma, parsed_eol
|
||||||
|
);
|
||||||
|
////Separating flags/parameters is optional.
|
||||||
|
////If this should change, the below code would raise a warning whenever 2 parameters/flags are
|
||||||
|
////not delmited by <,> or <eol>
|
||||||
|
//if there is next item, but it's not comma, then it must be Optional(#Comment) + <eof>
|
||||||
|
//let parsed_delimiter = parsed_comma || parsed_eol;
|
||||||
|
//if !parsed_delimiter && i < tokens.len() {
|
||||||
|
// //If not parsed , or eol but more tokens are comming
|
||||||
|
// err = err.or(Some(ParseError::mismatch(
|
||||||
|
// "Newline or ','",
|
||||||
|
// (token[i-1].to_string() + token[i].to_string()).spanned(token[i-1].span.until(token[i].span))
|
||||||
|
// )));
|
||||||
|
//}
|
||||||
|
|
||||||
|
(comment, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_flag_name(token: &Token) -> (Spanned<String>, Option<ParseError>) {
|
||||||
|
if let TokenContents::Baseline(name) = &token.contents {
|
||||||
|
if !name.starts_with("--") {
|
||||||
|
(
|
||||||
|
name.clone().spanned(token.span),
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"longform of a flag (Starting with --)",
|
||||||
|
token_to_spanned_string(token),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
//Discard preceding --
|
||||||
|
let name = name[2..].to_string();
|
||||||
|
(name.spanned(token.span), None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
"".to_string().spanned_unknown(),
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"longform of a flag (Starting with --)",
|
||||||
|
token_to_spanned_string(token),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_flag_optional_shortform(
|
||||||
|
tokens: &[Token],
|
||||||
|
) -> (Option<Spanned<char>>, usize, Option<ParseError>) {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
return (None, 0, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = &tokens[0];
|
||||||
|
if let TokenContents::Baseline(shortform) = &token.contents {
|
||||||
|
let mut chars = shortform.chars();
|
||||||
|
match (chars.next(), chars.next_back()) {
|
||||||
|
(Some('('), Some(')')) => {
|
||||||
|
let mut err = None;
|
||||||
|
let mut start = token.span.start() + 1; //Skip '('
|
||||||
|
let end = token.span.end() - 1; // Skip ')'
|
||||||
|
let mut c: String = chars.collect();
|
||||||
|
let dash_count = c.chars().take_while(|c| *c == '-').count();
|
||||||
|
debug!("Dash count {}", dash_count);
|
||||||
|
match dash_count {
|
||||||
|
0 => {
|
||||||
|
//If no starting -
|
||||||
|
err = err.or_else(|| {
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"Shortflag starting with '-'",
|
||||||
|
c.clone().spanned((start, end)),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
//Skip over '-'
|
||||||
|
start += 1;
|
||||||
|
c.remove(0);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
//If --
|
||||||
|
err = err.or_else(|| {
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
"Shortflag starting with a single '-'",
|
||||||
|
c.clone().spanned((start, end)),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
//Skip over --
|
||||||
|
start += dash_count;
|
||||||
|
c = c
|
||||||
|
.strip_prefix(&"-".repeat(dash_count))
|
||||||
|
.unwrap_or("X")
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let err = err.or_else(|| match c.chars().count() {
|
||||||
|
0 => Some(ParseError::mismatch(
|
||||||
|
"Shortflag of exactly 1 character",
|
||||||
|
shortform.clone().spanned((start, end)),
|
||||||
|
)),
|
||||||
|
1 => None,
|
||||||
|
_ => Some(ParseError::mismatch(
|
||||||
|
"Shortflag of exactly 1 character",
|
||||||
|
c.clone().spanned((start, end)),
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
let c = c.chars().next().unwrap_or('X').spanned((start, end));
|
||||||
|
|
||||||
|
(Some(*c.spanned((start, end))), 1, err)
|
||||||
|
}
|
||||||
|
_ => (None, 0, None),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(None, 0, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_eol(tokens: &[Token]) -> (bool, usize) {
|
||||||
|
if !tokens.is_empty() && tokens[0].contents.is_eol() {
|
||||||
|
(true, 1)
|
||||||
|
} else {
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_comma(tokens: &[Token]) -> (bool, usize) {
|
||||||
|
fn is_comma(token: &Token) -> bool {
|
||||||
|
match &token.contents {
|
||||||
|
TokenContents::Baseline(base) => base == ",",
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !tokens.is_empty() && is_comma(&tokens[0]) {
|
||||||
|
(true, 1)
|
||||||
|
} else {
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///True for short or longform flags. False otherwise
|
||||||
|
fn is_flag(token: &Token) -> bool {
|
||||||
|
match &token.contents {
|
||||||
|
TokenContents::Baseline(item) => item.starts_with('-'),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_signature(name: &str, params: Vec<Parameter>, flags: Vec<Flag>) -> Signature {
|
||||||
|
let mut sign = Signature::new(name);
|
||||||
|
|
||||||
|
for param in params.into_iter() {
|
||||||
|
// pub positional: Vec<(PositionalType, Description)>,
|
||||||
|
sign.positional
|
||||||
|
.push((param.pos_type, param.desc.unwrap_or_else(|| "".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
for flag in flags.into_iter() {
|
||||||
|
sign.named.insert(
|
||||||
|
flag.long_name,
|
||||||
|
(flag.named_type, flag.desc.unwrap_or_else(|| "".to_string())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sign
|
||||||
|
}
|
||||||
|
|
||||||
|
//Currently the lexer does not split baselines on , and :
|
||||||
|
//The parameter list requires this. Therefore here is a hacky method doing this.
|
||||||
|
fn lex_split_baseline_tokens_on(
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
extra_baseline_terminal_tokens: &[char],
|
||||||
|
) -> Vec<Token> {
|
||||||
|
debug!("Before lex fix up {:?}", tokens);
|
||||||
|
let make_new_token =
|
||||||
|
|token_new: String, token_new_end: usize, terminator_char: Option<char>| {
|
||||||
|
let end = token_new_end;
|
||||||
|
let start = end - token_new.len();
|
||||||
|
|
||||||
|
let mut result = vec![];
|
||||||
|
//Only add token if its not empty
|
||||||
|
if !token_new.is_empty() {
|
||||||
|
result.push(Token::new(
|
||||||
|
TokenContents::Baseline(token_new),
|
||||||
|
Span::new(start, end),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
//Insert terminator_char as baseline token
|
||||||
|
if let Some(ch) = terminator_char {
|
||||||
|
result.push(Token::new(
|
||||||
|
TokenContents::Baseline(ch.to_string()),
|
||||||
|
Span::new(end, end + 1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
let mut result = Vec::with_capacity(tokens.len());
|
||||||
|
for token in tokens {
|
||||||
|
match token.contents {
|
||||||
|
TokenContents::Baseline(base) => {
|
||||||
|
let token_offset = token.span.start();
|
||||||
|
let mut current = "".to_string();
|
||||||
|
for (i, c) in base.chars().enumerate() {
|
||||||
|
if extra_baseline_terminal_tokens.contains(&c) {
|
||||||
|
result.extend(make_new_token(current, i + token_offset, Some(c)));
|
||||||
|
current = "".to_string();
|
||||||
|
} else {
|
||||||
|
current.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.extend(make_new_token(current, base.len() + token_offset, None));
|
||||||
|
}
|
||||||
|
_ => result.push(token),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
type Description = String;
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Parameter {
|
||||||
|
pub pos_type: PositionalType,
|
||||||
|
pub desc: Option<Description>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter {
|
||||||
|
pub fn new(pos_type: PositionalType, desc: Option<Description>, span: Span) -> Parameter {
|
||||||
|
Parameter {
|
||||||
|
pos_type,
|
||||||
|
desc,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error() -> Parameter {
|
||||||
|
Parameter::new(
|
||||||
|
PositionalType::optional("Internal Error", SyntaxShape::Any),
|
||||||
|
Some(
|
||||||
|
"Wanted to parse a parameter, but no input present. Please report this error!"
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
Span::unknown(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Flag {
|
||||||
|
pub long_name: String,
|
||||||
|
pub named_type: NamedType,
|
||||||
|
pub desc: Option<Description>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flag {
|
||||||
|
pub fn new(
|
||||||
|
long_name: String,
|
||||||
|
named_type: NamedType,
|
||||||
|
desc: Option<Description>,
|
||||||
|
span: Span,
|
||||||
|
) -> Flag {
|
||||||
|
Flag {
|
||||||
|
long_name,
|
||||||
|
named_type,
|
||||||
|
desc,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error() -> Flag {
|
||||||
|
Flag::new(
|
||||||
|
"Internal Error".to_string(),
|
||||||
|
NamedType::Switch(None),
|
||||||
|
Some(
|
||||||
|
"Wanted to parse a flag, but no input present. Please report this error!"
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
Span::unknown(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_params() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[param1:int, param2:string]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned(Span::new(0, 27)));
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::Int),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::String),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_params_with_comment() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
param1:path # My first param
|
||||||
|
param2:number # My second param
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned(Span::new(0, 64)));
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::FilePath),
|
||||||
|
"My first param".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
|
||||||
|
"My second param".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_params_without_type() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
param1 # My first param
|
||||||
|
param2:number # My second param
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned(Span::new(0, 0)));
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::Any),
|
||||||
|
"My first param".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
|
||||||
|
"My second param".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn oddly_but_correct_written_params() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
param1 :int # param1
|
||||||
|
|
||||||
|
param2 : number # My second param
|
||||||
|
|
||||||
|
|
||||||
|
param4, param5:path , param6 # param6
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned(Span::new(0, 0)));
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::Int),
|
||||||
|
"param1".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
|
||||||
|
"My second param".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param4".into(), SyntaxShape::Any),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param5".into(), SyntaxShape::FilePath),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param6".into(), SyntaxShape::Any),
|
||||||
|
"param6".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn err_wrong_type() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: ".",
|
||||||
|
"def f [ param1:strig ] { echo hi }"
|
||||||
|
);
|
||||||
|
assert!(actual.err.contains("type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//For what ever reason, this gets reported as not used
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn assert_signature_has_flag(sign: &Signature, name: &str, type_: NamedType, comment: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
Some((type_, comment.to_string())),
|
||||||
|
sign.named.get(name).cloned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_only_flags() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
--list (-l) : path # First flag
|
||||||
|
--verbose : number # Second flag
|
||||||
|
]";
|
||||||
|
// --all(-a) # My switch
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned_unknown());
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"list",
|
||||||
|
NamedType::Optional(Some('l'), SyntaxShape::FilePath),
|
||||||
|
"First flag",
|
||||||
|
);
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"verbose",
|
||||||
|
NamedType::Optional(None, SyntaxShape::Number),
|
||||||
|
"Second flag",
|
||||||
|
);
|
||||||
|
// assert_signature_has_flag(
|
||||||
|
// &sign,
|
||||||
|
// "verbose",
|
||||||
|
// NamedType::Switch(Some('a')),
|
||||||
|
// "Second flag",
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_params_and_flags() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
--list (-l) : path # First flag
|
||||||
|
param1, param2:table # Param2 Doc
|
||||||
|
--verbose # Second flag
|
||||||
|
param3 : number,
|
||||||
|
--flag3 # Third flag
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned_unknown());
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"list",
|
||||||
|
NamedType::Optional(Some('l'), SyntaxShape::FilePath),
|
||||||
|
"First flag",
|
||||||
|
);
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"verbose",
|
||||||
|
NamedType::Optional(None, SyntaxShape::Any),
|
||||||
|
"Second flag",
|
||||||
|
);
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"flag3",
|
||||||
|
NamedType::Optional(None, SyntaxShape::Any),
|
||||||
|
"Third flag",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
// --list (-l) : path # First flag
|
||||||
|
// param1, param2:table # Param2 Doc
|
||||||
|
// --verbose # Second flag
|
||||||
|
// param3 : number,
|
||||||
|
// --flag3 # Third flag
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::Any),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::Table),
|
||||||
|
"Param2 Doc".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param3".into(), SyntaxShape::Number),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_def_with_parameters_and_flags_no_delimiter() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[ param1:int param2
|
||||||
|
--force (-f) param3 # Param3
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned_unknown());
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"force",
|
||||||
|
NamedType::Optional(Some('f'), SyntaxShape::Any),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
// --list (-l) : path # First flag
|
||||||
|
// param1, param2:table # Param2 Doc
|
||||||
|
// --verbose # Second flag
|
||||||
|
// param3 : number,
|
||||||
|
// --flag3 # Third flag
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param1".into(), SyntaxShape::Int),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param2".into(), SyntaxShape::Any),
|
||||||
|
"".into()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PositionalType::Mandatory("param3".into(), SyntaxShape::Any),
|
||||||
|
"Param3".into()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_example_signature() {
|
||||||
|
let name = "my_func";
|
||||||
|
let sign = "[
|
||||||
|
d:int # The required d parameter
|
||||||
|
--x (-x):string # The all powerful x flag
|
||||||
|
--y (-y):int # The accompanying y flag
|
||||||
|
]";
|
||||||
|
let (sign, err) = parse_signature(name, &sign.to_string().spanned_unknown());
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"x",
|
||||||
|
NamedType::Optional(Some('x'), SyntaxShape::String),
|
||||||
|
"The all powerful x flag",
|
||||||
|
);
|
||||||
|
assert_signature_has_flag(
|
||||||
|
&sign,
|
||||||
|
"y",
|
||||||
|
NamedType::Optional(Some('y'), SyntaxShape::Int),
|
||||||
|
"The accompanying y flag",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sign.positional,
|
||||||
|
vec![(
|
||||||
|
PositionalType::Mandatory("d".into(), SyntaxShape::Int),
|
||||||
|
"The required d parameter".into()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
47
crates/nu-parser/src/parse/util.rs
Normal file
47
crates/nu-parser/src/parse/util.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use nu_errors::ParseError;
|
||||||
|
use nu_protocol::hir::{Expression, SpannedExpression};
|
||||||
|
use nu_source::{Span, Spanned, SpannedItem};
|
||||||
|
|
||||||
|
use crate::lex::Token;
|
||||||
|
|
||||||
|
pub(crate) fn token_to_spanned_string(token: &Token) -> Spanned<String> {
|
||||||
|
token.contents.to_string().spanned(token.span)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Easy shorthand function to create a garbage expression at the given span
|
||||||
|
pub fn garbage(span: Span) -> SpannedExpression {
|
||||||
|
SpannedExpression::new(Expression::Garbage, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn trim_quotes(input: &str) -> String {
|
||||||
|
let mut chars = input.chars();
|
||||||
|
|
||||||
|
match (chars.next(), chars.next_back()) {
|
||||||
|
(Some('\''), Some('\'')) => chars.collect(),
|
||||||
|
(Some('"'), Some('"')) => chars.collect(),
|
||||||
|
(Some('`'), Some('`')) => chars.collect(),
|
||||||
|
_ => input.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn verify_and_strip(
|
||||||
|
contents: &Spanned<String>,
|
||||||
|
left: char,
|
||||||
|
right: char,
|
||||||
|
) -> (String, Option<ParseError>) {
|
||||||
|
let mut chars = contents.item.chars();
|
||||||
|
|
||||||
|
match (chars.next(), chars.next_back()) {
|
||||||
|
(Some(l), Some(r)) if l == left && r == right => {
|
||||||
|
let output: String = chars.collect();
|
||||||
|
(output, None)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
String::new(),
|
||||||
|
Some(ParseError::mismatch(
|
||||||
|
format!("value in {} {}", left, right),
|
||||||
|
contents.clone(),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ use nu_source::{b, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// The types of named parameter that a command can have
|
/// The types of named parameter that a command can have
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum NamedType {
|
pub enum NamedType {
|
||||||
/// A flag without any associated argument. eg) `foo --bar, foo -b`
|
/// A flag without any associated argument. eg) `foo --bar, foo -b`
|
||||||
Switch(Option<char>),
|
Switch(Option<char>),
|
||||||
|
Loading…
Reference in New Issue
Block a user