From 8bc3f4607bd9cd89797492cc75f97e5e225b5db8 Mon Sep 17 00:00:00 2001 From: sekiguchi-nagisa Date: Wed, 6 Dec 2023 01:32:49 +0900 Subject: [PATCH] now not allow ${34 }, ${3.14} notation. require space after ${. close #735 --- CHANGELOG.md | 3 ++ src/parser.cpp | 13 +++++++++ src/parser.h | 1 + test/cmdline/cmdline_test.cpp | 2 +- test/exec/cases/base/arg_array.ds | 2 +- test/exec/cases/base/string_interpolation.ds | 2 +- test/exec/cases/syntax/interp1.ds | 3 ++ test/exec/cases/syntax/interp2.ds | 8 ++++++ test/node/node_test.cpp | 30 ++++++++++---------- 9 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 test/exec/cases/syntax/interp1.ds create mode 100644 test/exec/cases/syntax/interp2.ds diff --git a/CHANGELOG.md b/CHANGELOG.md index 18dd77ffc..f26d9b0dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,9 @@ #### Core - **Breaking Change**: remove ``name`` parameter from ``CLI`` attribute +- **Breaking Change**: need spaces between `${` and number + - due to suppress potential syntax ambiguity + - now ``${345 }``, ``${3.14}`` notations are syntax error - add ``toplevel`` parameter to ``CLI`` attribute - in io redirection, allow file descriptor number greater than 4 (up to 9) - now support like the following bash idiom diff --git a/src/parser.cpp b/src/parser.cpp index 123678c79..7b2b4ebd0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2325,7 +2325,20 @@ std::unique_ptr Parser::parse_interpolation(EmbedNode::Kind kind) { auto ctx = this->inIgnorableNLCtx(); unsigned int pos = START_POS(); TRY(this->expect(TokenKind::START_INTERP)); + bool mayNeedSpace = false; + const Token oldToken = this->curToken; + const TokenKind oldKind = this->curKind; + if (oldKind == TokenKind::INT_LITERAL || oldKind == TokenKind::FLOAT_LITERAL) { + if (!this->hasSpace() && !this->hasNewline()) { + mayNeedSpace = true; + } + } auto node = TRY(this->parse_expression()); + if (mayNeedSpace && isa(*node)) { + this->createError(oldKind, oldToken, START_INTERP_NUM_NEED_SPACE, + "require space between `${' and number"); + return nullptr; + } auto endToken = TRY(this->expect(TokenKind::RBC)); return std::make_unique(pos, kind, std::move(node), endToken); } diff --git a/src/parser.h b/src/parser.h index 774c8371e..b079ee163 100644 --- a/src/parser.h +++ b/src/parser.h @@ -84,6 +84,7 @@ class Parser : public ydsh::ParserBase { static constexpr const char *REDIR_NEED_SPACE = "RedirNeedSpace"; static constexpr const char *INVALID_HERE_START = "InvalidHereStart"; static constexpr const char *HERE_START_NEED_SPACE = "HereStartNeedSpace"; + static constexpr const char *START_INTERP_NUM_NEED_SPACE = "InterpNumNeedSpace"; ObserverPtr compCtx; diff --git a/test/cmdline/cmdline_test.cpp b/test/cmdline/cmdline_test.cpp index 1c6ff81bb..39914090f 100644 --- a/test/cmdline/cmdline_test.cpp +++ b/test/cmdline/cmdline_test.cpp @@ -459,7 +459,7 @@ TEST_F(CmdlineTest, execPath) { TEST_F(CmdlineTest, locale) { { - auto builder = ds("-c", "echo ${3.14}").addEnv("LANG", "de_DE.UTF-8"); // locale independent + auto builder = ds("-c", "echo ${ 3.14 }").addEnv("LANG", "de_DE.UTF-8"); // locale independent ASSERT_NO_FATAL_FAILURE(this->expect(std::move(builder), 0, "3.140000\n")); } } diff --git a/test/exec/cases/base/arg_array.ds b/test/exec/cases/base/arg_array.ds index 9a4249a29..bb55f19e3 100644 --- a/test/exec/cases/base/arg_array.ds +++ b/test/exec/cases/base/arg_array.ds @@ -50,7 +50,7 @@ $args = @(${0.0/-0.0}) assert $args.size() == 1 assert $args[0] == (0.0/-0.0) as String -$args = @(${3.156}) +$args = @(${ 3.156 }) assert $args.size() == 1 assert $args[0] == (3.156) as String diff --git a/test/exec/cases/base/string_interpolation.ds b/test/exec/cases/base/string_interpolation.ds index f925b978f..bc623e715 100644 --- a/test/exec/cases/base/string_interpolation.ds +++ b/test/exec/cases/base/string_interpolation.ds @@ -24,7 +24,7 @@ assert("this is true" == "this is ${true}") assert("this is false" == "this is ${false}") ## Float -assert "${3.14}" == 3.14 as String +assert "${ 3.14 }" == 3.14 as String assert "${3.14/0.0}" == (3.14/0.0) as String assert "${-3.1/0.0}" == (-3.1/0.0) as String assert "${0.0/0.0}" == (0.0/0.0) as String diff --git a/test/exec/cases/syntax/interp1.ds b/test/exec/cases/syntax/interp1.ds new file mode 100644 index 000000000..d25c8d8a7 --- /dev/null +++ b/test/exec/cases/syntax/interp1.ds @@ -0,0 +1,3 @@ +#$test($result = 'parse', $lineNum = 3, $chars = 8, $errorKind = 'InterpNumNeedSpace', $status = 1) + +echo ${34245 } \ No newline at end of file diff --git a/test/exec/cases/syntax/interp2.ds b/test/exec/cases/syntax/interp2.ds new file mode 100644 index 000000000..c1fed81de --- /dev/null +++ b/test/exec/cases/syntax/interp2.ds @@ -0,0 +1,8 @@ +#$test($result = 'parse', $lineNum = 8, $chars = 9, $errorKind = 'InterpNumNeedSpace', $status = 1) + +echo ${ +23 +} +echo ${(52345)} + +echo "${34245.0}" \ No newline at end of file diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp index 6e5d6e7b1..83dc11cbe 100644 --- a/test/node/node_test.cpp +++ b/test/node/node_test.cpp @@ -504,36 +504,36 @@ static constexpr NodeDumpParam paramTable[] = { resolvedType: null )"}, - {DumpOp::untyped, R"(assert (!ls > 34 | 34 with < ${34.1} &).poll())", 1, 0, R"EOF( + {DumpOp::untyped, R"(assert (!ls > 34 | 34 with < ${ 34.1} &).poll())", 1, 0, R"EOF( nodes: - nodeKind: Assert token: pos: 0 - size: 46 + size: 47 type: condNode: nodeKind: Apply token: pos: 7 - size: 39 + size: 40 type: exprNode: nodeKind: Access token: pos: 7 - size: 37 + size: 38 type: recvNode: nodeKind: Fork token: pos: 7 - size: 32 + size: 33 type: exprNode: nodeKind: Pipeline token: pos: 8 - size: 28 + size: 29 type: nodes: - nodeKind: UnaryOp @@ -598,7 +598,7 @@ static constexpr NodeDumpParam paramTable[] = { - nodeKind: With token: pos: 19 - size: 17 + size: 18 type: exprNode: nodeKind: Number @@ -613,7 +613,7 @@ static constexpr NodeDumpParam paramTable[] = { - nodeKind: Redir token: pos: 27 - size: 9 + size: 10 type: fdName: "0" newFd: -1 @@ -622,7 +622,7 @@ static constexpr NodeDumpParam paramTable[] = { nodeKind: CmdArg token: pos: 29 - size: 7 + size: 8 type: expansionSize: 0 expansionError: false @@ -631,13 +631,13 @@ static constexpr NodeDumpParam paramTable[] = { - nodeKind: Embed token: pos: 29 - size: 7 + size: 8 type: kind: "CMD_ARG" exprNode: nodeKind: Number token: - pos: 31 + pos: 32 size: 4 type: kind: "Float" @@ -657,7 +657,7 @@ static constexpr NodeDumpParam paramTable[] = { opKind: "ForkKind::JOB" nameInfo: token: - pos: 40 + pos: 41 size: 4 name: "poll" handle: null @@ -665,7 +665,7 @@ static constexpr NodeDumpParam paramTable[] = { argsNode: nodeKind: Args token: - pos: 44 + pos: 45 size: 2 type: nodes: @@ -676,11 +676,11 @@ static constexpr NodeDumpParam paramTable[] = { nodeKind: String token: pos: 7 - size: 39 + size: 40 type: kind: "STRING" init: true - value: "`(!ls > 34 | 34 with < ${34.1} &).poll()'" + value: "`(!ls > 34 | 34 with < ${ 34.1} &).poll()'" )EOF"}, {DumpOp::typed, R"(case $SIGINT { $SIGINT => [34:34]; else => (34,)})", 0, 0, R"EOF(