Commit cd50687b authored by marja@chromium.org's avatar marja@chromium.org

(Pre)Parser: Move ParsePrimaryExpression to ParserBase.

Notes:
- To be able to move the recursive descent functions to ParserBase one at a
time, we temporarily need routing functions from traits to Parser/PreParser,
since the recursive descent functions form a cyclic structure.
- PreParser used to always allow intrinsic syntax. After this CL, it depends on
allow_natives_syntax() which was already in ParserBase.
- This CL also decouples (Pre)ParserTraits better from (Pre)Parser, passing more
information as parameters, so that the Traits don't need to get it from
(Pre)Parser.
R=ulan@chromium.org
BUG=v8:3126
LOG=N

Review URL: https://codereview.chromium.org/163333003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19374 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 4c2626a0
......@@ -535,7 +535,7 @@ void ParserTraits::ReportMessageAt(Scanner::Location source_location,
}
Handle<String> ParserTraits::GetSymbol() {
Handle<String> ParserTraits::GetSymbol(Scanner* scanner) {
int symbol_id = -1;
if (parser_->pre_parse_data() != NULL) {
symbol_id = parser_->pre_parse_data()->GetSymbolIdentifier();
......@@ -544,21 +544,99 @@ Handle<String> ParserTraits::GetSymbol() {
}
Handle<String> ParserTraits::NextLiteralString(PretenureFlag tenured) {
Scanner& scanner = parser_->scanner();
if (scanner.is_next_literal_ascii()) {
Handle<String> ParserTraits::NextLiteralString(Scanner* scanner,
PretenureFlag tenured) {
if (scanner->is_next_literal_ascii()) {
return parser_->isolate_->factory()->NewStringFromAscii(
scanner.next_literal_ascii_string(), tenured);
scanner->next_literal_ascii_string(), tenured);
} else {
return parser_->isolate_->factory()->NewStringFromTwoByte(
scanner.next_literal_utf16_string(), tenured);
scanner->next_literal_utf16_string(), tenured);
}
}
Expression* ParserTraits::ThisExpression(
Scope* scope,
AstNodeFactory<AstConstructionVisitor>* factory) {
return factory->NewVariableProxy(scope->receiver());
}
Expression* ParserTraits::ExpressionFromLiteral(
Token::Value token, int pos,
Scanner* scanner,
AstNodeFactory<AstConstructionVisitor>* factory) {
Factory* isolate_factory = parser_->isolate()->factory();
switch (token) {
case Token::NULL_LITERAL:
return factory->NewLiteral(isolate_factory->null_value(), pos);
case Token::TRUE_LITERAL:
return factory->NewLiteral(isolate_factory->true_value(), pos);
case Token::FALSE_LITERAL:
return factory->NewLiteral(isolate_factory->false_value(), pos);
case Token::NUMBER: {
ASSERT(scanner->is_literal_ascii());
double value = StringToDouble(parser_->isolate()->unicode_cache(),
scanner->literal_ascii_string(),
ALLOW_HEX | ALLOW_OCTAL |
ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY);
return factory->NewNumberLiteral(value, pos);
}
default:
ASSERT(false);
}
return NULL;
}
Expression* ParserTraits::ExpressionFromIdentifier(
Handle<String> name, int pos, Scope* scope,
AstNodeFactory<AstConstructionVisitor>* factory) {
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
// The name may refer to a module instance object, so its type is unknown.
#ifdef DEBUG
if (FLAG_print_interface_details)
PrintF("# Variable %s ", name->ToAsciiArray());
#endif
Interface* interface = Interface::NewUnknown(parser_->zone());
return scope->NewUnresolved(factory, name, interface, pos);
}
Expression* ParserTraits::ExpressionFromString(
int pos, Scanner* scanner,
AstNodeFactory<AstConstructionVisitor>* factory) {
Handle<String> symbol = GetSymbol(scanner);
if (parser_->fni_ != NULL) parser_->fni_->PushLiteralName(symbol);
return factory->NewLiteral(symbol, pos);
}
Expression* ParserTraits::ParseArrayLiteral(bool* ok) {
return parser_->ParseArrayLiteral(ok);
}
Expression* ParserTraits::ParseObjectLiteral(bool* ok) {
return parser_->ParseObjectLiteral(ok);
}
Expression* ParserTraits::ParseExpression(bool accept_IN, bool* ok) {
return parser_->ParseExpression(accept_IN, ok);
}
Expression* ParserTraits::ParseV8Intrinsic(bool* ok) {
return parser_->ParseV8Intrinsic(ok);
}
Parser::Parser(CompilationInfo* info)
: ParserBase<ParserTraits>(&scanner_,
info->isolate()->stack_guard()->real_climit(),
info->extension(),
this),
isolate_(info->isolate()),
symbol_cache_(0, info->zone()),
......@@ -567,7 +645,6 @@ Parser::Parser(CompilationInfo* info)
reusable_preparser_(NULL),
original_scope_(NULL),
target_stack_(NULL),
extension_(info->extension()),
pre_parse_data_(NULL),
fni_(NULL),
zone_(info->zone()),
......@@ -3430,124 +3507,6 @@ void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) {
}
Expression* Parser::ParsePrimaryExpression(bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
// 'true'
// 'false'
// Identifier
// Number
// String
// ArrayLiteral
// ObjectLiteral
// RegExpLiteral
// '(' Expression ')'
int pos = peek_position();
Expression* result = NULL;
switch (peek()) {
case Token::THIS: {
Consume(Token::THIS);
result = factory()->NewVariableProxy(scope_->receiver());
break;
}
case Token::NULL_LITERAL:
Consume(Token::NULL_LITERAL);
result = factory()->NewLiteral(isolate()->factory()->null_value(), pos);
break;
case Token::TRUE_LITERAL:
Consume(Token::TRUE_LITERAL);
result = factory()->NewLiteral(isolate()->factory()->true_value(), pos);
break;
case Token::FALSE_LITERAL:
Consume(Token::FALSE_LITERAL);
result = factory()->NewLiteral(isolate()->factory()->false_value(), pos);
break;
case Token::IDENTIFIER:
case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode.
Handle<String> name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
// The name may refer to a module instance object, so its type is unknown.
#ifdef DEBUG
if (FLAG_print_interface_details)
PrintF("# Variable %s ", name->ToAsciiArray());
#endif
Interface* interface = Interface::NewUnknown(zone());
result = scope_->NewUnresolved(factory(), name, interface, pos);
break;
}
case Token::NUMBER: {
Consume(Token::NUMBER);
ASSERT(scanner().is_literal_ascii());
double value = StringToDouble(isolate()->unicode_cache(),
scanner().literal_ascii_string(),
ALLOW_HEX | ALLOW_OCTAL |
ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY);
result = factory()->NewNumberLiteral(value, pos);
break;
}
case Token::STRING: {
Consume(Token::STRING);
Handle<String> symbol = GetSymbol();
result = factory()->NewLiteral(symbol, pos);
if (fni_ != NULL) fni_->PushLiteralName(symbol);
break;
}
case Token::ASSIGN_DIV:
result = ParseRegExpLiteral(true, CHECK_OK);
break;
case Token::DIV:
result = ParseRegExpLiteral(false, CHECK_OK);
break;
case Token::LBRACK:
result = ParseArrayLiteral(CHECK_OK);
break;
case Token::LBRACE:
result = ParseObjectLiteral(CHECK_OK);
break;
case Token::LPAREN:
Consume(Token::LPAREN);
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
parenthesized_function_ = (peek() == Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
break;
case Token::MOD:
if (allow_natives_syntax() || extension_ != NULL) {
result = ParseV8Intrinsic(CHECK_OK);
break;
}
// If we're not allowing special syntax we fall-through to the
// default case.
default: {
Token::Value tok = Next();
ReportUnexpectedToken(tok);
*ok = false;
return NULL;
}
}
return result;
}
Expression* Parser::ParseArrayLiteral(bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
......
......@@ -463,8 +463,26 @@ class ParserTraits {
}
// Producing data during the recursive descent.
Handle<String> GetSymbol();
Handle<String> NextLiteralString(PretenureFlag tenured);
Handle<String> GetSymbol(Scanner* scanner = NULL);
Handle<String> NextLiteralString(Scanner* scanner,
PretenureFlag tenured);
Expression* ThisExpression(Scope* scope,
AstNodeFactory<AstConstructionVisitor>* factory);
Expression* ExpressionFromLiteral(
Token::Value token, int pos, Scanner* scanner,
AstNodeFactory<AstConstructionVisitor>* factory);
Expression* ExpressionFromIdentifier(
Handle<String> name, int pos, Scope* scope,
AstNodeFactory<AstConstructionVisitor>* factory);
Expression* ExpressionFromString(
int pos, Scanner* scanner,
AstNodeFactory<AstConstructionVisitor>* factory);
// Temporary glue; these functions will move to ParserBase.
Expression* ParseArrayLiteral(bool* ok);
Expression* ParseObjectLiteral(bool* ok);
Expression* ParseExpression(bool accept_IN, bool* ok);
Expression* ParseV8Intrinsic(bool* ok);
private:
Parser* parser_;
......@@ -626,7 +644,6 @@ class Parser : public ParserBase<ParserTraits> {
Expression* ParseNewPrefix(PositionStack* stack, bool* ok);
Expression* ParseMemberWithNewPrefixesExpression(PositionStack* stack,
bool* ok);
Expression* ParsePrimaryExpression(bool* ok);
Expression* ParseArrayLiteral(bool* ok);
Expression* ParseObjectLiteral(bool* ok);
......@@ -735,7 +752,6 @@ class Parser : public ParserBase<ParserTraits> {
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
Target* target_stack_; // for break, continue statements
v8::Extension* extension_;
ScriptDataImpl* pre_parse_data_;
FuncNameInferrer* fni_;
......
......@@ -81,8 +81,7 @@ void PreParserTraits::ReportMessageAt(int start_pos,
}
PreParserIdentifier PreParserTraits::GetSymbol() {
Scanner* scanner = pre_parser_->scanner();
PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) {
pre_parser_->LogSymbol();
if (scanner->current_token() == Token::FUTURE_RESERVED_WORD) {
return PreParserIdentifier::FutureReserved();
......@@ -107,6 +106,42 @@ PreParserIdentifier PreParserTraits::GetSymbol() {
}
PreParserExpression PreParserTraits::ExpressionFromString(
int pos, Scanner* scanner, PreParserFactory* factory) {
const int kUseStrictLength = 10;
const char* kUseStrictChars = "use strict";
pre_parser_->LogSymbol();
if (scanner->is_literal_ascii() &&
scanner->literal_length() == kUseStrictLength &&
!scanner->literal_contains_escapes() &&
!strncmp(scanner->literal_ascii_string().start(), kUseStrictChars,
kUseStrictLength)) {
return PreParserExpression::UseStrictStringLiteral();
}
return PreParserExpression::StringLiteral();
}
PreParserExpression PreParserTraits::ParseArrayLiteral(bool* ok) {
return pre_parser_->ParseArrayLiteral(ok);
}
PreParserExpression PreParserTraits::ParseObjectLiteral(bool* ok) {
return pre_parser_->ParseObjectLiteral(ok);
}
PreParserExpression PreParserTraits::ParseExpression(bool accept_IN, bool* ok) {
return pre_parser_->ParseExpression(accept_IN, ok);
}
PreParserExpression PreParserTraits::ParseV8Intrinsic(bool* ok) {
return pre_parser_->ParseV8Intrinsic(ok);
}
PreParser::PreParseResult PreParser::PreParseLazyFunction(
LanguageMode mode, bool is_generator, ParserRecorder* log) {
log_ = log;
......@@ -1111,90 +1146,6 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
}
PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
// 'true'
// 'false'
// Identifier
// Number
// String
// ArrayLiteral
// ObjectLiteral
// RegExpLiteral
// '(' Expression ')'
Expression result = Expression::Default();
switch (peek()) {
case Token::THIS: {
Next();
result = Expression::This();
break;
}
case Token::FUTURE_RESERVED_WORD:
case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::YIELD:
case Token::IDENTIFIER: {
// Using eval or arguments in this context is OK even in strict mode.
Identifier id = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
result = Expression::FromIdentifier(id);
break;
}
case Token::NULL_LITERAL:
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
case Token::NUMBER: {
Next();
break;
}
case Token::STRING: {
Next();
result = GetStringSymbol();
break;
}
case Token::ASSIGN_DIV:
result = ParseRegExpLiteral(true, CHECK_OK);
break;
case Token::DIV:
result = ParseRegExpLiteral(false, CHECK_OK);
break;
case Token::LBRACK:
result = ParseArrayLiteral(CHECK_OK);
break;
case Token::LBRACE:
result = ParseObjectLiteral(CHECK_OK);
break;
case Token::LPAREN:
Consume(Token::LPAREN);
parenthesized_function_ = (peek() == Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
break;
case Token::MOD:
result = ParseV8Intrinsic(CHECK_OK);
break;
default: {
Token::Value next = Next();
ReportUnexpectedToken(next);
*ok = false;
return Expression::Default();
}
}
return result;
}
PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
......@@ -1266,7 +1217,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
case Token::STRING:
Consume(next);
checker.CheckProperty(next, kValueProperty, CHECK_OK);
GetStringSymbol();
LogSymbol();
break;
case Token::NUMBER:
Consume(next);
......@@ -1475,19 +1426,4 @@ void PreParser::LogSymbol() {
}
PreParser::Expression PreParser::GetStringSymbol() {
const int kUseStrictLength = 10;
const char* kUseStrictChars = "use strict";
LogSymbol();
if (scanner()->is_literal_ascii() &&
scanner()->literal_length() == kUseStrictLength &&
!scanner()->literal_contains_escapes() &&
!strncmp(scanner()->literal_ascii_string().start(), kUseStrictChars,
kUseStrictLength)) {
return Expression::UseStrictStringLiteral();
}
return Expression::StringLiteral();
}
} } // v8::internal
......@@ -32,6 +32,7 @@
#include "scopes.h"
#include "token.h"
#include "scanner.h"
#include "v8.h"
namespace v8 {
namespace internal {
......@@ -41,11 +42,13 @@ template <typename Traits>
class ParserBase : public Traits {
public:
ParserBase(Scanner* scanner, uintptr_t stack_limit,
v8::Extension* extension,
typename Traits::Type::Parser this_object)
: Traits(this_object),
parenthesized_function_(false),
scope_(NULL),
function_state_(NULL),
extension_(extension),
scanner_(scanner),
stack_limit_(stack_limit),
stack_overflow_(false),
......@@ -329,7 +332,9 @@ class ParserBase : public Traits {
bool* ok);
typename Traits::Type::Expression ParseRegExpLiteral(bool seen_equal,
bool* ok);
bool* ok);
typename Traits::Type::Expression ParsePrimaryExpression(bool* ok);
// Used to detect duplicates in object literals. Each of the values
// kGetterProperty, kSetterProperty and kValueProperty represents
......@@ -393,6 +398,7 @@ class ParserBase : public Traits {
typename Traits::Type::Scope* scope_; // Scope stack.
FunctionState* function_state_; // Function state stack.
v8::Extension* extension_;
private:
Scanner* scanner_;
......@@ -641,11 +647,39 @@ class PreParserTraits {
}
// Producing data during the recursive descent.
PreParserIdentifier GetSymbol();
static PreParserIdentifier NextLiteralString(PretenureFlag tenured) {
PreParserIdentifier GetSymbol(Scanner* scanner);
static PreParserIdentifier NextLiteralString(Scanner* scanner,
PretenureFlag tenured) {
return PreParserIdentifier::Default();
}
static PreParserExpression ThisExpression(PreParserScope* scope,
PreParserFactory* factory) {
return PreParserExpression::This();
}
static PreParserExpression ExpressionFromLiteral(
Token::Value token, int pos, Scanner* scanner,
PreParserFactory* factory) {
return PreParserExpression::Default();
}
static PreParserExpression ExpressionFromIdentifier(
PreParserIdentifier name, int pos, PreParserScope* scope,
PreParserFactory* factory) {
return PreParserExpression::FromIdentifier(name);
}
PreParserExpression ExpressionFromString(int pos,
Scanner* scanner,
PreParserFactory* factory = NULL);
// Temporary glue; these functions will move to ParserBase.
PreParserExpression ParseArrayLiteral(bool* ok);
PreParserExpression ParseObjectLiteral(bool* ok);
PreParserExpression ParseExpression(bool accept_IN, bool* ok);
PreParserExpression ParseV8Intrinsic(bool* ok);
private:
PreParser* pre_parser_;
};
......@@ -676,7 +710,7 @@ class PreParser : public ParserBase<PreParserTraits> {
PreParser(Scanner* scanner,
ParserRecorder* log,
uintptr_t stack_limit)
: ParserBase<PreParserTraits>(scanner, stack_limit, this),
: ParserBase<PreParserTraits>(scanner, stack_limit, NULL, this),
log_(log) {}
// Pre-parse the program from the character stream; returns true on
......@@ -823,7 +857,6 @@ class PreParser : public ParserBase<PreParserTraits> {
Expression ParseNewExpression(bool* ok);
Expression ParseMemberExpression(bool* ok);
Expression ParseMemberWithNewPrefixesExpression(unsigned new_count, bool* ok);
Expression ParsePrimaryExpression(bool* ok);
Expression ParseArrayLiteral(bool* ok);
Expression ParseObjectLiteral(bool* ok);
Expression ParseV8Intrinsic(bool* ok);
......@@ -922,7 +955,7 @@ typename Traits::Type::Identifier ParserBase<Traits>::ParseIdentifier(
bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER) {
typename Traits::Type::Identifier name = this->GetSymbol();
typename Traits::Type::Identifier name = this->GetSymbol(scanner());
if (allow_eval_or_arguments == kDontAllowEvalOrArguments &&
!is_classic_mode() && this->IsEvalOrArguments(name)) {
ReportMessageAt(scanner()->location(), "strict_eval_arguments");
......@@ -931,7 +964,7 @@ typename Traits::Type::Identifier ParserBase<Traits>::ParseIdentifier(
return name;
} else if (is_classic_mode() && (next == Token::FUTURE_STRICT_RESERVED_WORD ||
(next == Token::YIELD && !is_generator()))) {
return this->GetSymbol();
return this->GetSymbol(scanner());
} else {
this->ReportUnexpectedToken(next);
*ok = false;
......@@ -955,7 +988,7 @@ typename Traits::Type::Identifier ParserBase<
*ok = false;
return Traits::EmptyIdentifier();
}
return this->GetSymbol();
return this->GetSymbol(scanner());
}
......@@ -969,7 +1002,7 @@ typename Traits::Type::Identifier ParserBase<Traits>::ParseIdentifierName(
*ok = false;
return Traits::EmptyIdentifier();
}
return this->GetSymbol();
return this->GetSymbol(scanner());
}
......@@ -1004,7 +1037,7 @@ ParserBase<Traits>::ParseRegExpLiteral(bool seen_equal, bool* ok) {
int literal_index = function_state_->NextMaterializedLiteralIndex();
typename Traits::Type::Identifier js_pattern =
this->NextLiteralString(TENURED);
this->NextLiteralString(scanner(), TENURED);
if (!scanner()->ScanRegExpFlags()) {
Next();
ReportMessageAt(scanner()->location(), "invalid_regexp_flags");
......@@ -1012,12 +1045,115 @@ ParserBase<Traits>::ParseRegExpLiteral(bool seen_equal, bool* ok) {
return Traits::EmptyExpression();
}
typename Traits::Type::Identifier js_flags =
this->NextLiteralString(TENURED);
this->NextLiteralString(scanner(), TENURED);
Next();
return factory()->NewRegExpLiteral(js_pattern, js_flags, literal_index, pos);
}
#define CHECK_OK ok); \
if (!*ok) return this->EmptyExpression(); \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
template <class Traits>
typename Traits::Type::Expression ParserBase<Traits>::ParsePrimaryExpression(
bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
// 'true'
// 'false'
// Identifier
// Number
// String
// ArrayLiteral
// ObjectLiteral
// RegExpLiteral
// '(' Expression ')'
int pos = peek_position();
typename Traits::Type::Expression result = this->EmptyExpression();
Token::Value token = peek();
switch (token) {
case Token::THIS: {
Consume(Token::THIS);
result = this->ThisExpression(scope_, factory());
break;
}
case Token::NULL_LITERAL:
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
case Token::NUMBER:
Next();
result = this->ExpressionFromLiteral(token, pos, scanner(), factory());
break;
case Token::IDENTIFIER:
case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode.
typename Traits::Type::Identifier name =
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
result =
this->ExpressionFromIdentifier(name, pos, scope_, factory());
break;
}
case Token::STRING: {
Consume(Token::STRING);
result = this->ExpressionFromString(pos, scanner(), factory());
break;
}
case Token::ASSIGN_DIV:
result = this->ParseRegExpLiteral(true, CHECK_OK);
break;
case Token::DIV:
result = this->ParseRegExpLiteral(false, CHECK_OK);
break;
case Token::LBRACK:
result = this->ParseArrayLiteral(CHECK_OK);
break;
case Token::LBRACE:
result = this->ParseObjectLiteral(CHECK_OK);
break;
case Token::LPAREN:
Consume(Token::LPAREN);
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
parenthesized_function_ = (peek() == Token::FUNCTION);
result = this->ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
break;
case Token::MOD:
if (allow_natives_syntax() || extension_ != NULL) {
result = this->ParseV8Intrinsic(CHECK_OK);
break;
}
// If we're not allowing special syntax we fall-through to the
// default case.
default: {
Next();
ReportUnexpectedToken(token);
*ok = false;
}
}
return result;
}
#undef CHECK_OK
template <typename Traits>
void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty(
Token::Value property,
......
......@@ -1391,7 +1391,7 @@ void RunParserSyncTest(const char* context_data[][2],
static const ParserFlag flags[] = {
kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
kAllowForOf
kAllowForOf, kAllowNativesSyntax
};
for (int i = 0; context_data[i][0] != NULL; ++i) {
for (int j = 0; statement_data[j] != NULL; ++j) {
......@@ -2046,3 +2046,20 @@ TEST(NoErrorsRegexpLiteral) {
RunParserSyncTest(context_data, statement_data, kSuccess);
}
TEST(Intrinsics) {
const char* context_data[][2] = {
{"", ""},
{ NULL, NULL }
};
const char* statement_data[] = {
"%someintrinsic(arg)",
NULL
};
// Parsing will fail or succeed depending on whether we allow natives syntax
// or not.
RunParserSyncTest(context_data, statement_data, kSuccessOrError);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment