Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/164.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何基于CFG验证输入?_C++_Algorithm_Parsing_Context Free Grammar_Compiler Theory - Fatal编程技术网

C++ 如何基于CFG验证输入?

C++ 如何基于CFG验证输入?,c++,algorithm,parsing,context-free-grammar,compiler-theory,C++,Algorithm,Parsing,Context Free Grammar,Compiler Theory,考虑一下这种语法: expr ::= LP expr RP | expr PLUS|MINUS expr | expr STAR|SLASH expr | term term ::= INTEGER|FLOAT 上下文无关语法定义为G=(V,∑,R,S),其中(在本例中): 现在,让我们定义一个名为Parser的小类,它的定义是(代码示例在C++中提供): 出于测试目的,字符串(2+3)*4已标记为以下集合 { TK_LP, TK_INT

考虑一下这种语法:

expr ::= LP expr RP
         | expr PLUS|MINUS expr
         | expr STAR|SLASH expr
         | term

term ::= INTEGER|FLOAT
上下文无关语法定义为
G=(V,∑,R,S)
,其中(在本例中):

现在,让我们定义一个名为
Parser
的小类,它的定义是(代码示例在C++中提供):

出于测试目的,字符串
(2+3)*4已标记为以下集合

{ TK_LP, TK_INTEGER, TK_PLUS, TK_INTEGER, TK_RP, TK_STAR, TK_INTEGER }
并被用作
解析器的输入数据

现在来看最难的部分:算法。据我所知,我在想:

1) 从起始符号向量(
LP expr RP
)中获取第一条规则,并将其拆分为单词

