diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c116247 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "windows-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f023bea --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "*.embeddedhtml": "html", + "infix_to_rpn.h": "c", + "utilities.h": "c", + "brigde.h": "c" + } +} \ No newline at end of file diff --git a/RPNtoInfix.cpp b/RPNtoInfix.cpp new file mode 100644 index 0000000..e527084 --- /dev/null +++ b/RPNtoInfix.cpp @@ -0,0 +1,82 @@ +/* PROCEDURE */ +// STEP (1) - read input into string variable "input". +// STEP (2) - read the string variable character by character. +// STEP (2a) - if the current character is a number, push it to stack "S" and create a new null tree. +// STEP (2b) - if the current character is an operator, pop the top 2 number in the stack +// and push a new tree (with the two trees as the branches) to the stack. +// STEP (3) - display the tree via in-order traversal. +// STEP (3a) - if the currently-displayed tree's data is not a number, +// add brackets to the display before the next tree. + +#include +#include +#include +#include +using namespace std; + +class Tree { +private: + char data; + Tree *Left, *Right; + +public: + Tree(char d, Tree *L, Tree *R) { + data = d; + Left = L; + Right = R; + } + ~Tree() { }; + char getData() { return data; }; + Tree* getLeft() { return Left; } + Tree* getRight() { return Right; } +}; + +void inOrder(Tree *T); + +int main() { + stack S; + int stringlength; + Tree* temp[2]; + string input; + Tree *null = NULL; + Tree *T; + int i; // index. + /* --- STEP (1) --- */ + getline(cin, input); + stringlength = input.length(); + /* --- STEP (2) --- */ + for (i = 0; i < stringlength; i++) { + if ((input[i] >= '0') && (input[i] <= '9')) { + /* --- STEP (2a) --- */ + S.push(new Tree(input[i], null, null)); + } else if ((input[i] == '+') || (input[i] == '-') + || (input[i] == '*') || (input[i] == '/')) { + /* --- STEP (2b) --- */ + temp[0] = S.top(); + S.pop(); + temp[1] = S.top(); + S.pop(); + S.push(new Tree(input[i], temp[1], temp[0])); + } + T = S.top(); + } + /* --- STEP (3) --- */ + inOrder(T); +} + +void inOrder(Tree *T) { + if (T == NULL) { + return; + } + if ((T->getData() < '0') || (T->getData() > '9')) { + /* STEP (3a) */ + cout << "("; + } + inOrder(T->getLeft()); + cout << T->getData(); + inOrder(T->getRight()); + if ((T->getData() < '0') || (T->getData() > '9')) { + /* STEP (3a) */ + cout << ")"; + } +} \ No newline at end of file diff --git a/include/brigde.h b/include/brigde.h new file mode 100644 index 0000000..1af9204 --- /dev/null +++ b/include/brigde.h @@ -0,0 +1,19 @@ + +#include +#include +#include +#include + +// Structure to store number-letter mapping +typedef struct { + char letter; + char number[32]; +} Mapping; + +// Function prototypes +void remove_spaces(char *str); +int is_number(const char *str); +char generate_unique_letter(char *str, char used_letters[], int *used_count); +char *parse_and_replace_rpn(char *expression, Mapping mappings[], int *mapping_count); +void replace_letters_with_numbers(char *input, Mapping mappings[], int mapping_count); + diff --git a/makefile b/makefile index 6a2b980..2c4a241 100644 --- a/makefile +++ b/makefile @@ -2,41 +2,25 @@ CC := gcc SRCDIR := src BUILDDIR := build -TESTDIR := test - TARGET := convert -TEST_TARGET := convert_tests SOURCES := $(shell find $(SRCDIR) -type f -name *.c) OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.c=.o)) -TEST_SOURCES := $(shell find $(TESTDIR) -type f -name *.c) -TEST_OBJECTS := $(patsubst $(TESTDIR)/%,$(BUILDDIR)/%,$(TEST_SOURCES:.c=.o)) -TEST_OBJECTS += $(OBJECTS) -TEST_OBJECTS := $(filter-out build/main.o, $(TEST_OBJECTS)) -CFLAGS := -std=c99 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wstrict-overflow\ - -fstack-protector -O2 -LIBS := -lcheck -lm -lsubunit -lrt -lpthread +CFLAGS := -std=c99 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wstrict-overflow -fstack-protector -O2 +LIBS := -lm -lpthread INC := -I include -all: convert tests -convert: $(TARGET) -tests: $(TEST_TARGET) +all: $(TARGET) $(TARGET): $(OBJECTS) $(CC) $^ -o $(TARGET) $(INC) $(CFLAGS) $(LIBS) -$(TEST_TARGET): $(TEST_OBJECTS) - $(CC) $^ -o $(TEST_TARGET) $(INC) $(CFLAGS) $(LIBS) - $(BUILDDIR)/%.o: $(SRCDIR)/%.c - mkdir -p $(BUILDDIR) - $(CC) -c -o $@ $< $(INC) $(CFLAGS) $(LIBS) - -$(BUILDDIR)/%.o: $(TESTDIR)/%.c - mkdir -p $(BUILDDIR) - $(CC) -c -o $@ $< $(INC) $(CFLAGS) $(LIBS) + mkdir -p $(BUILDDIR) || true + $(CC) -c -o $@ $< $(INC) $(CFLAGS) .PHONY: clean clean: - -@rm -rf $(BUILDDIR) $(TEST_TARGET) $(TARGET) + -@rm -rf $(BUILDDIR) $(TARGET) + diff --git a/rpne.py b/rpne.py new file mode 100644 index 0000000..0f88046 --- /dev/null +++ b/rpne.py @@ -0,0 +1,189 @@ +class ParenthesisMismatchError(Exception): + """ Raised when opening parentheses in infix notation doesn't match + the closing ones. + """ + def __init__(self, *args): + Exception.__init__(self, *args) + + def __str__(self): + return "Parentheses mismatch" + +class RPNOperator(object): + """ + An operator in terms of Reversed Polish Notation. + This class is used by RPNExprFactory to parse and by RPNCalculator to + calculate given expression value. + """ + def __init__(self, symbol, priority=1, number_of_operands=2, call=None): + """ + Creates the RPNOperator due to specified attributes. + + @type symbol: str + @param symbol: A symbol of an operator. + It's used to find the operator in infix notation string. + @type priority: int + @param priority: The priority of the operator. + Higher number means higher priority. + @type number_of_operands: int + @param number_of_operands: The number of operands that operator needs. + @type call: function + @param call: A function that performs an operation on the operands + and returns a value. + If it's not defined the RPNCalculator won't be able + to evaluate the expression. + """ + self.symbol = symbol + self.priority = priority + self.number_of_operands = number_of_operands + self.call = call + + def __str__(self): + return self.symbol + + def __repr__(self): + return "RPNOperator('%s')" % self.symbol + + def __call__(self, *args, **kwargs): + self.calculate(*args, **kwargs) + + def calculate(self, *args, **kwargs): + if self.call: + return self.call(*args) + else: + return None + + +class RPNParser(object): + """ + A parser which is able to create a RPNExpression object. + It needs to have operators (as a list of RPNOperator objects) + and parenthesis symbols defined. It should be used to generate + RPNExpressions which can be stored and/or evaluated later on. + """ + def __init__(self, operators=None, + open_parenthesis='(', close_parenthesis=')'): + """ + initializes the parser. + @type operators: list + @param operators: list of RPNOperator objects. + These are used to determine what is an operator in the + expression. + @type open_parenthesis: string + @param open_parenthesis: a symbol that will be interpreted as + a left (opening) parenthesis. default '(' + @type close_parenthesis: a symbol that will be interpreted as + a right (closing) parenthesis. default ')' + """ + self.open_parenthesis = open_parenthesis + self.close_parenthesis = close_parenthesis + self.operators = operators[:] + + def _infix_to_list(self, infix_expression): + ''' + This method changes infix_expression to the list to help parsing + by generate_rpm_expression. It's not meant to be used from outside. + It creates a list where every symbol/literal, operator or parenthesis are + a separate element in order to rpm expression generating method + @type infix_expression: str + @param infix_expression: Infix expression string + ''' + pos = 0 + expr_list = list() + slice_begin = 0 + #Get characters on which we should stop when parsing + first_chars = [operator.symbol[0] for operator in self.operators] + first_chars.append(self.open_parenthesis[0]) + first_chars.append(self.close_parenthesis[0]) + #map objects with their names first characters + obj_to_stop = dict((char,list()) for char in set(first_chars)) + map(lambda x: obj_to_stop[x.symbol[0]].append(x), self.operators) + obj_to_stop[self.open_parenthesis[0]].append(self.open_parenthesis) + obj_to_stop[self.close_parenthesis[0]].append(self.close_parenthesis) + while pos < len(infix_expression): + if infix_expression[pos] in obj_to_stop: + objects = obj_to_stop[infix_expression[pos]] + for obj in objects: + if isinstance(obj, RPNOperator): + length = len(obj.symbol) + symbol = obj.symbol + elif isinstance(obj, basestring): + length = len(obj) + symbol = obj + if infix_expression[pos:pos + length] == symbol: + if slice_begin < pos: + expr_list.append(infix_expression[slice_begin: pos]) + expr_list.append(obj) + pos += length + slice_begin = pos + break + else: + pos += 1 + else: + pos += 1 + expr_list.append(infix_expression[slice_begin:]) + return expr_list + + def check_parentheses(self, infix_expression): + counter = 0 + for ch in infix_expression: + if ch == self.open_parenthesis: + counter += 1 + if ch == self.close_parenthesis: + counter -= 1 + if counter < 0: + raise ParenthesisMismatchError + if counter: + raise ParenthesisMismatchError + + def generate_rpn_expression(self, infix_expression): + """ + parses the expression in infix notation. It needs properly defined + operators and parenthesis (@see: __init__). + @type infix_expression: str + @param infix_expression: infix expression to be parsed + @type: RPNExpression + @return: a RPNExpression object that is ready to be evaluated + (provided that operators are properly defined). + """ + #remove white characters + infix_expression = ''.join(infix_expression.split()) + #TODO: optimize, check, test + self.check_parentheses(infix_expression) + stack = [] + output = RPNExpression() + infix_expr_list = self._infix_to_list(infix_expression) + for atom in infix_expr_list: + if atom == self.open_parenthesis: + stack.append(atom) + elif atom == self.close_parenthesis: + element = stack.pop() + while str(element) != self.open_parenthesis: + output.append(element) + element = stack.pop() + elif atom in self.operators: + if (len(stack) == 0 or + str(stack[-1]) == self.open_parenthesis or + stack[-1].priority < atom.priority): + stack.append(atom) + elif (isinstance(stack[-1], RPNOperator) and + stack[-1].priority >= atom.priority): + output.append(stack.pop()) + while (len(stack) > 0 and + isinstance(stack[-1], RPNOperator) and + stack[-1].priority >= atom.priority): + output.append(atom.priority.pop()) + stack.append(atom) + else: + #this is an operand + output.append(atom) + output.extend(stack[::-1]) + stack = [] + return output + + +class RPNExpression(list): + def evaluate(self, values=None): + raise NotImplementedError + + def __str__(self): + return ' '.join([str(item) for item in self]) \ No newline at end of file diff --git a/src/brigde.c b/src/brigde.c new file mode 100644 index 0000000..88fb4b8 --- /dev/null +++ b/src/brigde.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include "brigde.h" + +// Function to remove spaces from a string +void remove_spaces(char *str) { + char *write_ptr = str; + char *read_ptr = str; + + while (*read_ptr) { + if (*read_ptr != ' ') { + *write_ptr++ = *read_ptr; + } + read_ptr++; + } + *write_ptr = '\0'; +} + +// Function to check if a string is a number (supports decimal and scientific notation) +int is_number(const char *str) { + if (*str == '-' || *str == '+') str++; // Handle negative or positive numbers + if (!*str) return 0; // Empty string is not a number + + int has_digit = 0, has_exponent = 0, has_decimal = 0; + + while (*str) { + if (isdigit(*str)) { + has_digit = 1; + } else if (*str == '.' && !has_decimal && !has_exponent) { + has_decimal = 1; // Allow one decimal point before exponent + } else if ((*str == 'e' || *str == 'E') && has_digit && !has_exponent) { + has_exponent = 1; // Allow one exponent + has_digit = 0; // Reset digit flag to ensure digits follow the exponent + if (*(str + 1) == '+' || *(str + 1) == '-') str++; // Skip exponent sign + } else { + return 0; // Invalid character for a number + } + str++; + } + + return has_digit; // Valid number must have at least one digit +} + +// Function to generate a unique letter not already in the string +char generate_unique_letter(char *str, char used_letters[], int *used_count) { + for (char c = 'a'; c <= 'z'; c++) { + if (!strchr(str, c) && !strchr(used_letters, c)) { + used_letters[*used_count] = c; + (*used_count)++; + return c; + } + } + return '?'; // Return a placeholder if no unique letter is found +} + +// Function to find numbers in an expression and replace them with unique letters +char* parse_and_replace_rpn(char *expression, Mapping mappings[], int *mapping_count) { + char number[32]; + int index = 0; + char result[256] = ""; + char used_letters[26] = {0}; // Track used letters + int used_count = 0; + + // Iterate over each character in the expression + for (int i = 0; expression[i] != '\0'; i++) { + if (isdigit(expression[i]) || expression[i] == '.') { + number[index++] = expression[i]; // Capture the number + } else { + if (index > 0) { + number[index] = '\0'; // Null-terminate the current number string + // Replace the number with a unique letter + char unique_letter = generate_unique_letter(expression, used_letters, &used_count); + if (unique_letter != '?') { + snprintf(result + strlen(result), sizeof(result) - strlen(result), "%c", unique_letter); + // Store the mapping + mappings[*mapping_count].letter = unique_letter; + snprintf(mappings[*mapping_count].number, sizeof(mappings[*mapping_count].number), "%s", number); + (*mapping_count)++; + } + index = 0; // Reset for the next number + } + strncat(result, &expression[i], 1); // Copy the non-number character to result + } + } + + // Handle case where the last part of the expression is a number + if (index > 0) { + number[index] = '\0'; + char unique_letter = generate_unique_letter(expression, used_letters, &used_count); + if (unique_letter != '?') { + snprintf(result + strlen(result), sizeof(result) - strlen(result), "%c", unique_letter); + // Store the mapping + mappings[*mapping_count].letter = unique_letter; + snprintf(mappings[*mapping_count].number, sizeof(mappings[*mapping_count].number), "%s", number); + (*mapping_count)++; + + } + } + remove_spaces(result); + return strdup(result); // Return the dynamically allocated string +} + +// Function to replace letters with their corresponding numbers in a string +void replace_letters_with_numbers(char *input, Mapping mappings[], int mapping_count) { + char result[256] = ""; + const char *ptr = input; + + while (*ptr) { + int replaced = 0; + for (int i = 0; i < mapping_count; i++) { + if (*ptr == mappings[i].letter) { + strcat(result, mappings[i].number); + replaced = 1; + break; + } + } + if (!replaced) { + strncat(result, ptr, 1); + } + ptr++; + } + + printf("Replaced string: %s\n", result); +} + +// int main() { +// char *expression = "( 1 /((x^ 2 )+ 100 ))"; // Example expression +// Mapping mappings[32]; // Array to store letter-number mappings +// int mapping_count = 0; + +// char *parsed_expression = parse_and_replace_rpn(expression, mappings, &mapping_count); +// printf("Parsed and replaced expression: %s\n", parsed_expression); + +// replace_letters_with_numbers(parsed_expression, mappings, mapping_count); + +// free(parsed_expression); // Free dynamically allocated memory +// return 0; +// } diff --git a/src/main.c b/src/main.c index b76075f..7170c0a 100644 --- a/src/main.c +++ b/src/main.c @@ -2,12 +2,15 @@ #include #include #include +#include "infix_to_rpn.h" +#include "brigde.h" #include "utilities.h" #include "rpn_to_infix.h" -#include "infix_to_rpn.h" int main(void){ + while (1){ char expr[500]; + printf("\n========== RPN To Infix Converter ==========\n"); printf("Valid characters: lowercase alphabet (a-z), +, -, *, /, ^, (, and )\n"); printf("Please input your expression to convert: "); @@ -29,32 +32,45 @@ int main(void){ expr[strlen(expr) - 1] = '\0'; } - printf("[1]: Convert from Reverse Polish Notation to Infix Notation\n"); - printf("[2]: Convert from Infix Notation to Reverse Polish Notation\n\n"); - printf("Your selection: "); + // printf("[1]: Convert from Reverse Polish Notation to Infix Notation\n"); + // printf("[2]: Convert from Infix Notation to Reverse Polish Notation\n\n"); + // printf("Your selection: "); - uint8_t option = getchar(); + // uint8_t option = getchar(); - while ((option != '1') && (option != '2')){ - if (option != '\n'){ - printf("Invalid option. Please select either '1' or '2': "); - } - option = getchar(); - } + // while ((option != '1') && (option != '2')){ + // if (option != '\n'){ + // printf("Invalid option. Please select either '1' or '2': "); + // } + // option = getchar(); + // } printf("\n"); - - if (option == '1'){ - char *result = rpn_to_infix(expr); - printf("RPN: %s\n",expr); + Mapping mappings[26]; + int mapping_count = 0; + char *exprs = parse_and_replace_rpn(expr, mappings, &mapping_count); + // printf(exprs); + + + if (is_valid_rpn_expr(exprs)){ + char *result = rpn_to_infix(exprs); + printf("RPN: %s\n",exprs); + replace_letters_with_numbers(result, mappings, mapping_count); printf("Infix: %s\n",result); free(result); - } else if (option == '2'){ - char *result = infix_to_rpn(expr); - printf("Infix: %s\n",expr); + } else if (is_valid_infix_expr(exprs)){ + char *result = infix_to_rpn(exprs); + printf("Infix: %s\n",exprs); + replace_letters_with_numbers(result, mappings, mapping_count); printf("RPN: %s\n",result); free(result); } + else{ + print_error("Invalid RPN or Infix expression"); + } + printf("\n"); + free(exprs); + } return 0; }