Files
dsa/assignment1/problem3/uncompress.c

324 lines
6.7 KiB
C

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"
#include "uncompress.h"
ASTScanner *CreateASTScanner(char *string) {
/* Special case define in pdf */
if (string == NULL) {
string = "NULL";
}
/* allocate scanner memory space */
ASTScanner *scanner = (ASTScanner *)malloc(sizeof(ASTScanner));
if (scanner == NULL) {
fprintf(stderr, "allocate scanner memory failed\n");
exit(1);
}
/* init values */
scanner->string = string;
scanner->length = strlen(string);
scanner->current_index = 0;
return scanner;
}
char ASTScan(ASTScanner *scanner, ASTNode **ret_node) {
size_t i;
/* return if it reached the end */
if (scanner->current_index == scanner->length) {
*ret_node = NULL;
return 0;
}
/* allocate memory space */
ASTNode *node = (ASTNode *)calloc(1, sizeof(ASTNode));
if (node == NULL) {
fprintf(stderr, "allocate ASTNode memory failed\n");
exit(1);
}
/* read until end or '(' or ')',
* after the loop, i point after '(' or ')' */
i = scanner->current_index;
while (i < scanner->length) {
if (scanner->string[i] == '(' || scanner->string[i] == ')') {
i++;
break;
}
i++;
}
/* if read ( */
if (scanner->string[i - 1] == '(') {
node->type = 2;
/* check is there is any number before ( */
if (i - 1 == 0) {
fprintf(stderr, "Syntax error: invailed value of repeat at begining\n");
return 1;
}
if (!IsNumber(scanner->string[i - 2])) {
fprintf(stderr, "Syntax error: invailed value of repeat at index %ld\n",
i - 1);
return 1;
}
/* get number and the string before the number */
node->repeat = GetNumber(scanner->string, scanner->current_index, i - 1,
&node->string);
/* if the string exists */
if (node->string.string) {
/* change type to 1 */
node->type = 1;
node->repeat = 0;
i = node->string.end;
}
/* if read ) */
} else if (scanner->string[i - 1] == ')') {
node->type = 3;
/* if there are string before ) */
if (i - 1 != scanner->current_index) {
/* change type to 1 */
node->type = 1;
node->string.string = scanner->string;
node->string.start = scanner->current_index;
node->string.end = i - 1;
i = i - 1;
}
/* until the end is a string */
} else {
node->type = 1;
node->string.string = scanner->string;
node->string.start = scanner->current_index;
node->string.end = i;
}
/* set current_index */
scanner->current_index = i;
/* set return node */
*ret_node = node;
return 0;
}
unsigned int GetNumber(char *string, size_t start, size_t end,
ASTString *other_string) {
unsigned int result = 0;
size_t index = end - 1;
/* convert string to integer */
for (unsigned count = 1; IsNumber(string[index]); index--, count++) {
result = result + (string[index] - '0') * Power(10, count);
}
/* if there is a string before the number,
* set to other_string */
if (index + 1 != start) {
other_string->string = string;
other_string->start = start;
other_string->end = index + 1;
}
return result;
}
bool IsNumber(const char c) { return c >= '0' && c <= '9'; }
unsigned int Power(int x, int y) {
unsigned int result = 1;
for (int i = 1; i < y; i++) {
result = result * x;
}
return result;
}
void DEBUG_NODE(ASTNode *root) {
ASTNode *currNode = root;
while (currNode) {
printf("[%p] type: %d, next: %p, child: %p, repeat: %d, string: %ld-%ld\n",
currNode, currNode->type, currNode->next, currNode->child,
currNode->repeat, currNode->string.start, currNode->string.end);
if (currNode->type == 2) {
/* recursion to child */
DEBUG_NODE(currNode->child);
}
/* move forward */
currNode = currNode->next;
}
}
void DestroyASTTree(ASTTree *tree) {
ASTNode *currNode, *delNode;
/* push root node into stack */
Push(&tree->stack, tree->root);
/* loop to destroy all node */
while (!IsEmpty(&tree->stack)) {
/* get node from stack */
Pop(&tree->stack, &currNode);
while (currNode) {
if (currNode->type == 2) {
/* store child node into stack */
Push(&tree->stack, currNode->child);
}
/* delete node */
delNode = currNode;
currNode = currNode->next;
free(delNode);
}
}
/* finally do not forget to free everything */
DestroyStack(&tree->stack);
free(tree);
}
ASTTree *CreateASTTree() {
/* allocate memory */
ASTTree *tree = (ASTTree *)calloc(1, sizeof(ASTTree));
if (tree == NULL) {
fprintf(stderr, "Can not allocate AST Tree memory\n");
exit(1);
}
/* create stack with tree */
CreateStack(&tree->stack, DEFAULT_STACK_SIZE);
return tree;
}
void ASTCompileTree(ASTTree *tree, FILE *file) {
ASTCompileNode(tree->root, file);
/* new line at the end */
fputc('\n', file);
}
void ASTCompileNode(ASTNode *root, FILE *file) {
ASTNode *currNode = root;
/* loop for node and its all next node */
for (; currNode; currNode = currNode->next) {
if (currNode->type == 2) {
/* repeat to compile child node */
for (int i = 0; i < currNode->repeat; i++) {
/* recursion */
ASTCompileNode(currNode->child, file);
}
} else if (currNode->type == 1) {
/* print string */
for (size_t i = currNode->string.start; i < currNode->string.end; i++) {
fputc(currNode->string.string[i], file);
}
}
}
}
char ASTScanAll(ASTTree *tree, ASTScanner *scanner) {
bool goback = false;
char err;
ASTNode *node = NULL, *last = NULL;
/* scan the first (root) node */
ASTScan(scanner, &node);
tree->root = node;
tree->max++;
last = tree->root;
/* set last node as last type 2 node */
if (node != NULL) {
if (node->type == 2) {
Push(&tree->stack, node);
}
}
/* scan until end */
while (err = ASTScan(scanner, &node), node) {
/* raise error */
if (err != 0) {
return err;
}
/* set goback flag, set last node as last type 2 node */
if (node->type == 3) {
goback = true;
Pop(&tree->stack, &last);
free(node);
continue;
/* uf node type is 2, record in stack */
} else if (node->type == 2) {
Push(&tree->stack, node);
}
/* connect to last node */
if (last->type == 1) {
last->next = node;
} else if (last->type == 2) {
if (goback) {
last->next = node;
goback = false;
} else {
last->child = node;
}
}
/* record last node, increase max */
last = node;
tree->max++;
}
return 0;
}
void Uncompress(char *string, FILE *file) {
char err;
/* create scanner and AST tree */
ASTScanner *scanner = CreateASTScanner(string);
ASTTree *tree = CreateASTTree();
/* scan all token into tree */
err = ASTScanAll(tree, scanner);
if (err != 0) {
fprintf(stderr, "EXITED");
exit(1);
}
/*
printf("---debug---\n");
DEBUG_NODE(tree->root);
printf("---debug---\n");
*/
/* compile */
ASTCompileTree(tree, file);
/* free memory */
DestroyASTTree(tree);
DestroyASTScanner(scanner);
}
void DestroyASTScanner(ASTScanner *scanner) { free(scanner); }