2) 检查规则中的第一个单词是否为terminal

  • 如果单词是终端,则将其与第一个标记进行比较。
    • 若它们相等,则增加标记索引并移动到规则中的下一个单词
    • 如果它们不相等,则保留标记索引并移动到下一个规则
  • 如果单词不是终端,并且在以前的递归中没有使用,则增加标记索引并进入递归解析(传递新规则和非终端单词)
  • 虽然我对该算法不确定,但我仍然尝试实现它(当然,不成功):

    1) Outter
    Parse
    启动递归的函数:

    void Parser::Parse()
    {
        int startingTokenIndex = 0;
        string word = this->startingSymbol;
        for (int ruleIndex = 0; ruleIndex < this->ruleNames[word].size(); ruleIndex++)
        {
            this->parseRecursive(this->ruleNames[word], ruleIndex, startingTokenIndex, "");
        }
    }
    
    S -> As
    s -> + As | epsilon
    A -> a | b
    
    void解析器::Parse()
    {
    int startingTokenIndex=0;
    字符串字=此->开始符号;
    对于(int ruleIndex=0;ruleIndexruleNames[word].size();ruleIndex++)
    {
    this->parseRecursive(this->ruleNames[word],ruleIndex,startingTokenIndex,”);
    }
    }
    
    2) 递归函数:

    void Parser::parseRecursive(vector<string> rules, unsigned ruleIndex, unsigned startingTokenIndex, string prevRule)
    {
        printf("%s - %s\n", rules[ruleIndex].c_str(), this->tokenNames[this->tokenList[startingTokenIndex]].c_str());
        vector<string> temp = this->split(rules[ruleIndex], ' ');
        vector<vector<string>> ruleWords;
        bool breakOutter = false;
    
        for (unsigned wordIndex = 0; wordIndex < temp.size(); wordIndex++)
        {
            ruleWords.push_back(this->split(temp[wordIndex], '|'));
        }
    
        for (unsigned wordIndex = 0; wordIndex < ruleWords.size(); wordIndex++)
        {
            breakOutter = false;
            for (unsigned subWordIndex = 0; subWordIndex < ruleWords[wordIndex].size(); subWordIndex++)
            {
                string word = ruleWords[wordIndex][subWordIndex];
                if (this->isTerm(word))
                {
                    if (this->tokenNames[this->tokenList[startingTokenIndex]] == this->makeToken(word))
                    {
                        printf("%s ", word.c_str());
                        startingTokenIndex++;
                    } else {
                        breakOutter = true;
                        break;
                    }
                } else {
                    if (prevRule != word)
                    {
                        startingTokenIndex++;
                        this->parseRecursive(this->ruleNames[word], 0, startingTokenIndex, word);
                        prevRule = word;
                    }
                }
            }
    
            if (breakOutter)
                break;
        }
    }
    
    void Parser::parseRecursive(向量规则、无符号规则索引、无符号启动令牌索引、字符串规则)
    {
    printf(“%s-%s\n”,规则[ruleIndex].c_str(),this->tokenNames[this->tokenList[startingTokenIndex]].c_str());
    向量温度=此->拆分(规则[ruleIndex],“”);
    矢量规则词;
    bool breakouter=错误;
    对于(无符号wordIndex=0;wordIndex拆分(临时[wordIndex],“|”);
    }
    for(无符号wordIndex=0;wordIndexisTerm(字))
    {
    如果(this->tokenNames[this->tokenList[startingTokenIndex]]==this->makeToken(word))
    {
    printf(“%s”,word.c_str());
    startingTokenIndex++;
    }否则{
    breakouter=true;
    打破
    }
    }否则{
    if(prevRule!=word)
    {
    startingTokenIndex++;
    this->parseRecursive(this->ruleNames[word],0,startingTokenIndex,word);
    规则=单词;
    }
    }
    }
    如果(断接器)
    打破
    }
    }
    

    我应该对算法进行哪些更改才能使其正常工作?

    根据您想要实现的一次性解析器或编译器,会使用不同的方法。对于编译器,编译器主要使用LR,用于手动实现LL。 基本上,对于LL,手动实现使用递归下降(对于每个非终端,创建一个实现它的函数)。 例如,对于语法:

    S -> S + A | A
    A -> a | b
    
    让我们消除左递归和左因式分解(LL语法不适用于左递归):

    这种实施是可能的:

    void S (void)
    {
        A ();
        s ();
    }
    void s (void)
    {
        if (get_next_token (). value! = '+')
            return;
        A ();
        s ();
    }
    void A (void)
    {
        token * tok = get_next_token ();
        if (tok.value! = 'a' && tok.value! = 'b')
                syntax_error ();
    }
    
    如果要添加SDD,则继承的属性将通过参数传递,合成的属性将作为输出值传递

    评论:
    不要一次收集所有令牌,根据需要获取它们:获取下一个令牌()。

    根据要实现一次性解析器或编译器的内容,使用不同的方法。对于编译器,编译器主要使用LR,用于手动实现LL。 基本上,对于LL,手动实现使用递归下降(对于每个非终端,创建一个实现它的函数)。 例如,对于语法:

    S -> S + A | A
    A -> a | b
    
    让我们消除左递归和左因式分解(LL语法不适用于左递归):

    这种实施是可能的:

    void S (void)
    {
        A ();
        s ();
    }
    void s (void)
    {
        if (get_next_token (). value! = '+')
            return;
        A ();
        s ();
    }
    void A (void)
    {
        token * tok = get_next_token ();
        if (tok.value! = 'a' && tok.value! = 'b')
                syntax_error ();
    }
    
    如果要添加SDD,则继承的属性将通过参数传递,合成的属性将作为输出值传递

    评论:
    不要一次收集所有代币,根据需要获取:获取下一个代币()。

    你是想实现你自己的bison/flex还是我遗漏了什么?@W.F.我会说“有点”。然而,AFAIK bison基于语法生成C代码,而我希望保留语法并使用规则进行解析。我担心这可能会涉及stackoverflow…,也称为“龙书”的广泛主题。关于这个主题的经典文本。似乎您需要一个自上而下的解析器()。因此,尝试将解析函数拆分为更小的部分。parsexpr(),ParseInt()。然后,每个函数都可以专用于解析单个规则。看看llvm教程,他们还使用自顶向下的解析器从头开始构建了一个编译器:你是在尝试实现你自己的bison/flex还是我遗漏了什么?@W.F.我会说“有点”。然而,AFAIK bison基于语法生成C代码,而我希望保留语法并使用规则进行解析。我担心这可能会涉及stackoverflow…,也称为“龙书”的广泛主题。关于这个主题的经典文本。似乎您需要一个自上而下的解析器()。因此,尝试将解析函数拆分为更小的部分。parsexpr(),ParseInt()。然后,每个函数都可以专用于解析单个规则。请看llvm教程,其中他们还使用自顶向下的解析器从头构建了编译器:
    void S (void)
    {
        A ();
        s ();
    }
    void s (void)
    {
        if (get_next_token (). value! = '+')
            return;
        A ();
        s ();
    }
    void A (void)
    {
        token * tok = get_next_token ();
        if (tok.value! = 'a' && tok.value! = 'b')
                syntax_error ();
    }