Skip to content
lexer.c 4.88 KiB
Newer Older
Seblu's avatar
Seblu committed
/*
** lexer.c for 42sh
**
** Made by Seblu
** Login   <seblu@epita.fr>
**
** Started on  Sun Jul 30 04:36:53 2006 Seblu
** Last update Sat Aug 19 01:41:32 2006 Seblu
Seblu's avatar
Seblu committed
*/

#include <stdio.h>
#include <string.h>
Seblu's avatar
Seblu committed
#include <ctype.h>
Seblu's avatar
Seblu committed
#include "parser.h"
#include "../shell/shell.h"
#include "../readline/readline.h"
Seblu's avatar
Seblu committed
#include "../common/common.h"
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
#define is_quote(c) ((c) == '"' || (c) == '\'' || (c) == '`')
#define is_sep(c) ((c) == ' ' || (c) == '\t' || (c) == '\v')

static int	lexer_reconize(ts_parser *parser);

static void	lexer_eat(ts_parser *lex);

static ts_token	token_create(te_tokenid id, const char *string);
Seblu's avatar
Seblu committed

static void	token_set(ts_token *token, te_tokenid id, const char *s);

ts_token operators[] =
Seblu's avatar
Seblu committed
  {
    {TOK_AND, "&&"},
    {TOK_OR, "||"},
    {TOK_DSEMI, ";;"},
    {TOK_DLESS, "<<"},
    {TOK_DGREAT, ">>"},
    {TOK_LESSAND, "<&"},
    {TOK_GREATAND, ">&"},
    {TOK_LESSGREAT, "<>"},
    {TOK_DLESSDASH, "<<-"},
    {TOK_CLOBBER, ">|"},
    {TOK_SEP, ";"},
    {TOK_SEPAND, "&"},
    {TOK_NEWLINE, "\n"},
    {0, NULL}
  };

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

void		lexer_reset(ts_parser *parser)
{
  token_set(&parser->token, TOK_NONE, NULL);
  if (parser->buf) free(parser->buf);
Seblu's avatar
Seblu committed
  parser->buf = NULL;
Seblu's avatar
Seblu committed
  parser->buf_size = parser->buf_pos = 0;
Seblu's avatar
Seblu committed
}

ts_token	lexer_lookahead(ts_parser *parser)
Seblu's avatar
Seblu committed
{
  if (parser->status != PARSE_OK)
    return token_create(TOK_ERR, NULL);
  if (parser->token.id == TOK_NONE)
    lexer_eat(parser);
  return parser->token;
Seblu's avatar
Seblu committed
}

ts_token	lexer_gettoken(ts_parser *parser)
Seblu's avatar
Seblu committed
{
  ts_token	buf;

  if (parser->status != PARSE_OK)
    return token_create(TOK_ERR, NULL);
  if (parser->token.id == TOK_NONE)
Seblu's avatar
Seblu committed
    lexer_eat(parser);
  buf = parser->token;
  parser->token = token_create(TOK_NONE, NULL);
  return buf;
}

static ts_token	token_create(te_tokenid id, const char *string)
{
  ts_token	new = { id, string };

  return new;
}

static void	token_set(ts_token *token, te_tokenid id, const char *s)
{
  if (token->id == TOK_WORD && token->str)
    free((char*) token->str);
  token->id = id;
  token->str = s;
Seblu's avatar
Seblu committed
}

void		lexer_eat(ts_parser *parser)
{
Seblu's avatar
Seblu committed
  char		*lbuf, *lbuf2;

  // return EOF (the last), if file is ended
Seblu's avatar
Seblu committed
  if (parser->status == PARSE_END)
    return;
Seblu's avatar
Seblu committed
  //if line is void, start readding
Seblu's avatar
Seblu committed
  if (parser->buf_size == 0) {
    if ((parser->buf = readline(get_prompt(TYPE_PS1))) == NULL) {
      token_set(&parser->token, TOK_EOF, "eof");
Seblu's avatar
Seblu committed
      parser->status = PARSE_END;
      return;
    }
    parser->buf_pos = 0;
    parser->buf_size = strlen(parser->buf);
  }
Seblu's avatar
Seblu committed
  //read line while a token will not be reconized
  while (!lexer_reconize(parser)) {
    if ((lbuf2 = readline(get_prompt(TYPE_PS2))) == NULL) {
      token_set(&parser->token, TOK_EOF, "eof");
Seblu's avatar
Seblu committed
      parser->status = PARSE_END;
Seblu's avatar
Seblu committed
      return;
Seblu's avatar
Seblu committed
    }
Seblu's avatar
Seblu committed
    lbuf = parser->buf;
    parser->buf = strmerge(2, lbuf, lbuf2);
    parser->buf_size = strlen(parser->buf);
    free(lbuf), free(lbuf2);
  }
Seblu's avatar
Seblu committed
static int	lexer_reconize(ts_parser *parser)
{
  const char	*buf = parser->buf;
  const size_t	buf_size = parser->buf_size;
  size_t	*buf_pos = &parser->buf_pos;
  size_t	token_start;
  size_t	token_pos;
  int		end_found = 0;
  char		backed = 0;
  char		quoted = 0;

  //eat spaces (" ",\t, \v)
  while (*buf_pos < buf_size &&
	 is_sep(buf[*buf_pos]))
    ++*buf_pos;
  //check for leading \n token
  if (buf[*buf_pos] == '\n') {
    ++*buf_pos;
    token_set(&parser->token, TOK_NEWLINE, "\n");
Seblu's avatar
Seblu committed
    return 1;
  }
  //cut a token
  token_start = token_pos = *buf_pos;
  for (; !end_found && token_pos < buf_size; ++token_pos)
    if (backed)
      backed = 0;
    else if (!quoted && (buf[token_pos] == '\n' || is_sep(buf[token_pos])))
      { end_found = 1; break; }
    else if (!quoted && is_quote(buf[token_pos]))
      quoted = buf[token_pos];
    else if (quoted && buf[token_pos] == quoted)
      quoted = 0;
    else if (!backed && buf[token_pos] == '\\')
      backed = 1;
  if (!end_found) return 0;
  parser->buf_pos = token_pos;
  printf("cutted token: '%s'\n",
Seblu's avatar
Seblu committed
	 strndup(buf + token_start, token_pos - token_start));
  //check if it's a registered keyword
  for (register int i = 0; keywords[i].str; ++i)
    if (!strncmp(keywords[i].str, buf + token_start,
Seblu's avatar
Seblu committed
		 token_pos - token_start)) {
      token_set(&parser->token, keywords[i].id, keywords[i].str);
      printf("reconized token: %d (%s)\n", keywords[i].id, keywords[i].str);
Seblu's avatar
Seblu committed
      return 1;
    }
Seblu's avatar
Seblu committed
  //althought this token is a word
  token_set(&parser->token, TOK_WORD,
Seblu's avatar
Seblu committed
		    strndup(buf + token_start, token_pos - token_start));
  printf("reconized token (WORD): %d (%s)\n",
	 parser->token.id, parser->token.str);
Seblu's avatar
Seblu committed
  return 1;
Seblu's avatar
Seblu committed
}