/*
* Copyright (C) 2020 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see .
*/
#include "rpn_parse.h"
/**@brief Macro for @ref rpn_ops member definition
* @param NAME @ref rpn_lib.h symbol
* @param s The short (1 char) symbol code
* @param l The long (char*) symbole code
*/
#define __op(NAME, s, l) {&NAME, &CODE_SZ(NAME), s, l}
const rpn_op_t rpn_ops[] = {\
__op(rpn_add, '+', "add"),\
__op(rpn_sub, '-', "sub"),\
__op(rpn_div, '/', "div"),\
__op(rpn_mul, '*', "mul"),\
__op(rpn_mod, '%', "mod"),\
__op(rpn_neg, '!', "neg"),\
__op(rpn_not, '~', "not"),\
__op(rpn_and, '&', "and"),\
__op(rpn_or, '|', "or"),\
__op(rpn_xor, '^', "xor"),\
__op(rpn_shr, 'r', ">>"),\
__op(rpn_shl, 'l', "<<"),\
__op(rpn_xchg, 'x', "xchg"),\
__op(rpn_dup, 'd', "dup"),\
__op(rpn_pop_op, 'p', "pop"),\
};
#undef __op
int rpn_tokenizer_start(rpn_tokenizer_t *tokenizer, rpn_tokenized_t *dst,
const char* expr, size_t argc)
{
int err;
bzero(tokenizer, sizeof(rpn_tokenizer_t));
tokenizer->orig = expr;
tokenizer->toks = dst;
tokenizer->toks->argc = argc;
tokenizer->toks->tokens_sz = 0;
tokenizer->toks->tokens = NULL;
if(!(tokenizer->buff = strdup(expr)))
{
err = errno;
snprintf(tokenizer->err_reason, 64,
"Error duplicating expression for tokenization : %s",
strerror(err));
errno = err;
return -1;
}
tokenizer->cur = tokenizer->buff;
return 0;
}
rpn_token_t* rpn_tok(rpn_tokenizer_t *tokenizer)
{
char *token;
rpn_token_t ret, *tmp, *res;
int err;
token = tokenizer->cur;
if(!*token) // end of expression
{
rpn_tokenizer_free(tokenizer);
return NULL;
}
while(*(tokenizer->cur))
{
if(*(tokenizer->cur) == ' ' ||
*(tokenizer->cur) == '\n' ||
*(tokenizer->cur) == '\t')
{
break;
}
tokenizer->cur++;
tokenizer->chr_no++;
}
if(*(tokenizer->cur))
{
// not end, go on next chr for further rpn_tok calls
*(tokenizer->cur) = '\0';
tokenizer->cur++;
tokenizer->chr_no++;
}
// we have a clean token '\0' terminated
if(rpn_tokenize(token, &ret, tokenizer->err_reason) < 0)
{
errno = 0;
return NULL;
}
if(ret.type == RPN_arg && ret.arg_n >= tokenizer->toks->argc)
{
// invalid argument number
if(tokenizer->toks->argc)
{
snprintf(tokenizer->err_reason, 64,
"Argument number is too big : should be in [0..%ld] but \"%s\" found",
tokenizer->toks->argc - 1, token);
}
else
{
snprintf(tokenizer->err_reason, 64,
"No argument accepted but \"%s\" found",
token);
}
errno = EINVAL;
return NULL;
}
if(tokenizer->toks->tokens_sz >= tokenizer->allocated_toks)
{
tokenizer->allocated_toks += 8;
tmp = realloc(tokenizer->toks->tokens,
sizeof(rpn_token_t) * tokenizer->allocated_toks);
if(!tmp)
{
err = errno;
snprintf(tokenizer->err_reason, 64,
"Unable to realloc tokens list : %s",
strerror(err));
errno = err;
return NULL;
}
tokenizer->toks->tokens = tmp;
}
res = &(tokenizer->toks->tokens[tokenizer->toks->tokens_sz]);
*res = ret;
tokenizer->toks->tokens_sz++;
return res;
}
int rpn_tokenize(const char *token, rpn_token_t *dst, char error[64])
{
int rep;
unsigned long num;
const char *orig;
if((rep = rpn_match_token_i(token)) >= 0)
{
dst->type = RPN_op;
dst->op_n = rep;
dst->op = &(rpn_ops[rep]);
return 0;
}
orig = token;
if(*token == 'A')
{
if(!token[1])
{
snprintf(error, 64,
"Argument number is missing, lonely \"A \" found");
return -1;
}
dst->type = RPN_arg;
token++;
}
else
{
dst->type = RPN_val;
}
if(rpn_match_number(token, &num) < 0)
{
snprintf(error, 64,
"Invalid %snumber : \"%s\"",
dst->type == RPN_arg?"argument ":"constant ",
orig);
return -1;
}
if(dst->type == RPN_arg)
{
dst->arg_n = num;
}
else
{
dst->value = num;
}
return 0;
}
void rpn_tokenizer_free(rpn_tokenizer_t *tokenizer)
{
if(tokenizer->buff)
{
free(tokenizer->buff);
bzero(tokenizer, sizeof(rpn_tokenizer_t));
}
}
char* rpn_tokenized_expr(rpn_tokenized_t *tokens, char long_op)
{
size_t expr_sz, i;
int err, written;
char *cur, *tmp, *expr;
rpn_token_t *token;
int ALLOC_CHUNK=22; /* 64 bit uint is 20 decimal digit */
#ifdef DEBUG
if(!tokens)
{
dprintf(2, "Error, NULL ptr given to rpn_rokenize_expr");
err = EINVAL;
goto ret_err;
}
#endif
errno = 0;
err = 0;
expr_sz = 0;
expr_sz = 128;
cur = expr = malloc(sizeof(char)*expr_sz);
if(!expr)
{
err=errno;
dprintf(2,"Error allocating memory for expression : %s",
strerror(err));
goto ret_err;
}
for(i=0; itokens_sz; i++)
{
token = &(tokens->tokens[i]);
if(cur - expr >= expr_sz - ALLOC_CHUNK)
{
expr_sz += 128;
tmp = realloc(expr, sizeof(char) * expr_sz);
if(!tmp)
{
err=errno;
dprintf(2,"Error allocating memory for expression : %s",
strerror(err));
goto ret_err;
}
cur = tmp + (cur - expr);
expr = tmp;
}
switch(token->type)
{
case RPN_op:
if(!long_op)
{
*cur = token->op->chr;
cur[1] = ' ';
cur[2] = '\0';
written = 2;
}
else
{
written = snprintf(cur, ALLOC_CHUNK,
"%s ", token->op->str);
written--;
}
break;
case RPN_arg:
written = snprintf(cur, ALLOC_CHUNK,
"A%lu ", token->arg_n);
break;
case RPN_val:
written = snprintf(cur, ALLOC_CHUNK,
"0x%lX ", token->value);
break;
default:
#ifdef DEBUG
dprintf(2,
"Invalid token type encountered : %d\n",
token->type);
#endif
err = EUCLEAN;
goto free_err;
}
#ifdef DEBUG
if(written > ALLOC_CHUNK)
{
dprintf(2, "Expression too long : %s",
token->op->str);
err = EINVAL;
goto free_err;
}
#endif
cur += written;
}
return expr;
free_err:
free(expr);
ret_err:
errno = err;
return NULL;
}
const rpn_op_t* rpn_match_token(const char* token)
{
int rep;
if((rep = rpn_match_token_i(token)) < 0)
{
return NULL;
}
return &(rpn_ops[rep]);
}
int rpn_match_token_i(const char* token)
{
unsigned char i;
if(token[1] == '\0') //strlen(token) == 1
{
foreach_rpn_ops(i)
{
if(rpn_ops[i].chr == token[0])
{
return (int)i;
}
}
return -1;
}
foreach_rpn_ops(i)
{
if(!strncmp(rpn_ops[i].str, token, 8))
{
return (int)i;
}
}
return -1;
}
int rpn_match_number(const char* token, unsigned long *result)
{
char *endptr;
unsigned long long res;
int base;
if(*token == '\0')
{
return -1;
}
base = 10;
if(*token == '0' && token[1] != '\0')
{
token++;
if(*token == 'x')
{
token++;
base = 16;
}
else if(*token == 'o')
{
token++;
base = 8;
}
else if(*token == 'b')
{
token++;
base = 2;
}
}
res = strtoull(token, &endptr, base);
if(*endptr != '\0')
{
return -1;
}
*result = res;
return 0;
}
size_t rpn_op_sz()
{
return sizeof(rpn_ops)/sizeof(rpn_op_t);
}