blob: 7106ae9a78d08d01158b41f93ba30c3b19eb04a1 [file] [log] [blame]
// 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