CSE320/hw4/src/execution.c

401 lines
9.7 KiB
C

/*
* DO NOT MODIFY THE CONTENTS OF THIS FILE.
* IT WILL BE REPLACED DURING GRADING
*/
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "mush.h"
#include "mush.tab.h"
#include "debug.h"
/*
* Mush: Execution engine.
*/
extern int yylex_destroy();
extern void push_input(FILE *in);
extern int pop_input(void);
extern int input_depth(void);
extern STMT *mush_parsed_stmt;
static int exec_run();
static int exec_cont();
#define PROMPT "mush: "
/* Uncomment this to enable tracing of the parser. */
//int yydebug = 1;
/*
* A quit handler is installed to allow user-initiated escape
* from constructs that wait for signals.
*/
static volatile sig_atomic_t got_quit = 0;
static void handler(int sig) {
got_quit = 1;
}
/*
* Target for longjmp() to jump to after a low-level error has
* occurred, e.g. in expression evaluation.
*/
static jmp_buf onerror;
/*
* Top-level interpreter loop.
* Reads single statements from stdin and either inserts them into the program,
* if they have a line number, otherwise executes them immediately.
*/
int exec_interactive() {
signal(SIGQUIT, SIG_IGN);
while(1) {
if(!input_depth() && isatty(fileno(stdin)))
fprintf(stdout, "%s", PROMPT);
fflush(stdout);
if(!yyparse()) {
STMT *stmt = mush_parsed_stmt;
if(stmt != NULL) {
if(stmt->lineno) {
prog_insert(stmt);
} else {
if(stmt->class == RUN_STMT_CLASS) {
free_stmt(stmt);
stmt = NULL;
exec_run();
} else if(stmt->class == CONT_STMT_CLASS) {
free_stmt(stmt);
stmt = NULL;
exec_cont();
} else {
exec_stmt(stmt);
free_stmt(stmt);
stmt = NULL;
}
}
}
} else {
if(pop_input())
break;
}
if(!input_depth() && isatty(fileno(stdin))) {
store_show(stderr);
fprintf(stderr, "\n");
jobs_show(stderr);
}
}
yylex_destroy();
return 0;
}
/*
* Enter an execution loop starting at the beginning of the program.
*/
static int exec_run() {
prog_reset();
return exec_cont();
}
/*
* Enter an execution loop starting at the current line number.
*/
static int exec_cont() {
int err = 0;
STMT *stmt;
stmt = prog_fetch();
if(stmt == NULL) {
fprintf(stderr, "No statement to execute\n");
return -1;
}
if(setjmp(onerror))
return -1;
signal(SIGQUIT, handler);
while(!got_quit) {
stmt = prog_fetch();
if(!stmt) {
fprintf(stderr, "STOP (end of program)\n");
break;
}
prog_next();
err = exec_stmt(stmt);
if(err)
break;
}
signal(SIGQUIT, SIG_IGN);
if(got_quit)
fprintf(stderr, "Quit!\n");
got_quit = 0;
return err;
}
/*
* Execute a statement.
* This function is called from exec_run().
* It can also be called separately to execute an individual statement
* read interactively.
*
* Successful execution (except for STOP) returns 0.
* Successful execution of STOP returns 1.
* Unsuccessful execution returns -1.
*/
int exec_stmt(STMT *stmt) {
int val; char *str;
FILE *in;
if(setjmp(onerror))
return -1;
if(stmt->lineno)
debug("execute statement %d", stmt->lineno);
switch(stmt->class) {
case LIST_STMT_CLASS:
prog_list(stdout);
break;
case DELETE_STMT_CLASS:
prog_delete(stmt->members.delete_stmt.from, stmt->members.delete_stmt.to);
break;
case STOP_STMT_CLASS:
if(stmt->lineno)
fprintf(stderr, "STOP at line %d\n", stmt->lineno);
return 1;
case GOTO_STMT_CLASS:
if(!prog_goto(stmt->members.goto_stmt.lineno))
return -1;
break;
case SET_STMT_CLASS:
switch(stmt->members.set_stmt.expr->type) {
case NUM_VALUE_TYPE:
val = eval_to_numeric(stmt->members.set_stmt.expr);
store_set_int(stmt->members.set_stmt.name, val);
break;
case STRING_VALUE_TYPE:
str = eval_to_string(stmt->members.set_stmt.expr);
store_set_string(stmt->members.set_stmt.name, str);
break;
default:
break;
}
break;
case UNSET_STMT_CLASS:
store_set_string(stmt->members.unset_stmt.name, NULL);
break;
case IF_STMT_CLASS:
val = eval_to_numeric(stmt->members.if_stmt.expr);
if(val) {
if(!prog_goto(stmt->members.if_stmt.lineno))
return -1;
else
return 0;
}
break;
case SOURCE_STMT_CLASS:
in = fopen(stmt->members.source_stmt.file, "r");
if(!in) {
fprintf(stderr, "Couldn't open source file: '%s'\n",
stmt->members.source_stmt.file);
return -1;
}
push_input(in);
break;
case FG_STMT_CLASS:
{
PIPELINE *pp = stmt->members.sys_stmt.pipeline;
int job = jobs_run(pp);
store_set_int(JOB_VAR, job);
int status = jobs_wait(job);
store_set_int(STATUS_VAR, status);
if(pp->capture_output) {
char *output = jobs_get_output(job);
debug("Captured output: '%s'", output);
store_set_string(OUTPUT_VAR, output);
}
jobs_expunge(job);
}
break;
case BG_STMT_CLASS:
{
int job = jobs_run(stmt->members.sys_stmt.pipeline);
store_set_int(JOB_VAR, job);
}
break;
case WAIT_STMT_CLASS:
{
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
int status = jobs_wait(job);
store_set_int(STATUS_VAR, status);
char *output = jobs_get_output(job);
if(output)
store_set_string(OUTPUT_VAR, output);
jobs_expunge(job);
}
break;
case POLL_STMT_CLASS:
{
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
int status = jobs_poll(job);
store_set_int(STATUS_VAR, status);
if(status >= 0) {
char *output = jobs_get_output(job);
if(output)
store_set_string(OUTPUT_VAR, output);
jobs_expunge(job);
}
}
break;
case CANCEL_STMT_CLASS:
{
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
jobs_cancel(job);
}
break;
case PAUSE_STMT_CLASS:
{
jobs_pause();
}
break;
default:
fprintf(stderr, "Unknown statement class: %d\n", stmt->class);
abort();
}
return 0;
}
/*
* Evaluate an expression, returning an integer result.
* It is assumed that the jmp_buf onerror has been initialized by the caller
* with a control point to escape to in case there is an error during evaluation.
*/
long eval_to_numeric(EXPR *expr) {
char *endp, *str1, *str2;
long opr1, opr2;
int err;
switch(expr->class) {
case LIT_EXPR_CLASS:
opr1 = strtol(expr->members.value, &endp, 0);
if(*endp == '\0') {
return opr1;
} else {
fprintf(stderr, "Literal '%s' is not an integer\n", expr->members.value);
longjmp(onerror, 0);
}
case STRING_EXPR_CLASS:
case NUM_EXPR_CLASS:
err = store_get_int(expr->members.variable, &opr1);
if(err) {
fprintf(stderr, "Variable %s does not have an integer value\n",
expr->members.variable);
longjmp(onerror, 0);
}
return opr1;
fprintf(stderr, "String variable %s in expression not implemented\n",
expr->members.variable);
abort();
case UNARY_EXPR_CLASS:
opr1 = eval_to_numeric(expr->members.unary_expr.arg);
switch(expr->members.unary_expr.oprtr) {
case NOT_OPRTR:
return opr1 ? 0 : 1;
default:
fprintf(stderr, "Unknown unary numeric operator: %d\n",
expr->members.unary_expr.oprtr);
abort();
}
case BINARY_EXPR_CLASS:
if(expr->members.binary_expr.oprtr == EQUAL_OPRTR) {
if(expr->members.binary_expr.arg1->type == NUM_VALUE_TYPE &&
expr->members.binary_expr.arg2->type == NUM_VALUE_TYPE) {
opr1 = eval_to_numeric(expr->members.binary_expr.arg1);
opr2 = eval_to_numeric(expr->members.binary_expr.arg2);
return opr1 == opr2;
} else {
str1 = eval_to_string(expr->members.binary_expr.arg1);
str2 = eval_to_string(expr->members.binary_expr.arg2);
return !strcmp(str1, str2);
}
}
opr1 = eval_to_numeric(expr->members.binary_expr.arg1);
opr2 = eval_to_numeric(expr->members.binary_expr.arg2);
switch(expr->members.binary_expr.oprtr) {
case AND_OPRTR:
return opr1 && opr2;
case OR_OPRTR:
return opr1 || opr2;
case PLUS_OPRTR:
return opr1 + opr2;
case MINUS_OPRTR:
return opr1 - opr2;
case TIMES_OPRTR:
return opr1 * opr2;
case DIVIDE_OPRTR:
return opr1 / opr2;
case MOD_OPRTR:
return opr1 % opr2;
case LESS_OPRTR:
return opr1 < opr2;
case GREATER_OPRTR:
return opr1 > opr2;
case LESSEQ_OPRTR:
return opr1 <= opr2;
case GREATEQ_OPRTR:
return opr1 >= opr2;
default:
fprintf(stderr, "Unknown binary numeric operator: %d\n",
expr->members.binary_expr.oprtr);
abort();
}
default:
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
abort();
}
return 0;
}
/*
* Evaluate an expression, returning a string result.
* It is assumed that the jmp_buf onerror has been initialized by the caller
* with a control point to escape to in case there is an error during evaluation.
*/
char *eval_to_string(EXPR *expr) {
char *str1, *str2;
switch(expr->class) {
case LIT_EXPR_CLASS:
return expr->members.value;
case NUM_EXPR_CLASS:
case STRING_EXPR_CLASS:
str1 = store_get_string(expr->members.variable);
if(!str1) {
fprintf(stderr, "Variable %s does not have a value\n",
expr->members.variable);
longjmp(onerror, 0);
}
return str1;
case UNARY_EXPR_CLASS:
str1 = eval_to_string(expr->members.unary_expr.arg);
switch(expr->members.unary_expr.oprtr) {
default:
(void)str1;
fprintf(stderr, "Unknown unary string operator: %d\n",
expr->members.unary_expr.oprtr);
abort();
}
case BINARY_EXPR_CLASS:
str1 = eval_to_string(expr->members.binary_expr.arg1);
str2 = eval_to_string(expr->members.binary_expr.arg2);
switch(expr->members.binary_expr.oprtr) {
default:
(void)str1; (void)str2;
fprintf(stderr, "Unknown binary string operator: %d\n",
expr->members.binary_expr.oprtr);
abort();
}
default:
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
abort();
}
return 0;
}