Skip to content
parser.c 8.27 KiB
Newer Older
Seblu's avatar
Seblu committed
/*
Seblu's avatar
Seblu committed
** parser.c for 42sh
Seblu's avatar
Seblu committed
**
** Made by Seblu
** Login   <seblu@epita.fr>
**
** Started on  Wed Aug  2 00:56:07 2006 Seblu
** Last update Fri Sep  1 01:00:42 2006 Seblu
Seblu's avatar
Seblu committed
*/

Seblu's avatar
Seblu committed
#include <stdio.h>
#include <string.h>
#include <unistd.h>
Seblu's avatar
Seblu committed
#include <setjmp.h>
Seblu's avatar
Seblu committed
#include "parser.h"
#include "../common/macro.h"
Seblu's avatar
Seblu committed
#include "../shell/shell.h"
Seblu's avatar
Seblu committed
#include "getline.h"
Seblu's avatar
Seblu committed

/*
Seblu's avatar
Seblu committed
** ============
** DECLARATIONS
** ============
Seblu's avatar
Seblu committed
*/
Seblu's avatar
Seblu committed

static s_token keywords[] =
  {
    {TOK_IF, "if", 2},
    {TOK_THEN, "then", 4},
    {TOK_ELSE, "else", 4},
    {TOK_FI, "fi", 2},
    {TOK_ELIF, "elif", 4},
    {TOK_DO, "do", 2},
    {TOK_DONE, "done", 4},
    {TOK_CASE, "case", 4},
    {TOK_ESAC, "esac", 4},
    {TOK_WHILE, "while", 5},
    {TOK_UNTIL, "until", 5},
    {TOK_FOR, "for", 3},
    {TOK_IN, "in", 2},
    {TOK_LBRACE, "{", 1},
    {TOK_RBRACE, "}", 1},
    {TOK_BANG, "!", 1},
    {TOK_NONE, NULL, 0}
  };
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*regnode(s_parser *parser, s_ast_node *node);
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
/*!
Seblu's avatar
Seblu committed
** Parse an input, following the Grammar rule input
** input:       list '\n'
**		list EOF
**		| '\n'
**		| EOF
Seblu's avatar
Seblu committed
**
Seblu's avatar
Seblu committed
** @param parser parser struct
Seblu's avatar
Seblu committed
**
Seblu's avatar
Seblu committed
** @return parent ast node, for execution
Seblu's avatar
Seblu committed
*/
Seblu's avatar
Seblu committed
static s_ast_node	*parse_input(s_parser *parser);
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_list(s_parser *parser);
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_andor(s_parser *parser);
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_pipeline(s_parser *parser);
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_command(s_parser *parser);
Seblu's avatar
Seblu committed

static s_ast_node	*parse_simplecommand(s_parser *parser);

Seblu's avatar
Seblu committed
/*!
** Notify a parse error
**
** @param parser parser where error appear
** @param t token near of the error
*/
Seblu's avatar
Seblu committed
static void		parse_error(s_parser *parser, s_token t);
Seblu's avatar
Seblu committed

#if DEBUG_PARSER==1
# define debugmsg(msg) fprintf(stderr, "debug: %s\n", (msg))
#else
# define debugmsg(msg)
#endif

Seblu's avatar
Seblu committed
/*
Seblu's avatar
Seblu committed
** ===========
** DEFINITIONS
** ===========
Seblu's avatar
Seblu committed
*/
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
s_parser		*parser_init(int fd)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_parser		*new;
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
  secmalloc(new, sizeof (s_parser));
  new->lexer = lexer_init(fd);
Seblu's avatar
Seblu committed
  new->error = 0;
  new->regnodes = NULL;
  new->regsize = new->regpos = 0;
Seblu's avatar
Seblu committed
  return new;
}

Seblu's avatar
Seblu committed
static s_ast_node	*regnode(s_parser *parser, s_ast_node *node)
Seblu's avatar
Seblu committed
{
  if (!node)
    return node;
  if (parser->regpos >= parser->regsize) {
    parser->regsize += 50;
    secrealloc(parser->regnodes, parser->regnodes,
Seblu's avatar
Seblu committed
	       parser->regsize * sizeof (s_ast_node));
Seblu's avatar
Seblu committed
  }
  parser->regnodes[parser->regpos] = node;
  ++parser->regpos;
  return node;
}

Seblu's avatar
Seblu committed
static void		parse_error(s_parser *parser, s_token t)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  debugmsg("parse_error");
  fprintf(stderr, "%s: syntax error near unexpected token `%s'\n",
	  shell->name, t.str);
  parser->error = 1;
Seblu's avatar
Seblu committed
  shell->status = ERROR_PARSE;
Seblu's avatar
Seblu committed
  if (parser->regnodes)
    for (register int i = 0; parser->regnodes[i]; ++i)
      ast_destruct(parser->regnodes[i]);
  longjmp(parser->stack, 1);
/* static int		is_keyword(s_token t) */
/* { */
/*   for (int i = 0; keywords[i].id != TOK_NONE; ++i) */
/*     if (!strncmp(t.str, keywords[i].str, keywords[i].len)) { */
/*       t.id = keywords[i].id; */
/*       return 1; */
/*     } */
/*   return 0; */
/* } */

static int		is_assignment(s_token t)
{
  return strchr(t.str, '=') == NULL ? 0 : 1;
}

static void		recon(s_token t)
{
  //check for keywords
  for (int i = 0; keywords[i].id != TOK_NONE; ++i)
    if (!strncmp(t.str, keywords[i].str, keywords[i].len)) {
      t.id = keywords[i].id;
    }
  //check
}

