/* * 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); }