Bison BNFC生成的C解析器中的内存管理

Bison BNFC生成的C解析器中的内存管理,bison,flex-lexer,yacc,bnfc,Bison,Flex Lexer,Yacc,Bnfc,我使用BNFC生成解析器BNFC-m-c./mylang.cf。在内部,BNFC makefile调用bison来生成C解析器 Parser.c : mylang.y ${BISON} ${BISON_OPTS} mylang.y -o Parser.c 我可以通过调用下面生成的psProc方法成功解析源代码 /* Global variables holding parse results for entrypoints. */ Proc YY_RESULT_Proc_ = 0; /

我使用BNFC生成解析器
BNFC-m-c./mylang.cf
。在内部,BNFC makefile调用bison来生成C解析器

Parser.c : mylang.y
    ${BISON} ${BISON_OPTS} mylang.y -o Parser.c
我可以通过调用下面生成的
psProc
方法成功解析源代码

/* Global variables holding parse results for entrypoints. */
Proc YY_RESULT_Proc_ = 0;
// ...

/* Entrypoint: parse Proc from string. */
Proc psProc(const char *str)
{
  YY_BUFFER_STATE buf;
  mylang__init_lexer(0);
  buf = mylang__scan_string(str);
  int result = yyparse();
  mylang__delete_buffer(buf);
  if (result)
  { /* Failure */
    return 0;
  }
  else
  { /* Success */
    return YY_RESULT_Proc_;
  }
}

struct Proc_;
typedef struct Proc_ *Proc;

