| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Worked on the Interpreter for questions and Parser skeleton
Worked on the Parser skeleton and list functions
Worked on the utility functions and wrote the list functions.
First, clone the repo and build the project.
git clone cd ExamNavigator mkdir bin; make
Now, the program can be used as such
bin/ExamNavigator -i <question_bank_file> -u <user_params_file>
This will generate the question paper and print it.
This implementation of the 'ExamNavigator' language has a strict requirement of only
one curly brace assignment per line, and a blank line to separate question
blocks.
It is freely able to handle 'MiXeD cAse' and excess trailing and leading
whitespaces without errors.
The general syntax for the question bank file can be seen here
\question
{type = mcq}
{difficulty = 0.2}
{text = C is a language}
{opt=Functional,Procedural,Objects,Events}
{ans=Functional}
The general syntax for the user parameters file can be seen here
\sample
{type = mcq}
{difficulty <= 0.2}
{number = 5}
The parser has been designed in a generalized manner for the most part,
but the two types of input files, question bank and user parameters,
have been parsed separately.
Since the line containing the "difficulty" directive in the user parameters
contains two pieces of information, the target difficulty and the comparator,
it also had to be dealt with specially.
In general however, the parser simply takes in one line of input, keeps track of whether it is in a question block and the line numbers of the files, and calls assign() from utils for the assignment in case of question bank files, and handles it locally in case of user parameter files. It also raises errors where possible.
The functions involved in the Utils are :
void assign(Question_B question, char string_before[], char string_after[]);
void assignType(Question_B question, char *string_after);
void assignDiff(Question_B question, char *string_after);
void assignOpt(Question_B question, char *string_after);
void assignAns(Question_B question, char *string_after);
char *stripWhitespace(char *line, int *length);
bool isBlankLine(char *line);
int hashString(const char *str);
int insertString(const char *str, int n, string_node *hashtable[]);
int findString(const char *str, int n, string_node *hashtable[]);So, In this way, each of the functions mentioned above are a part of the Utils and operate as a medium between Parser and Interpreter.
Utils is the part of the program which controls the manipulation and storage of the Input Data that is provided by the User to Parser.
This is the header file for the interpreter.c of the project. It contains the structs “Question”, the “Question_Bank” and the “User_Parameter”. The struct Question contains information about the question such as the text, options, and the final answer. The struct Question_Bank contains the collection of questions in an array and also has a variable to store the total number of questions. The User_Parameter struct stores the requirements and the conditions for the final question paper.
This file contains the functions which are used to create the final question paper from the large question bank following the requirements of the user.
int comparator_fn_diff(double A, double B, char comparator[2]);
int comparator_fn_type(char *Question_type, User_Parameters *UP);
int get_Questions(User_Parameters *UP, Question_Bank *QB);
int create_QuestionPaper(Question **Question_collection, int size, int N);
int print_QuestionPaper(Question **QuestionPaper, int N);The get_Questions function takes the struct User_Parameter and struct Question_Bank as parameters and chooses “all” the questions that satisfy the given conditions of type and difficulty and store them in an array of struct questions.
Time Complexity: As we traverse through the whole Question_Bank in a loop the time complexity would be O(N).
For checking if the questions satisfy our conditions in the get_Questions function we use two functions in the get_Questions function which are comparator_fn_diff and comparator_fn_type. The comparator_fn_diff takes the difficulty level of the question and the difficulty level asked in the question paper and a comparator for deciding which comparator to use, to compare both the difficulties. It returns 1 if the required condition is satisfied.
Time Complexity: This takes O(1) time as it is just comparing two variables.
Similarly, this function takes the question type and the User_Parameter struct as parameters to compare the type of the question and the required type. It returns 1 if the required condition is satisfied.
Time Complexity: This also takes O(1) time as it is just comparing two variables.
We have already chosen the questions that follow our type and difficulty level. This function picks randomly the number of questions (using the rand and srand in time.h) that the user wants and while picking we take care of repetitions by using a hash table, and puts these questions into the final QP struct (the question paper struct).
Time Complexity: We traverse through the while loop till we have the required number of questions and also we might also have to traverse for the linear probing (can't be more than N) so the total time complexity would be O(N*N).
The data structures which are mainly used are structs and arrays of structs. We have also used Hashtables to ensure that the questions are not repeated.
'list' is a generic container to act as a dynamic array, similar to C++'s vector template. Functions like listAppend(), listInsertAt(), and listDeleteAt() are available to modify data held in a list.
A set of wrappers for specific data types can be generated, which call
underlying generic functions.
In both cases,
a list l must be initialized with listInit(&l, sizeof(data_type)
and destroyed after use with listDestroy(&l)
For the wrapper functions, add the line GENERATE_LIST_WRAPPER(data_type) near the end of src/listwrapper.h. The data type must itself be accessible from the headerfile. See for example:
// in listwrapper.h
typedef struct __test_struct {
int a;
char c;
} test_struct;
GENERATE_LIST_WRAPPER(test_struct);The struct was declared in place so is accessible, and a set of wrappers for it is generated.
#include "listwrapper.h"
#include <stdio.h>
int main()
{
list l;
listInit(&l, sizeof(test_struct));
for (int i = 0; i < 10; i++) {
test_struct cur;
cur.a = i;
cur.c = 'a' + i;
test_structListAppend(&l, cur);
}
for (int i = 0; i < 10; i++) {
test_struct out = test_structListAt(&l, i);
printf("%d %c\n", out.a, out.c);
}
putchar('\n');
listDestroy(&l);
return 0;
}For accessing data stored in a list, listAt() is used, which returns a void pointer to the specified index. This pointer must be typecast to the correct data type to be used properly.
#include "list.h"
list l;
listInit(&l, sizeof(int)); // a list to store integers
int x = 10;
listAppend(&l, &x); // a pointer to the data must be passed
// (l.num_elements == 1) and l.empty is false
int y = *(int *)listAt(&l);
// the pointer returned is typecast and dereferenced
// now (y == 10)
listDeleteAt(&l, 0);
// (l.num_elems == 0) and l.empty is true
listDestroy(&l); // free the memory associated with the list| Back | FazBrowse Home | New Git URL |