#include #include #include "scanner.h" /** * The function _acceptToken checks whether the current token matches a target identifier. * if this is the case, it will mark this token as the given Type, then move to the next token. * @param lp List pointer to the start of the tokenlist. * @param ident target identifier * @param type enum describing the nature to assign if the token is accepted. * @return a bool denoting whether the current token matches the target identifier. */ bool _acceptToken(List *lp, char *ident, enum Type type) { if (*lp != NULL && strcmp(((*lp)->t), ident) == 0) { (*lp)->type = type; *lp = (*lp)->next; return true; } return false; } /** * The function parseExecutable parses an executable. * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the executable was parsed successfully. */ bool _parseExecutable(List *lp) { char s = (*lp)->t[0]; // Right now we only check if the first character is illegal (to prevent an operator token from mistakenly being assigned a COMMAND type.) switch (s) { case '|': case '&': case ';': case '\n': case '>': case '<': return false; } (*lp)->type = EXECUTABLE; *lp = (*lp)->next; return true; } /** * Checks whether the input string \param s is an operator. * @param s input string. * @return a bool denoting whether the current string is an operator. */ //bool isOperator(char *s) { bool _isOperator(List *lp) { // NULL-terminated array makes it easy to expand this array later // without changing the code at other places. char *operators[] = { "&", "&&", "||", ";", "<", ">", "|", NULL }; for (int i = 0; operators[i] != NULL; i++) { if (strcmp((*lp)->t, operators[i]) == 0) { return true; } } return false; } /** * The function parseOptions parses options. * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the options were parsed successfully. */ bool _parseOptions(List *lp) { while (*lp != NULL && !_isOperator(lp)) { (*lp)->type = OPTION; (*lp) = (*lp)->next; } return true; } /** * The function parseRedirections parses a command according to the grammar: * * ::= * * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the command was parsed successfully. */ bool _parseCommand(List *lp) { return _parseExecutable(lp) && _parseOptions(lp); } /** * The function parsePipeline parses a pipeline according to the grammar: * * ::= "|" * | * * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the pipeline was parsed successfully. */ bool _parsePipeline(List *lp) { if (!_parseCommand(lp)) { return false; } if (_acceptToken(lp, "|", PIPELINE)) { return _parsePipeline(lp); } return true; } /** * The function parseFileName parses a filename. * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the filename was parsed successfully. */ bool _parseFilename(List *lp) { //we run a POSIX compliant system, meaning all characters save '/' and NULL are allowed in filenames. //NULL is already taken care of by the List library, and / just means the file is located in a directory. //as a result the only limit we place on filenames are the reserved (operator) characters. switch ((*lp)->t[0]) { case '|': case '&': case ';': case '\n': case '>': case '<': return false; } (*lp)->type = FILENAME; *lp = (*lp)->next; return true; } /** * The function parseRedirections parses redirections according to the grammar: * * ::= * | * * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the redirections were parsed successfully. */ bool _parseRedirections(List *lp) { if (isEmpty(*lp)) { return true; } if (_acceptToken(lp, "<", REDIRECT)) { if (!_parseFilename(lp)) return false; if (_acceptToken(lp, ">", REDIRECT)) return _parseFilename(lp); else return true; } else if (_acceptToken(lp, ">", REDIRECT)) { if (!_parseFilename(lp)) return false; if (_acceptToken(lp, "<", REDIRECT)) return _parseFilename(lp); else return true; } return true; } /** * The function parseBuiltIn parses a builtin. * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the builtin was parsed successfully. */ bool _parseBuiltIn(List *lp) { // NULL-terminated array makes it easy to expand this array later // without changing the code at other places. char *builtIns[] = { "exit", "status", "true", "false", #if EXT_PROMPT "debug", "cd", #endif NULL }; for (int i = 0; builtIns[i] != NULL; i++) { if (_acceptToken(lp, builtIns[i], BUILTIN)) { return true; } } return false; } /** * The function parseChain parses a chain according to the grammar: * * ::= * | * * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the chain was parsed successfully. */ bool _parseChain(List *lp) { if (_parseBuiltIn(lp)) { return _parseOptions(lp); } if (_parsePipeline(lp)) { return _parseRedirections(lp); } return false; } /** * The function parseInputLine parses an inputline according to the grammar: * * ::= & * | && * | || * | ; * | * | * * @param lp List pointer to the start of the tokenlist. * @return a bool denoting whether the inputline was parsed successfully. */ bool parseInputLine(List *lp) { if (isEmpty(*lp)) { return true; } if (!_parseChain(lp)) { return false; } if (_acceptToken(lp, "&", BACKGROUND) || _acceptToken(lp, "&&", COMPOSITION)) { return parseInputLine(lp); } else if (_acceptToken(lp, "||", COMPOSITION)) { return parseInputLine(lp); } else if (_acceptToken(lp, ";", COMPOSITION)) { return parseInputLine(lp); } return true; }