Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ea869b0 authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Remove malloc in edify functions"

parents d882b889 c4447325
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -27,18 +27,19 @@
#include <errno.h>
#include <stdio.h>

#include <memory>
#include <string>

#include <android-base/file.h>

#include "expr.h"

static void ExprDump(int depth, const Expr* n, const std::string& script) {
static void ExprDump(int depth, const std::unique_ptr<Expr>& n, const std::string& script) {
    printf("%*s", depth*2, "");
    printf("%s %p (%d-%d) \"%s\"\n",
           n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
           n->name.c_str(), n->fn, n->start, n->end,
           script.substr(n->start, n->end - n->start).c_str());
    for (int i = 0; i < n->argc; ++i) {
    for (size_t i = 0; i < n->argv.size(); ++i) {
        ExprDump(depth+1, n->argv[i], script);
    }
}
@@ -57,7 +58,7 @@ int main(int argc, char** argv) {
        return 1;
    }

    Expr* root;
    std::unique_ptr<Expr> root;
    int error_count = 0;
    int error = parse_string(buffer.data(), &root, &error_count);
    printf("parse returned %d; %d errors encountered\n", error, error_count);
+75 −44
Original line number Diff line number Diff line
@@ -40,12 +40,12 @@ static bool BooleanString(const std::string& s) {
    return !s.empty();
}

bool Evaluate(State* state, Expr* expr, std::string* result) {
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
    if (result == nullptr) {
        return false;
    }

    std::unique_ptr<Value> v(expr->fn(expr->name, state, expr->argc, expr->argv));
    std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
    if (!v) {
        return false;
    }
@@ -58,8 +58,8 @@ bool Evaluate(State* state, Expr* expr, std::string* result) {
    return true;
}

Value* EvaluateValue(State* state, Expr* expr) {
    return expr->fn(expr->name, state, expr->argc, expr->argv);
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
    return expr->fn(expr->name.c_str(), state, expr->argv);
}

Value* StringValue(const char* str) {
@@ -73,12 +73,12 @@ Value* StringValue(const std::string& str) {
    return StringValue(str.c_str());
}

Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc == 0) {
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    if (argv.empty()) {
        return StringValue("");
    }
    std::string result;
    for (int i = 0; i < argc; ++i) {
    for (size_t i = 0; i < argv.size(); ++i) {
        std::string str;
        if (!Evaluate(state, argv[i], &str)) {
            return nullptr;
@@ -89,8 +89,8 @@ Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
    return StringValue(result);
}

Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc != 2 && argc != 3) {
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    if (argv.size() != 2 && argv.size() != 3) {
        state->errmsg = "ifelse expects 2 or 3 arguments";
        return nullptr;
    }
@@ -102,16 +102,16 @@ Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {

    if (!cond.empty()) {
        return EvaluateValue(state, argv[1]);
    } else if (argc == 3) {
    } else if (argv.size() == 3) {
        return EvaluateValue(state, argv[2]);
    }

    return StringValue("");
}

Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string msg;
    if (argc > 0 && Evaluate(state, argv[0], &msg)) {
    if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
        state->errmsg = msg;
    } else {
        state->errmsg = "called abort()";
@@ -119,8 +119,8 @@ Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
    return nullptr;
}

Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
    for (int i = 0; i < argc; ++i) {
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    for (size_t i = 0; i < argv.size(); ++i) {
        std::string result;
        if (!Evaluate(state, argv[i], &result)) {
            return nullptr;
@@ -134,7 +134,7 @@ Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
    return StringValue("");
}

Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string val;
    if (!Evaluate(state, argv[0], &val)) {
        return nullptr;
@@ -149,8 +149,8 @@ Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
    return StringValue(val);
}

Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
    for (int i = 0; i < argc; ++i) {
Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    for (size_t i = 0; i < argv.size(); ++i) {
        std::string v;
        if (!Evaluate(state, argv[i], &v)) {
            return nullptr;
@@ -161,7 +161,7 @@ Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
}

Value* LogicalAndFn(const char* name, State* state,
                   int argc, Expr* argv[]) {
                    const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string left;
    if (!Evaluate(state, argv[0], &left)) {
        return nullptr;
@@ -174,7 +174,7 @@ Value* LogicalAndFn(const char* name, State* state,
}

Value* LogicalOrFn(const char* name, State* state,
                   int argc, Expr* argv[]) {
                   const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string left;
    if (!Evaluate(state, argv[0], &left)) {
        return nullptr;
@@ -187,7 +187,7 @@ Value* LogicalOrFn(const char* name, State* state,
}

Value* LogicalNotFn(const char* name, State* state,
                    int argc, Expr* argv[]) {
                    const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string val;
    if (!Evaluate(state, argv[0], &val)) {
        return nullptr;
@@ -197,7 +197,7 @@ Value* LogicalNotFn(const char* name, State* state,
}

Value* SubstringFn(const char* name, State* state,
                   int argc, Expr* argv[]) {
                   const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string needle;
    if (!Evaluate(state, argv[0], &needle)) {
        return nullptr;
@@ -212,7 +212,7 @@ Value* SubstringFn(const char* name, State* state,
    return StringValue(result);
}

Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string left;
    if (!Evaluate(state, argv[0], &left)) {
        return nullptr;
@@ -226,7 +226,8 @@ Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
    return StringValue(result);
}

Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* InequalityFn(const char* name, State* state,
                    const std::vector<std::unique_ptr<Expr>>& argv) {
    std::string left;
    if (!Evaluate(state, argv[0], &left)) {
        return nullptr;
@@ -240,7 +241,7 @@ Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
    return StringValue(result);
}

Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
    if (!left) {
        return nullptr;
@@ -248,14 +249,15 @@ Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
    return EvaluateValue(state, argv[1]);
}

Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc != 2) {
Value* LessThanIntFn(const char* name, State* state,
                     const std::vector<std::unique_ptr<Expr>>& argv) {
    if (argv.size() != 2) {
        state->errmsg = "less_than_int expects 2 arguments";
        return nullptr;
    }

    std::vector<std::string> args;
    if (!ReadArgs(state, 2, argv, &args)) {
    if (!ReadArgs(state, argv, &args)) {
        return nullptr;
    }

@@ -276,20 +278,34 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
}

Value* GreaterThanIntFn(const char* name, State* state,
                        int argc, Expr* argv[]) {
    if (argc != 2) {
                        const std::vector<std::unique_ptr<Expr>>& argv) {
    if (argv.size() != 2) {
        state->errmsg = "greater_than_int expects 2 arguments";
        return nullptr;
    }

    Expr* temp[2];
    temp[0] = argv[1];
    temp[1] = argv[0];
    std::vector<std::string> args;
    if (!ReadArgs(state, argv, &args)) {
        return nullptr;
    }

    // Parse up to at least long long or 64-bit integers.
    int64_t l_int;
    if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
        state->errmsg = "failed to parse int in " + args[0];
        return nullptr;
    }

    int64_t r_int;
    if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
        state->errmsg = "failed to parse int in " + args[1];
        return nullptr;
    }

    return LessThanIntFn(name, state, 2, temp);
    return StringValue(l_int > r_int ? "t" : "");
}

Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
    return StringValue(name);
}

@@ -329,14 +345,22 @@ void RegisterBuiltins() {
//   convenience methods for functions
// -----------------------------------------------------------------

// Evaluate the expressions in argv, and put the results of strings in
// args. If any expression evaluates to nullptr, free the rest and return
// false. Return true on success.
bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* args) {
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
// evaluates to nullptr, return false. Return true on success.
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
              std::vector<std::string>* args) {
    return ReadArgs(state, argv, args, 0, argv.size());
}

bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
              std::vector<std::string>* args, size_t start, size_t len) {
    if (args == nullptr) {
        return false;
    }
    for (int i = 0; i < argc; ++i) {
    if (len == 0 || start + len > argv.size()) {
        return false;
    }
    for (size_t i = start; i < start + len; ++i) {
        std::string var;
        if (!Evaluate(state, argv[i], &var)) {
            args->clear();
@@ -347,15 +371,22 @@ bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* ar
    return true;
}

// Evaluate the expressions in argv, and put the results of Value* in
// args. If any expression evaluate to nullptr, free the rest and return
// false. Return true on success.
bool ReadValueArgs(State* state, int argc, Expr* argv[],
// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
// evaluate to nullptr, return false. Return true on success.
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
                   std::vector<std::unique_ptr<Value>>* args) {
    return ReadValueArgs(state, argv, args, 0, argv.size());
}

bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
                   std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
    if (args == nullptr) {
        return false;
    }
    for (int i = 0; i < argc; ++i) {
    if (len == 0 || start + len > argv.size()) {
        return false;
    }
    for (size_t i = start; i < start + len; ++i) {
        std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
        if (!v) {
            args->clear();
+46 −37
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@
#define _EXPRESSION_H

#include <unistd.h>

#include <memory>
#include <string>
#include <vector>

#include "error_code.h"

@@ -65,47 +68,49 @@ struct Value {

struct Expr;

using Function = Value* (*)(const char* name, State* state, int argc, Expr* argv[]);
using Function = Value* (*)(const char* name, State* state,
                            const std::vector<std::unique_ptr<Expr>>& argv);

struct Expr {
  Function fn;
    const char* name;
    int argc;
    Expr** argv;
  std::string name;
  std::vector<std::unique_ptr<Expr>> argv;
  int start, end;

  Expr(Function fn, const std::string& name, int start, int end) :
    fn(fn),
    name(name),
    start(start),
    end(end) {}
};

// Take one of the Expr*s passed to the function as an argument,
// evaluate it, return the resulting Value.  The caller takes
// ownership of the returned Value.
Value* EvaluateValue(State* state, Expr* expr);
// Evaluate the input expr, return the resulting Value.
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);

// Take one of the Expr*s passed to the function as an argument,
// evaluate it, assert that it is a string, and update the result
// parameter. This function returns true if the evaluation succeeds.
// This is a convenience function for older functions that want to
// deal only with strings.
bool Evaluate(State* state, Expr* expr, std::string* result);
// Evaluate the input expr, assert that it is a string, and update the result parameter. This
// function returns true if the evaluation succeeds. This is a convenience function for older
// functions that want to deal only with strings.
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);

// Glue to make an Expr out of a literal.
Value* Literal(const char* name, State* state, int argc, Expr* argv[]);
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);

// Functions corresponding to various syntactic sugar operators.
// ("concat" is also available as a builtin function, to concatenate
// more than two strings.)
Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);

// Global builtins, registered by RegisterBuiltins().
Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);

// Register a new function.  The same Function may be registered under
// multiple names, but a given name should only be used once.
@@ -120,15 +125,19 @@ Function FindFunction(const std::string& name);

// --- convenience functions for use in functions ---

// Evaluate the expressions in argv, and put the results of strings in
// args. If any expression evaluates to nullptr, free the rest and return
// false. Return true on success.
bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* args);
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
// evaluates to nullptr, return false. Return true on success.
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
              std::vector<std::string>* args);
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
              std::vector<std::string>* args, size_t start, size_t len);

// Evaluate the expressions in argv, and put the results of Value* in
// args. If any expression evaluate to nullptr, free the rest and return
// false. Return true on success.
bool ReadValueArgs(State* state, int argc, Expr* argv[], std::vector<std::unique_ptr<Value>>* args);
// Evaluate the expressions in argv, and put the results of Value* in args. If any
// expression evaluate to nullptr, return false. Return true on success.
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
                   std::vector<std::unique_ptr<Value>>* args);
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
                   std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);

// Use printf-style arguments to compose an error message to put into
// *state.  Returns NULL.
@@ -145,6 +154,6 @@ Value* StringValue(const char* str);

Value* StringValue(const std::string& str);

int parse_string(const char* str, Expr** root, int* error_count);
int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);

#endif  // _EXPRESSION_H
+27 −44
Original line number Diff line number Diff line
@@ -19,6 +19,10 @@
#include <stdlib.h>
#include <string.h>

#include <memory>
#include <string>
#include <vector>

#include "expr.h"
#include "yydefs.h"
#include "parser.h"
@@ -26,8 +30,8 @@
extern int gLine;
extern int gColumn;

void yyerror(Expr** root, int* error_count, const char* s);
int yyparse(Expr** root, int* error_count);
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
int yyparse(std::unique_ptr<Expr>* root, int* error_count);

struct yy_buffer_state;
void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
@@ -38,17 +42,11 @@ struct yy_buffer_state* yy_scan_string(const char* yystr);
static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
    va_list v;
    va_start(v, count);
    Expr* e = static_cast<Expr*>(malloc(sizeof(Expr)));
    e->fn = fn;
    e->name = "(operator)";
    e->argc = count;
    e->argv = static_cast<Expr**>(malloc(count * sizeof(Expr*)));
    Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
    for (size_t i = 0; i < count; ++i) {
        e->argv[i] = va_arg(v, Expr*);
        e->argv.emplace_back(va_arg(v, Expr*));
    }
    va_end(v);
    e->start = loc.start;
    e->end = loc.end;
    return e;
}

@@ -59,10 +57,7 @@ static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
%union {
    char* str;
    Expr* expr;
    struct {
        int argc;
        Expr** argv;
    } args;
    std::vector<std::unique_ptr<Expr>>* args;
}

%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
@@ -70,7 +65,10 @@ static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
%type <expr> expr
%type <args> arglist

%parse-param {Expr** root}
%destructor { delete $$; } expr
%destructor { delete $$; } arglist

%parse-param {std::unique_ptr<Expr>* root}
%parse-param {int* error_count}
%error-verbose

@@ -85,17 +83,11 @@ static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {

%%

input:  expr           { *root = $1; }
input:  expr           { root->reset($1); }
;

expr:  STRING {
    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
    $$->fn = Literal;
    $$->name = $1;
    $$->argc = 0;
    $$->argv = NULL;
    $$->start = @$.start;
    $$->end = @$.end;
    $$ = new Expr(Literal, $1, @$.start, @$.end);
}
|  '(' expr ')'                      { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
|  expr ';'                          { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
@@ -110,41 +102,32 @@ expr: STRING {
|  IF expr THEN expr ENDIF           { $$ = Build(IfElseFn, @$, 2, $2, $4); }
|  IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
| STRING '(' arglist ')' {
    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
    $$->fn = FindFunction($1);
    if ($$->fn == nullptr) {
        char buffer[256];
        snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
        yyerror(root, error_count, buffer);
    Function fn = FindFunction($1);
    if (fn == nullptr) {
        std::string msg = "unknown function \"" + std::string($1) + "\"";
        yyerror(root, error_count, msg.c_str());
        YYERROR;
    }
    $$->name = $1;
    $$->argc = $3.argc;
    $$->argv = $3.argv;
    $$->start = @$.start;
    $$->end = @$.end;
    $$ = new Expr(fn, $1, @$.start, @$.end);
    $$->argv = std::move(*$3);
}
;

arglist:    /* empty */ {
    $$.argc = 0;
    $$.argv = NULL;
    $$ = new std::vector<std::unique_ptr<Expr>>;
}
| expr {
    $$.argc = 1;
    $$.argv = static_cast<Expr**>(malloc(sizeof(Expr*)));
    $$.argv[0] = $1;
    $$ = new std::vector<std::unique_ptr<Expr>>;
    $$->emplace_back($1);
}
| arglist ',' expr {
    $$.argc = $1.argc + 1;
    $$.argv = static_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
    $$.argv[$$.argc-1] = $3;
    $$->push_back(std::unique_ptr<Expr>($3));
}
;

%%

void yyerror(Expr** root, int* error_count, const char* s) {
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
  if (strlen(s) == 0) {
    s = "syntax error";
  }
@@ -152,7 +135,7 @@ void yyerror(Expr** root, int* error_count, const char* s) {
  ++*error_count;
}

int parse_string(const char* str, Expr** root, int* error_count) {
int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count) {
    yy_switch_to_buffer(yy_scan_string(str));
    return yyparse(root, error_count);
}
+3 −2
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <memory>
#include <string>

#include <gtest/gtest.h>
@@ -21,7 +22,7 @@
#include "edify/expr.h"

static void expect(const char* expr_str, const char* expected) {
    Expr* e;
    std::unique_ptr<Expr> e;
    int error_count = 0;
    EXPECT_EQ(0, parse_string(expr_str, &e, &error_count));
    EXPECT_EQ(0, error_count);
@@ -152,7 +153,7 @@ TEST_F(EdifyTest, big_string) {
TEST_F(EdifyTest, unknown_function) {
    // unknown function
    const char* script1 = "unknown_function()";
    Expr* expr;
    std::unique_ptr<Expr> expr;
    int error_count = 0;
    EXPECT_EQ(1, parse_string(script1, &expr, &error_count));
    EXPECT_EQ(1, error_count);
Loading