Question
Makefile: BIN = ./wish OBJS = wish.o wish_read.o wish_parse.o wish_yyparser.tab.o lex.yy.o CFLAGS = -Wall -pedantic -Wextra -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs
Makefile:
BIN = ./wish
OBJS = wish.o wish_read.o wish_parse.o \
wish_yyparser.tab.o lex.yy.o
CFLAGS = -Wall -pedantic -Wextra -Wcast-align -Wcast-qual \
-Wdisabled-optimization -Wformat=2 -Winit-self \
-Wmissing-include-dirs -Wredundant-decls -Wshadow \
-Wstrict-overflow=5 -Wundef -Werror -Wno-unused \
-g -Wno-unused-result -O3 -Wlogical-op -Wno-strict-overflow
all: $(BIN)
$(BIN): $(OBJS)
$(CC) $(OBJS) -o $(BIN) -lreadline #-lefence
wish.o: wish.c wish.h
wish_read.o: wish_read.c wish.h
wish_parse.o: wish_yyparser.tab.h wish.h
wish_yyparser.tab.c wish_yyparser.tab.h: wish_yyparser.y
bison -d wish_yyparser.y
wish_yyparser.tab.o: wish_yyparser.tab.c wish.h
lex.yy.o: lex.yy.c wish.h wish_yyparser.tab.h
lex.yy.c: wish_yylexer.l
flex -I wish_yylexer.l
clean:
rm -f $(OBJS) $(BIN) lex.yy.c\
wish_yyparser.tab.c wish_yyparser.tab.h
valgrind: all
clear
valgrind --leak-check=full $(BIN)
wish_parse.c:
#include
#include "wish.h"
// https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences
char *wish_unquote(char * s) {
return s; // MODIFY!
}
// Do not modify this function
void yyerror(const char* s) {
fprintf(stderr, "Parse error: %s ", s);
}
char *wish_safe_getenv(char *s) {
return s;
}
wish_read.c:
#include
#include
#include
#include "wish.h"
char *wish_read_line(FILE *in) {
char buffer[WISH_MAX_INPUT + 2] = ""; // truncate the buffer
// Get a string and check its length
fgets(buffer, WISH_MAX_INPUT + 2, in);
if(strlen(buffer) > WISH_MAX_INPUT) {
fputs("wish: line too long ", stderr);
// Clean the rest of the line
int c = fgetc(in);
while (c != ' ' && c != EOF)
c = fgetc(in);
return NULL;
}
// Trim the line
strtok(buffer, " ");
// Check the line for being blank
for(size_t i = 0; i
if(!isspace(buffer[i])) {
// Alloate memory
char *line = malloc(strlen(buffer) + 1);
if (!line) // Too bad
abort();
strcat(line, buffer);
return line;
}
}
return NULL;
}
int wish_read_config(char *fname, int ok_if_missing) {
FILE *config;
// Check if the file exists
if(!(config = fopen(fname, "r"))) {
if (ok_if_missing)
return 0;
// Report missing
perror(fname);
return 1;
}
// Read the file line by line
while(!feof(config)) {
char *line = wish_read_line(config);
if(line) {
#ifdef DEBUG
fprintf(stderr, "%s ", line); // Only for debugging
#endif
free(line);
}
}
fclose(config);
return 0;
}
wish_yylexer.l:
%{
#include "wish.h"
#include "wish_yyparser.tab.h"
#ifdef __APPLE__
extern int yylex(void);
extern int yyparse(void);
#endif
char *wish_safe_getenv(char *s);
char *wish_unquote(char *s);
%}
%option noyywrap
digit [0-9]
letter [a-zA-Z_]
alnum {digit}|{letter}
string \"([^ \\\"]|\\.)*\"
quoted_string \'([^ \\\']|\\.)*\'
permitted {digit}|{letter}|[-%@_+\[\]\\/,\.:]
unsupported [;()*^?~{}`]
%%
"#".*" "? { /* comment */ }
exit { return YY_EXIT; }
pwd { return YY_PWD; }
cd { return YY_CD; }
\${alnum}+ { yylval.s = strdup(wish_safe_getenv(yytext + 1));
return YY_TOK; }
{permitted}+ { yylval.s = strdup(wish_unquote(yytext));
return YY_TOK; }
{quoted_string} { yytext[strlen(yytext) - 1] = 0;
yylval.s = strdup(yytext + 1);
return YY_TOK; }
{string} { yytext[strlen(yytext) - 1] = 0;
yylval.s = strdup(wish_unquote(yytext + 1));
return YY_TOK; }
"
">" { return YY_MORE; }
">>" { return YY_MOREMORE; }
"&" { return YY_AMP; }
"|" { return YY_BAR; }
"=" { return YY_SET; }
{unsupported} { fprintf(stderr, "Unsupported command: %c ", yytext[0]);
return YY_UNKNOWN;}
[\t \b\v ]+ { /* whitespaces */ }
. { fprintf(stderr, "Illegal character: %c ", yytext[0]);
return YY_UNKNOWN; }
%%
// The function returns 0 if there are no syntax errors and 1, otherwise
int wish_parse_command(char *command) {
YY_BUFFER_STATE buffer = yy_scan_string(command);
int retval = yyparse();
yylex_destroy();
return retval;
}
wish_yyparser:
%{
#include "wish.h"
int yylex();
void yyerror(const char* s);
%}
%union {
char *s;
}
%token YY_SET
%token YY_EXIT
%token YY_JOBS
%token YY_PWD
%token YY_CD
%token YY_UNKNOWN
%token YY_LESS
%token YY_MORE
%token YY_MOREMORE
%token YY_AMP
%token YY_BAR
%token YY_TOK
%start cmdline
%%
cmdline:
%empty /* an empty line is valid, too! Do nothing */
| redir_prog bg_mode
| in_prog pipe bg_mode
| arg YY_SET arg
| YY_PWD
| YY_CD arg
| YY_EXIT { /* ... */ }
pipe:
YY_BAR out_prog
| pipe YY_BAR out_prog
redir_prog:
prog
| prog any_redir
in_prog:
prog
| prog in_redir
out_prog:
prog
| prog out_redir
inout_redir:
in_redir out_redir
| out_redir in_redir
out_redir:
out1_redir
| out2_redir
any_redir:
in_redir
| out_redir
| inout_redir
in_redir: YY_LESS arg
out1_redir: YY_MORE arg
out2_redir: YY_MOREMORE arg
bg_mode:
%empty
| YY_AMP
prog:
args
args:
arg
| args arg
arg:
YY_TOK
%%
/* This section is empty */
wish.c:
#include
#include "wish.h"
int main(int argc, char *argv[])
{
// These two lines make the macOS C compiler happy.
// Otherwise, it complains about unused parameters.
(void)argc;
(void)argv;
char path[PATH_MAX];
char *home = getenv("HOME");
#ifdef DEBUG
home = "."; // So that you could place the config into the CWD
#endif
sprintf(path, "%s/%s", (home ? home : "."), WISH_CONFIG);
wish_read_config(path, 1);
// This is just a skeleton for your convenience
fputs(WISH_DEFAULT_PROMPT, stdout);
char *line = wish_read_line(stdin);
if(line)
free(line);
return EXIT_SUCCESS;
}
wish.conf:
echo 'hello, world!
# do not parse! a very long line --------------------------------------------------------------------
set good bad
aa
wish.h:
#ifndef WISH_H
#define WISH_H
#include
#include
#define DEBUG
#define WISH_MAX_INPUT 80 // really modest :)
#define WISH_DEFAULT_PROMPT "> "
#define WISH_CONFIG "wish.conf"
char *wish_read_line(FILE *in);
int wish_read_config(char *fname, int ok_if_missing);
#endif
Parsing Commands In this assignment, you are to implement and test function char *wish_unqiote(char *s); modify function void wish_read_config(char *fname); and explore the function int wish_parse command(char *command) and flex and bison parser code (provided). The new functions are in the files wish.parse.c, Wish yyparser.y (a bison file), and wish yylexer.l (a flex file). 1. Function char *wish_unquote(char *s) shall return the same string s in which each escape sequence 1 shall be replaced by its value. For example, a backslash 6 ' followed by an 6n, shall be replaced by a newline character 6n. 2. For this project stage, you must install bison and flex, free parser and lexer generators. You are not required to write your bison and flex rules but are encouraged to look into the provided files. Mac0S users: your default flex and bison are OLD; please upgrade them. File wish yylexer.I describes the tokens admissible on the wish command line. The tokens are described as regular expressions. For example, "\#", *" " "? represents a comment (a pound sign \#, followed by zero or more characters, optionally followed by a newline). Flex converts the input file into a C file (do not edit that file manually!) that provides the function yylex(). The function is a token generator: every time you call it, you get the next token from the command line. File wish yyparser.y recursively describes the grammar of the wish language, that is, what combinations of the tokens are admissible. For example, the command line must be either a program-with-redirection, possibly followed by an ampersand for background execution, an "in', program, followed by a pipe and possibly an ampersand, or a built-in command (lines 30-35). Furthermore, a program-with-redirection is either just a program or a program with "in-out" redirection (lines 41-43), etc. Bison converts the input file into a C file (do not edit that file manually!) that provides the function yyparse( . The function calls yylex() to read tokens, validates the stream of tokens against the grammar rules, and executes C actions (the code in curly braces) whenever an appropriate grammar structure is encountered. For ease of use, I wrapped yyparse( ) into another function int Wish_parse command (char *command) that takes the command line command, validates its syntax, and returns 1 or 0 , depending on whether or not the command line had syntax errors. You will use this function in the future to execute the command line. 3. Rewrite the main() function and wish_read_config() from your previous assignment. 3a. Call wish_parse command() after each successful call to Wish read line(). (Including the calls in wish read config()). Pass the value returned by wish_read_line( to wish_parse_command(). 3b. Add the main loop to the main() function. The loop shall run for as long as the global variable wish_exit (provided) is 0. The loop shall display the prompt, call wish_read_line( , and then call wish_parse_command( . 3c. Modify line 69 of wish yyparser.y: add an action that changes the value of wish_exit. You can now exit wish by typing exit on the command line
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started