2
\$\begingroup\$

Write a program that checks if a sentence is correct according to the “English” grammar in §6.4.1. Assume that every sentence is terminated by a full stop (.) surrounded by whitespace. For example, birds fly but the fish swim . is a sentence, but birds fly but the fish swim (terminating dot missing) and birds fly but the fish swim. (no space before dot) are not. For each sentence entered, the program should simply respond “OK” or “not OK.” Hint: Don’t bother with tokens; just read into a string using >>.

#include <string>
#include <iostream>
using namespace std;

string str;

void putback(string s) {
    str = s;
}

string verb() {
    if (str == "rules" || str == "fly" || str == "swim") {
        return str + ' ';
    }
    else 
        return "";
}

string noun() {
    if (str == "birds" || str == "fish" || str == "C++") {
        return str + ' ';
    }
    else 
        return "";
}

string article() {
    if (str == "the") {
        string temp = str;
        cin >> str;
        return temp + ' ';
    }
    else 
        return "";
}

string conjunction() {
    if (str == "and" || str == "or" || str == "but") {
        return str + ' ';
    }
    else 
        return "";
}

string l;
string sentence() {
    l += article();
    if (noun() == "") {
        cerr << "NOT OK";
        return "";
    }
    else {
        l += noun();
        cin >> str;
        string temp = verb();
        if (temp == "") {
            cerr << "NOT OK";
            return "";
        }
        else {
            l += temp;
            cin >> str;
            if (str == "") {
                cerr << "NOT OK";
                return " ";
            }
            else if (str == ".") {
                l += ".";
                return l;
            }
            else {
                if(conjunction() == "") {
                    cerr << "NOT OK";
                }
                else {
                    l += str + ' ';
                    cin >> str;
                }
                return sentence();
            }
        }
    }
}

int main() 
try 
{
    string sen = " ";
    while (cin) {
        cin >> str;
        sen = sentence();
        if (str == ".") {
            cout << sen << '\n';
        }
    }
    //keep_window_open();
}
catch (exception& e) 
{
    cerr << "error: " << e.what() << '\n';
    //keep_window_open();
    return 1;
}
catch (...) 
{
    cerr << "Oops: unknown exception!\n";
    //keep_window_open();
    return 2;
}
The Grammar:
Sentence:
          Noun Verb                                   // e.g., C++ rules
          Article Noun Verb
          Sentence Conjunction Sentence   // e.g., Birds fly but fish swim
Conjunction:
          "and"
          "or"
          "but"
Article:
          "the"
Noun:
          "birds"
          "fish"
          "C++"
Verb:
          "rules"
          "fly"
          "swim"

This is perfectly working code. Is there anything I can improve on for my code? Do you think using global variables in this case is okay? Even for whether I have proper variable names is worth commenting. This question is from this webpage, exercise 6 at the very bottom.

\$\endgroup\$
0
2
\$\begingroup\$

You could learn how to use bison. It designed specifically for this.

Boiler plate for flex/bison project:

Eng.l

%option c++

%{
#define IN_EXPR_LEXER
#include "EngYac.hpp"
%}

SpaceChar     [ \t]
Space         {SpaceChar}+
SpaceTerm     {SpaceChar}|<<EOF>>
FullStop      {SpaceChar}\.{SpaceTerm}

%%

and                     {return yy::parser::token::Conjunction;}
or                      {return yy::parser::token::Conjunction;}
but                     {return yy::parser::token::Conjunction;}

the                     {return yy::parser::token::Article;}

birds                   {return yy::parser::token::Noun;}
fish                    {return yy::parser::token::Noun;}
C\+\+                   {return yy::parser::token::Noun;}

rules                   {return yy::parser::token::Verb;}
fly                     {return yy::parser::token::Verb;}
swim                    {return yy::parser::token::Verb;}

{FullStop}              {return yy::parser::token::FullStop;}

{Space}                 {/*Ignore*/}
.                       {std::cerr << "Error unknown character\n";}

%%

int yyFlexLexer::yywrap(void)
{
    return 1;
}


int yylex(int* /*type*/, yyFlexLexer& lexer)
{
    return lexer.yylex();
}

Eng.y

%{
    #ifndef IN_EXPR_LEXER
    #include <FlexLexer.h>
    #endif
    int yylex(int* type, yyFlexLexer& lexer);
%}
%skeleton "lalr1.cc"
%defines
%parse-param {yyFlexLexer& lexer}
%lex-param   {yyFlexLexer& lexer}

%token      Noun
%token      Verb
%token      Article
%token      Conjunction
%token      FullStop

%%
Input:      Sentence FullStop    

Sentence:   Noun Verb
        |   Article Noun Verb
        |   Sentence Conjunction Sentence


%%

void yy::parser::error(yy::location const&, std::string const& msg)
{
    std::cerr << "Error: " << msg << "\n";
}

main.cpp

#include <FlexLexer.h>
#include "EngYac.hpp"

int main()
{
    yyFlexLexer     lexer(&std::cin, &std::cout);
    yy::parser      parser(lexer);

    std::cout << (parser.parse() ? "FAIL" : "OK") << "\n";
}

Makefile

FLEX_SRC    = $(wildcard *.l)
BISON_SRC   = $(wildcard *.y)
FLEX_CPP    = $(patsubst %.l, %Lex.cpp, $(FLEX_SRC))
BISON_CPP   = $(patsubst %.y, %Yac.cpp, $(BISON_SRC))

SRC         = $(filter-out $(BISON_CPP), $(filter-out $(FLEX_CPP), $(wildcard *.cpp))) $(FLEX_CPP) $(BISON_CPP)
OBJ         = $(patsubst %.cpp,%.o,$(SRC))


eng:       $(OBJ)
    g++ -o eng -O3 -Wall -Wextra -Werror $(OBJ)

clean:
    rm EngLex.cpp EngYac.cpp EngYac.hpp location.hh position.hh stack.hh $(OBJ) expr


$(OBJ): $(FLEX_CPP) $(BISON_CPP)

%Lex.cpp:       %.l
    flex -o $*Lex.cpp $^

%Yac.cpp:       %.y
    bison -o $*Yac.cpp $^

Usage:

> make
> echo "birds rules but fish swim." | ./eng

OK
> echo "A test of English." | ./eng 
A test of English
Error: syntax error
FAIL
\$\endgroup\$
5
  • \$\begingroup\$ What about the "no dot before the spaces" part from the task? \$\endgroup\$ Jun 6 '17 at 6:03
  • \$\begingroup\$ @RolandIllig: The dot surrounded by space is to allow for easy parsing with operator>>. Its easy to do if we want to be perfect. \$\endgroup\$ Jun 6 '17 at 8:05
  • 1
    \$\begingroup\$ @RolandIllig: But I was bored. So updated. \$\endgroup\$ Jun 6 '17 at 8:14
  • \$\begingroup\$ Thanks. I was just curious how you would implement it, since it is a non-trivial problem with many solutions that work almost always. :) \$\endgroup\$ Jun 6 '17 at 22:32
  • \$\begingroup\$ @RolandIllig I dislike the solution because it does not reflect real life usage. In real sentences punctuation is usually not separated from the word by a space. So I personally think the specification is wrong. But we code to the specifications. :-) \$\endgroup\$ Jun 7 '17 at 8:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.