| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/shell/parser/parser.h" |
| |
| #include <tuple> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/developer/shell/parser/ast.h" |
| |
| namespace shell::parser { |
| namespace checks { |
| |
| class Skip { |
| public: |
| void Check(ast::Node* node) const {} |
| }; |
| |
| template <typename T> |
| class Expression { |
| public: |
| Expression(T checker) : checker_(std::move(checker)) {} |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto expr = node->AsExpression(); |
| ASSERT_TRUE(expr); |
| |
| ASSERT_GT(expr->Children().size(), 0u); |
| checker_.Check(expr->Children()[0].get()); |
| } |
| |
| private: |
| T checker_; |
| }; |
| |
| class Integer { |
| public: |
| Integer(uint64_t value) : value_(value) {} |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto integer = node->AsInteger(); |
| ASSERT_TRUE(integer); |
| |
| EXPECT_EQ(value_, integer->value()); |
| } |
| |
| private: |
| uint64_t value_; |
| }; |
| |
| class String { |
| public: |
| String(std::string value) : value_(value) {} |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto str = node->AsString(); |
| ASSERT_TRUE(str); |
| |
| EXPECT_EQ(value_, str->value()); |
| } |
| |
| private: |
| std::string value_; |
| }; |
| |
| class Identifier { |
| public: |
| Identifier(std::string identifier) : identifier_(identifier) {} |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto ident = node->AsIdentifier(); |
| ASSERT_TRUE(ident); |
| |
| EXPECT_EQ(identifier_, ident->identifier()); |
| } |
| |
| private: |
| std::string identifier_; |
| }; |
| |
| template <typename T> |
| class VariableDecl { |
| public: |
| VariableDecl(const std::string& name, bool is_const, T expr_check) |
| : name_(name), is_const_(is_const), expr_check_(std::move(expr_check)) {} |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto decl = node->AsVariableDecl(); |
| ASSERT_TRUE(decl); |
| |
| EXPECT_EQ(name_, decl->identifier()); |
| EXPECT_EQ(is_const_, decl->is_const()); |
| |
| expr_check_.Check(decl->expression()); |
| } |
| |
| private: |
| std::string name_; |
| bool is_const_; |
| T expr_check_; |
| }; |
| |
| template <typename... Args> |
| class Object { |
| public: |
| Object(Args... args) : fields_(args...) {} |
| |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto obj = node->AsObject(); |
| ASSERT_TRUE(obj); |
| ASSERT_EQ(obj->fields().size(), std::tuple_size<std::tuple<Args...>>::value); |
| if constexpr (std::tuple_size<std::tuple<Args...>>::value > 0) { |
| DoCheck(obj); |
| } |
| } |
| |
| private: |
| template <size_t I = 0> |
| void DoCheck(ast::Object* obj) const { |
| std::get<I>(fields_).Check(obj->fields()[I]); |
| |
| if constexpr ((I + 1) < std::tuple_size<std::tuple<Args...>>::value) { |
| DoCheck<I + 1>(obj); |
| } |
| } |
| |
| std::tuple<Args...> fields_; |
| }; |
| |
| template <typename T> |
| class Field { |
| public: |
| Field(const std::string& name, T value_check) : name_(name), value_check_(value_check) {} |
| |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto field = node->AsField(); |
| ASSERT_TRUE(field); |
| EXPECT_EQ(name_, field->name()); |
| value_check_.Check(field->value()); |
| } |
| |
| private: |
| std::string name_; |
| T value_check_; |
| }; |
| |
| class Path { |
| public: |
| Path(bool is_local, std::initializer_list<std::string> elements) |
| : is_local_(is_local), elements_(elements) {} |
| |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| auto path = node->AsPath(); |
| ASSERT_TRUE(path); |
| if (is_local_) { |
| EXPECT_TRUE(path->is_local()); |
| } else { |
| EXPECT_FALSE(path->is_local()); |
| } |
| |
| EXPECT_EQ(elements_.size(), path->elements().size()); |
| |
| for (size_t i = 0; i < std::min(elements_.size(), path->elements().size()); i++) { |
| EXPECT_EQ(elements_[i], path->elements()[i]); |
| } |
| } |
| |
| private: |
| bool is_local_; |
| std::vector<std::string> elements_; |
| }; |
| |
| template <typename A, typename B> |
| class AddSub { |
| public: |
| AddSub(A a, char op, B b) : op_(op), a_(std::move(a)), b_(std::move(b)) {} |
| |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(op_ == '+' || op_ == '-') << "Operator for AddSub should be + or -"; |
| auto add_sub = node->AsAddSub(); |
| ASSERT_TRUE(add_sub); |
| |
| if (op_ == '+') { |
| EXPECT_EQ(add_sub->type(), ast::AddSub::kAdd); |
| } else { |
| EXPECT_EQ(add_sub->type(), ast::AddSub::kSubtract); |
| } |
| |
| ASSERT_TRUE(add_sub->a()); |
| a_.Check(add_sub->a()); |
| ASSERT_TRUE(add_sub->b()); |
| b_.Check(add_sub->b()); |
| } |
| |
| private: |
| char op_; |
| A a_; |
| B b_; |
| }; |
| |
| template <typename... Args> |
| class Program { |
| public: |
| Program(Args... args) : checks_(args...) {} |
| |
| void Check(ast::Node* node) const { |
| ASSERT_TRUE(node); |
| |
| ASSERT_EQ(node->Children().size(), std::tuple_size<std::tuple<Args...>>::value); |
| if constexpr (std::tuple_size<std::tuple<Args...>>::value > 0) { |
| DoCheck(node); |
| } |
| } |
| |
| private: |
| template <size_t I = 0> |
| void DoCheck(ast::Node* node) const { |
| std::get<I>(checks_).Check(node->Children()[I].get()); |
| |
| if constexpr ((I + 1) < std::tuple_size<std::tuple<Args...>>::value) { |
| DoCheck<I + 1>(node); |
| } |
| } |
| |
| std::tuple<Args...> checks_; |
| }; |
| |
| } // namespace checks |
| |
| #define CHECK_NODE(node, check) \ |
| do { \ |
| SCOPED_TRACE("CHECK_NODE"); \ |
| using namespace checks; \ |
| check.Check(node.get()); \ |
| if (HasFatalFailure()) \ |
| return; \ |
| } while (0); |
| |
| TEST(ParserTest, VariableDecl) { |
| const auto kTestString = "var s = 0"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Integer('0'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(0))))); |
| } |
| |
| TEST(ParserTest, VariableDeclFail) { |
| const auto kTestString = "vars = 0"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' E[Expected space] Identifier('s') '=' Expression(Integer('0'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(0))))); |
| } |
| |
| TEST(ParserTest, TwoVariableDecl) { |
| const auto kTestString = |
| "var x = 0;\n" |
| "var y = 0"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' Expression(Integer('0'))) ';' " |
| "VariableDecl('var' Identifier('y') '=' Expression(Integer('0'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Integer(0))), Skip(), |
| VariableDecl("y", false, Expression(Integer(0))))); |
| } |
| |
| TEST(ParserTest, TwoVariableDeclFail) { |
| const auto kTestString = |
| "varx = 0;\n" |
| "var y = 0"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' E[Expected space] Identifier('x') '=' " |
| "Expression(Integer('0'))) ';' VariableDecl('var' Identifier('y') '=' " |
| "Expression(Integer('0'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Integer(0))), Skip(), |
| VariableDecl("y", false, Expression(Integer(0))))); |
| } |
| |
| TEST(ParserTest, TwoVariableDeclTrailingChars) { |
| const auto kTestString = |
| "var x = 0;\n" |
| "var y = 0;\n" |
| "xxx"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| // TODO: This error will be easier to handle well when more of the syntax is in place. |
| EXPECT_EQ("Program(E[Unrecoverable parse error])", parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(Skip())); |
| } |
| |
| TEST(ParserTest, TwoVariableDeclConst) { |
| const auto kTestString = |
| "var x = 0;\n" |
| "const y = 0"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(" |
| "VariableDecl('var' Identifier('x') '=' Expression(Integer('0'))) ';' " |
| "VariableDecl('const' Identifier('y') '=' Expression(Integer('0'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Integer(0))), Skip(), |
| VariableDecl("y", true, Expression(Integer(0))))); |
| } |
| |
| TEST(ParserTest, VariableDeclLongerInteger) { |
| const auto kTestString = "var s = 12345"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Integer('12345'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(12345))))); |
| } |
| |
| TEST(ParserTest, VariableDeclGroupedInteger) { |
| const auto kTestString = "var s = 12_345"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Integer('12' '_' '345'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(12345))))); |
| } |
| |
| TEST(ParserTest, VariableDeclHexInteger) { |
| const auto kTestString = "var s = 0xabfF0912"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Integer('0x' 'abfF0912'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(0xabfF0912))))); |
| } |
| |
| TEST(ParserTest, VariableDeclGroupedHexInteger) { |
| const auto kTestString = "var s = 0xabfF_0912"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Integer('0x' 'abfF' '_' '0912'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Integer(0xabfF0912))))); |
| } |
| |
| TEST(ParserTest, VariableDeclIntegerBadGroup) { |
| const auto kTestString = "var s = _0912"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Identifier('_0912'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Identifier("_0912"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclIntegerZeroFirst) { |
| const auto kTestString = "var s = 0912"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Identifier(E[Identifier cannot begin with a digit] '0912'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Identifier("0912"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclIntegerHexNoMark) { |
| const auto kTestString = "var s = 0abc"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Identifier(E[Identifier cannot begin with a digit] '0abc'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Identifier("0abc"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclString) { |
| const auto kTestString = R"(var s = "bob")"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ(R"(Program(VariableDecl('var' Identifier('s') '=' Expression(String('"' 'bob' '"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bob"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringEscapes) { |
| const auto kTestString = R"(var s = "bob\"\n\r\t")"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ(R"(Program(VariableDecl('var' Identifier('s') '=' )" |
| R"(Expression(String('"' 'bob' '\"' '\n' '\r' '\t' '"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bob\"\n\r\t"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringUtf8) { |
| const auto kTestString = R"(var s = "Karkat ♋ \u00264b")"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ(R"(Program(VariableDecl('var' Identifier('s') '=' )" |
| R"(Expression(String('"' 'Karkat ♋ ' '\u00264b' '"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("Karkat ♋ ♋"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringLinebreak) { |
| const auto kTestString = |
| "var s = \"bob\\\n" |
| "smith\""; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ(R"(Program(VariableDecl('var' Identifier('s') '=' )" |
| R"(Expression(String('"' 'bob' )" |
| "'\\\n'" |
| R"( 'smith' '"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bob\nsmith"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringDangling) { |
| const auto kTestString = "var s = \"bob"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(String('\"' 'bob' E[Expected '\"']))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bob"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObject) { |
| const auto kTestString = "var s = { foo: 6 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' Integer('6')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(Object(Field("foo", Integer(6))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectQuotedKey) { |
| const auto kTestString = "var s = { \"foo\": 6 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(String('\"' 'foo' '\"') ':' Integer('6')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(Object(Field("foo", Integer(6))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectTrailingComma) { |
| const auto kTestString = "var s = { foo: 6, }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' Integer('6')) ',' '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(Object(Field("foo", Integer(6))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectMissingComma) { |
| const auto kTestString = "var s = { foo: 6 bar: 7 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' Integer('6')) E[Expected ','] " |
| "Field(Identifier('bar') ':' Integer('7')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl( |
| "s", false, |
| Expression(Object(Field("foo", Integer(6)), Field("bar", Integer(7))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectNested) { |
| const auto kTestString = "var s = { foo: { bar: 7 } }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' " |
| "Object('{' Field(Identifier('bar') ':' Integer('7')) '}')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl( |
| "s", false, Expression(Object(Field("foo", Object(Field("bar", Integer(7))))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectMultiKey) { |
| const auto kTestString = "var s = { foo: { bar: 7 }, baz: 23, bang: \"hiiii\" }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' " |
| "Object('{' Field(Identifier('bar') ':' Integer('7')) '}')) ',' " |
| "Field(Identifier('baz') ':' Integer('23')) ',' " |
| "Field(Identifier('bang') ':' String('\"' 'hiiii' '\"')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl( |
| "s", false, |
| Expression(Object(Field("foo", Object(Field("bar", Integer(7)))), |
| Field("baz", Integer(23)), Field("bang", String("hiiii"))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectEmpty) { |
| const auto kTestString = "var s = {}"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Object('{' '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Object())))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectDangling) { |
| const auto kTestString = "var s = { foo: { bar: 7 }, baz: 23, bang: \"hiiii\""; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' Object('{' Field(Identifier('bar') ':' " |
| "Integer('7')) '}')) ',' Field(Identifier('baz') ':' Integer('23')) ',' " |
| "Field(Identifier('bang') ':' String('\"' 'hiiii' '\"')) E[Expected '}']))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl( |
| "s", false, |
| Expression(Object(Field("foo", Object(Field("bar", Integer(7)))), |
| Field("baz", Integer(23)), Field("bang", String("hiiii"))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectDanglingField) { |
| const auto kTestString = "var s = { foo: "; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' " |
| "E[Expected value]) E[Expected '}']))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Object(Field("foo", Skip())))))); |
| } |
| |
| TEST(ParserTest, VariableDeclObjectNoFieldSeparator) { |
| const auto kTestString = "var s = { foo 6 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') E[Expected ':'] Integer('6')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(Object(Field("foo", Integer(6))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringBadEscape) { |
| const auto kTestString = "var s = \"bob\\qbob\""; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(String('\"' 'bob' '\\' E[Bad escape sequence: '\\q'] 'bob' '\"'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bobbob"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclStringDanglingEscape) { |
| const auto kTestString = "var s = \"bob\\"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(String('\"' 'bob' '\\' E[Escape sequence at end of input] E[Expected '\"']))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(String("bob"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclPath) { |
| const auto kTestString = "var x = ./somewhere/else"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('.' '/' 'somewhere' '/' 'else'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("x", false, Expression(Path(true, {"somewhere", "else"}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclRootPath) { |
| const auto kTestString = "var x = /somewhere/else"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('/' 'somewhere' '/' 'else'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("x", false, Expression(Path(false, {"somewhere", "else"}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclRootOnlyPath) { |
| const auto kTestString = "var x = /"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('x') '=' Expression(Path('/'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Path(false, {}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclDotOnlyPath) { |
| const auto kTestString = "var x = ."; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('x') '=' Expression(Path('.'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Path(true, {}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclDotSlashPath) { |
| const auto kTestString = "var x = ./"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('x') '=' Expression(Path('.' '/'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Path(true, {}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclTrailingSlashPath) { |
| const auto kTestString = "var x = ./somewhere/else/"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('.' '/' 'somewhere' '/' 'else' '/'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("x", false, Expression(Path(true, {"somewhere", "else"}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclPathEscape) { |
| const auto kTestString = "var x = ./somew\\ here/else"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('.' '/' 'somew' '\\ ' 'here' '/' 'else'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("x", false, Expression(Path(true, {"somew here", "else"}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclPathQuote) { |
| const auto kTestString = "var x = ./somew` oo oo `here/else"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('.' '/' 'somew' '`' ' oo oo ' '`' 'here' '/' 'else'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, |
| Expression(Path(true, {"somew oo oo here", "else"}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclPathDanglingQuote) { |
| const auto kTestString = "var x = ./somew` oo oo "; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_TRUE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Path('.' '/' 'somew' '`' ' oo oo ' E[Expected '`']))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("x", false, Expression(Path(true, {"somew oo oo "}))))); |
| } |
| |
| TEST(ParserTest, VariableDeclPathInObject) { |
| const auto kTestString = "var x = { foo: ./somewhere/else }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('x') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' " |
| "Path('.' '/' 'somewhere' '/' 'else')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl( |
| "x", false, Expression(Object(Field("foo", Path(true, {"somewhere", "else"}))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclIdentifier) { |
| const auto kTestString = "var s = bob"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ("Program(VariableDecl('var' Identifier('s') '=' Expression(Identifier('bob'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, Expression(Identifier("bob"))))); |
| } |
| |
| TEST(ParserTest, VariableDeclIdentifierInObject) { |
| const auto kTestString = "var s = { foo: bob }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' Identifier('bob')) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, |
| Expression(Object(Field("foo", Identifier("bob"))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAdd) { |
| const auto kTestString = "var s = 1 + 2"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(Integer('1') '+' Integer('2')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(AddSub(Integer(1), '+', Integer(2)))))); |
| } |
| |
| TEST(ParserTest, VariableDeclSubtract) { |
| const auto kTestString = "var s = 1 - 2"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(Integer('1') '-' Integer('2')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(AddSub(Integer(1), '-', Integer(2)))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddStrings) { |
| const auto kTestString = "var s = \"foo\" + \"bar\""; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(String('\"' 'foo' '\"') '+' String('\"' 'bar' '\"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, |
| Expression(AddSub(String("foo"), '+', String("bar")))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddStringInt) { |
| const auto kTestString = "var s = \"foo\" + 2"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(String('\"' 'foo' '\"') '+' Integer('2')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(AddSub(String("foo"), '+', Integer(2)))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddIntString) { |
| const auto kTestString = "var s = 2 + \"bar\""; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(Integer('2') '+' String('\"' 'bar' '\"')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, |
| Program(VariableDecl("s", false, Expression(AddSub(Integer(2), '+', String("bar")))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddObjectVariable) { |
| const auto kTestString = "var s = foo + { bar: 7 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' Expression(AddSub(Identifier('foo') '+' " |
| "Object('{' Field(Identifier('bar') ':' Integer('7')) '}')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, |
| Expression(AddSub(Identifier("foo"), '+', |
| Object(Field("bar", Integer(7)))))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddVariableObject) { |
| const auto kTestString = "var s = { bar: 7 } + foo"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(Object('{' Field(Identifier('bar') ':' Integer('7')) '}') '+' " |
| "Identifier('foo')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl("s", false, |
| Expression(AddSub(Object(Field("bar", Integer(7))), '+', |
| Identifier("foo")))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddSubtractChain) { |
| const auto kTestString = "var s = 1 - 2 + 7"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(AddSub(AddSub(Integer('1') '-' Integer('2')) '+' Integer('7')))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl( |
| "s", false, |
| Expression(AddSub(AddSub(Integer(1), '-', Integer(2)), '+', Integer(7)))))); |
| } |
| |
| TEST(ParserTest, VariableDeclAddSubtractChainInObject) { |
| const auto kTestString = "var s = { foo: 1 - 2 + 7 }"; |
| |
| auto parse = Parse(kTestString); |
| EXPECT_FALSE(parse->HasErrors()); |
| |
| EXPECT_EQ( |
| "Program(VariableDecl('var' Identifier('s') '=' " |
| "Expression(Object('{' Field(Identifier('foo') ':' " |
| "AddSub(AddSub(Integer('1') '-' Integer('2')) '+' Integer('7'))) '}'))))", |
| parse->ToString(kTestString)); |
| |
| CHECK_NODE(parse, Program(VariableDecl( |
| "s", false, |
| Expression(Object(Field("foo", AddSub(AddSub(Integer(1), '-', Integer(2)), |
| '+', Integer(7)))))))); |
| } |
| |
| } // namespace shell::parser |