struct Proc_
{
  enum { is_PGround, is_PCollect, is_PVar, is_PVarRef, is_PNil, is_PSimpleType, is_PNegation, is_PConjunction, is_PDisjunction, is_PEval, is_PMethod, is_PExprs, is_PNot, is_PNeg, is_PMult, is_PDiv, is_PMod, is_PPercentPercent, is_PAdd, is_PMinus, is_PPlusPlus, is_PMinusMinus, is_PLt, is_PLte, is_PGt, is_PGte, is_PMatches, is_PEq, is_PNeq, is_PAnd, is_POr, is_PSend, is_PContr, is_PInput, is_PChoice, is_PMatch, is_PBundle, is_PIf, is_PIfElse, is_PNew, is_PPar } kind;
  union
  {
    struct { Ground ground_; } pground_;
    struct { Collection collection_; } pcollect_;
    struct { ProcVar procvar_; } pvar_;
    struct { Var var_; VarRefKind varrefkind_; } pvarref_;
    struct { SimpleType simpletype_; } psimpletype_;
    struct { Proc proc_; } pnegation_;
    struct { Proc proc_1, proc_2; } pconjunction_;
    struct { Proc proc_1, proc_2; } pdisjunction_;
    struct { Name name_; } peval_;
    struct { ListProc listproc_; Proc proc_; Var var_; } pmethod_;
    struct { Proc proc_; } pexprs_;
    struct { Proc proc_; } pnot_;
    struct { Proc proc_; } pneg_;
    struct { Proc proc_1, proc_2; } pmult_;
    struct { Proc proc_1, proc_2; } pdiv_;
    struct { Proc proc_1, proc_2; } pmod_;
    struct { Proc proc_1, proc_2; } ppercentpercent_;
    struct { Proc proc_1, proc_2; } padd_;
    struct { Proc proc_1, proc_2; } pminus_;
    struct { Proc proc_1, proc_2; } pplusplus_;
    struct { Proc proc_1, proc_2; } pminusminus_;
    struct { Proc proc_1, proc_2; } plt_;
    struct { Proc proc_1, proc_2; } plte_;
    struct { Proc proc_1, proc_2; } pgt_;
    struct { Proc proc_1, proc_2; } pgte_;
    struct { Proc proc_1, proc_2; } pmatches_;
    struct { Proc proc_1, proc_2; } peq_;
    struct { Proc proc_1, proc_2; } pneq_;
    struct { Proc proc_1, proc_2; } pand_;
    struct { Proc proc_1, proc_2; } por_;
    struct { ListProc listproc_; Name name_; Send send_; } psend_;
    struct { ListName listname_; Name name_; NameRemainder nameremainder_; Proc proc_; } pcontr_;
    struct { Proc proc_; Receipt receipt_; } pinput_;
    struct { ListBranch listbranch_; } pchoice_;
    struct { ListCase listcase_; Proc proc_; } pmatch_;
    struct { Bundle bundle_; Proc proc_; } pbundle_;
    struct { Proc proc_1, proc_2; } pif_;
    struct { Proc proc_1, proc_2, proc_3; } pifelse_;
    struct { ListNameDecl listnamedecl_; Proc proc_; } pnew_;
    struct { Proc proc_1, proc_2; } ppar_;
  } u;
};
关于
Proc psProc(const char*str)
,我有几个问题

  • 我能否在
    psProc
    返回后立即释放
    char*str
    参数引用的源缓冲区?我猜返回的
    Proc
    可能包含指向输入源缓冲区的指针,因此我应该确保源缓冲区的生存期长于返回的指针。对吗

  • 如何释放返回的
    Proc
    ?返回的
    Proc
    是指向
    Proc
    的指针,它通过指针组成一个抽象语法树。我只需要在返回的指针上调用
    free()
    一次就可以释放,对吗

  • Proc psProc(const char*str)
    的方法体中,它返回一个存储在全局变量
    YY\u RESULT\u Proc\中的指针。这是否意味着我不能从不同的线程同时调用
    psProc


  • 此类问题应在工具的文档中回答。但我在那里找不到它们:-(我对BNFC也没有太多经验,所以使用这个答案时要小心

  • psProc
    调用lexer的
    scan\u string
    接口,该接口复制提供的字符串。Flex喜欢在标记时修改输入,因此它只能通过复制来处理
    const char*
    输入。因此,在调用
    scan\u string
    后,可以立即释放字符串,但是
    psProc
    在返回之前解析整个输入,因此您不必这样做。当
    psProc
    返回时,您当然可以释放字符串

    我怀疑这是否对你来说是个问题,但是如果你打算在内存字符串中解析得非常大,你可能想考虑使用<代码> fMeMeuns(至少在POSIX平台上)。将字符串作为

    文件*
    打开,这不会避免复制,但它会以8k左右的块进行,从而避免在解析过程中保留整个字符串的两个副本

  • 我不知道BNFC希望您如何释放解析树节点(事实上,我怀疑它不希望您这样做)节点通过内部指针链接,当然可以编写一个AST walker,使用后序遍历递归释放所有节点。但我没有看到任何生成的代码可以这样做。也许我还没有仔细研究

    在顶级节点上调用
    free()
    将只释放一个节点。树的其余部分将被泄漏,因为不存在指向它的其他指针

  • 我很确定你对线程安全的怀疑是正确的。全局由
    Proc
    生产的reduce操作分配,然后由
    psProc
    返回。没有锁定,因此如果另一个线程中有另一个解析器,全局可能会被覆盖。全局只是指向n的指针ode将被返回,而节点本身应该是线程安全的,因为它是由解析器线程动态分配的。因此,您可能会将全局声明更改为使用线程本地存储,但这必须通过对生成的代码进行后处理来完成


  • 这些问题应该在该工具的文档中得到回答。但是我在那里找不到它们:-(我也没有很多BNFC的经验,所以在应用这个答案时要小心

  • psProc
    调用lexer的
    scan\u string
    接口,该接口复制提供的字符串。Flex喜欢在标记时修改输入,因此它只能通过复制来处理
    const char*
    输入。因此,在调用
    scan\u string
    后,可以立即释放字符串,但是
    psProc
    在返回之前解析整个输入,因此您不必这样做。当
    psProc
    返回时,您当然可以释放字符串

    我怀疑这是否对你来说是个问题,但是如果你打算在内存字符串中解析得非常大,你可能想考虑使用<代码> fMeMeuns(至少在POSIX平台上)。将字符串作为

    文件*
    打开,这不会避免复制,但它会以8k左右的块进行,从而避免在解析过程中保留整个字符串的两个副本

  • 我不知道BNFC希望您如何释放解析树节点(事实上,我怀疑它不希望您这样做)节点通过内部指针链接,当然可以编写一个AST walker,使用后序遍历递归释放所有节点。但我没有看到任何生成的代码可以这样做。也许我还没有仔细研究

    在顶级节点上调用
    free()
    将只释放一个节点。树的其余部分将被泄漏,因为不存在指向它的其他指针

  • 我很确定你对线程安全的怀疑是正确的。全局由
    Proc
    生产的reduce操作分配,然后由
    psProc
    返回。没有锁定,因此如果另一个线程中有另一个解析器,全局可能会被覆盖。全局只是指向n的指针ode将被返回,而节点本身应该是线程安全的,因为它是由解析器线程动态分配的。因此,您可能会将全局声明更改为使用线程本地存储,但这必须通过对生成的代码进行后处理来完成


  • 关于TLSIt的好观点很有趣,如果我不在返回点上调用
    free