Seblu's avatar
Seblu committed
s_ast_node		*parse(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  parser->regpos = 0;
  parser->error = 0;
  // prevent of too big register ast size
  if (parser->regsize >= 200)
    secrealloc(parser->regnodes, parser->regnodes,
Seblu's avatar
Seblu committed
	       (parser->regsize = 50) * sizeof (s_ast_node));
Seblu's avatar
Seblu committed
  if (setjmp(parser->stack))
    return NULL;
  show_prompt(PROMPT_PS1);
#if DEBUG_LEXER == 1
Seblu's avatar
Seblu committed
  //test lexer mode
Seblu's avatar
Seblu committed
      s_token tok = lexer_gettoken(parser->lexer);
      if (tok.id == TOK_EOF)
	exit(69);
      printf("Returned token: %d [%s]\n", tok.id,
	     (*tok.str == '\n') ? "\\n" : tok.str);
      if (tok.id == TOK_NEWLINE)
	show_prompt(PROMPT_PS1);
Seblu's avatar
Seblu committed
  return parse_input(parser);
}

Seblu's avatar
Seblu committed
static s_ast_node	*parse_input(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_token		token;
  s_ast_node		*buf;
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
  debugmsg("parse_input");
  token = lexer_lookahead(parser->lexer);
  if (token.id == TOK_EOF)
    return NULL;
  if (token.id == TOK_NEWLINE) {
    token = lexer_gettoken(parser->lexer);
Seblu's avatar
Seblu committed
    return NULL;
Seblu's avatar
Seblu committed
  }
Seblu's avatar
Seblu committed
  buf = parse_list(parser);
Seblu's avatar
Seblu committed
  token = lexer_gettoken(parser->lexer);
  if (token.id != TOK_EOF && token.id != TOK_NEWLINE)
Seblu's avatar
Seblu committed
    parse_error(parser, token);
Seblu's avatar
Seblu committed
  return buf;
}

Seblu's avatar
Seblu committed
static s_ast_node	*parse_list(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_token		token;
  s_ast_node		*lhs;
  s_ast_node		*rhs;
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
  debugmsg("parse_list");
Seblu's avatar
Seblu committed
  lhs = parse_andor(parser);
Seblu's avatar
Seblu committed
  token = lexer_lookahead(parser->lexer);
  if (token.id == TOK_SEP || token.id == TOK_SEPAND) {
    lexer_gettoken(parser->lexer);
    rhs = parse_list(parser);
    if (token.id == TOK_SEP)
      return regnode(parser, ast_sep_create(lhs, rhs));
    else
      return regnode(parser, ast_sepand_create(lhs, rhs));
  }
  return lhs;
Seblu's avatar
Seblu committed
}
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_andor(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_token		token;
  s_ast_node		*lhs;
  s_ast_node		*rhs;
Seblu's avatar
Seblu committed

  debugmsg("parse_andor");
  lhs = parse_pipeline(parser);
  token = lexer_lookahead(parser->lexer);
  if (token.id == TOK_AND || token.id == TOK_OR) {
    lexer_gettoken(parser->lexer);
    rhs = parse_andor(parser);
    if (token.id == TOK_AND)
      return regnode(parser, ast_and_create(lhs, rhs));
    else
      return regnode(parser, ast_or_create(lhs, rhs));
  }
  return lhs;
Seblu's avatar
Seblu committed
}
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
static s_ast_node	*parse_pipeline(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_token		token;
  s_ast_node		*lhs;
Seblu's avatar
Seblu committed
  int			banged = 0;
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
  debugmsg("parse_pipeline");
  token = lexer_lookahead(parser->lexer);
  if (token.id == TOK_BANG) {
    lexer_gettoken(parser->lexer);
    banged = 1;
  }
  lhs = parse_command(parser);
  return lhs;
}

Seblu's avatar
Seblu committed
static s_ast_node	*parse_command(s_parser *parser)
Seblu's avatar
Seblu committed
{
Seblu's avatar
Seblu committed
  s_token		token;
Seblu's avatar
Seblu committed

  debugmsg("parse_command");
Seblu's avatar
Seblu committed
  token = lexer_lookahead(parser->lexer);
  recon(token);
  if (token.id == TOK_WORD) {
    return parse_simplecommand(parser);
  }
Seblu's avatar
Seblu committed
  return NULL;
}
Seblu's avatar
Seblu committed

static s_ast_node	*parse_simplecommand(s_parser *parser)
{
  s_token		token;
  s_ast_node		*cmd;

  debugmsg("parse_simplecommand");
  cmd = regnode(parser, ast_cmd_create());
  //get prefix
  while (is_assignment(lexer_lookahead(parser->lexer)))
    ast_cmd_add_prefix(cmd, lexer_gettoken(parser->lexer).str);
  //get element
  if ((token = lexer_gettoken(parser->lexer)).id == TOK_WORD)
    ast_cmd_add_argv(cmd, token.str);
  else
    parse_error(parser, token);
  while (recon(token = lexer_lookahead(parser->lexer)),
	 token.id == TOK_WORD) {
    ast_cmd_add_argv(cmd, token.str);
    lexer_gettoken(parser->lexer);
  }
  return cmd;
}
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_shellcommand(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_funcdec(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_cmdprefix(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_redirection(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_element(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_compound_list(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_rulefor(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_rulewhile(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_ruleuntil(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_rulecase(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_ruleif(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_elseclause(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_dogroup(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_caseclause(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */

Seblu's avatar
Seblu committed
/* static s_ast_node	*parse_pattern(s_parser *parser) */
Seblu's avatar
Seblu committed
/* { */
/*   parser=parser; */
/*   return NULL; */
/* } */