From 6568f1cc5734193753bfbd2816db915620b28444 Mon Sep 17 00:00:00 2001 From: kappa Date: Sun, 20 Jan 2019 23:48:12 +0100 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B4=D0=B0=D1=82=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D1=80=D1=88=D0=BA=D0=B0=20=D0=B7=D0=B0=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BB=D0=BE=D0=BC=D0=B0=D1=87=D0=BA=D1=83=20=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=BB=D1=83=D1=82=D0=B0=D1=98=D1=83=D1=9B=D0=BE-?= =?UTF-8?q?=D1=82=D0=B0=D1=87=D0=BA=D0=B0=D1=81=D1=82=D1=83=20=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D1=82=D0=BC=D0=B5=D1=82=D0=B8=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internals.c | 33 +++++----- print.c | 15 ++++- read.c | 47 ++++++++++---- util.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 38 +++++++++++- 5 files changed, 273 insertions(+), 35 deletions(-) diff --git a/internals.c b/internals.c index db8b2f7..a2e2b91 100644 --- a/internals.c +++ b/internals.c @@ -26,7 +26,7 @@ object add(object parameters) } else if (listLength(parameters) == 0) { - NUM(result) = 0LL; + result = longlongToNumber(0LL); } else { @@ -34,7 +34,7 @@ object add(object parameters) first = CAR(parameters); rest = add(CDR(parameters)); - NUM(result) = NUM(first) + NUM(rest); + result = plusNum(first, rest); } return result; @@ -57,11 +57,12 @@ object subtract(object parameters) } else if (listLength(parameters) == 1) { - NUM(result) = -NUM(CAR(parameters)); + result = minusNum(CAR(parameters)); } else { - NUM(result) = NUM(CAR(parameters)) - NUM(add(CDR(parameters))); + result = plusNum(CAR(parameters), + minusNum(add(CDR(parameters)))); } return result; @@ -79,7 +80,7 @@ object multiply(object parameters) } else if (listLength(parameters) == 0) { - NUM(result) = 1LL; + result = longlongToNumber(1LL); } else { @@ -87,7 +88,7 @@ object multiply(object parameters) first = CAR(parameters); rest = multiply(CDR(parameters)); - NUM(result) = NUM(first) * NUM(rest); + result = timesNum(first,rest); } return result; @@ -110,27 +111,21 @@ object divide(object parameters) } else if (listLength(parameters) == 1) { - if (NUM(CAR(parameters)) != 0) - { - NUM(result) = 1/NUM(CAR(parameters)); - } - else - { - TYPE(result) = errorObject; - ERR(result) = divisionByZeroError; - } + result = inverseNum(CAR(parameters)); } else { - if (NUM(multiply(CDR(parameters))) != 0) + object check = inverseNum(multiply(CDR(parameters))); + if (TYPE(check) != errorObject) { - NUM(result) = NUM(CAR(parameters))/NUM(multiply(CDR(parameters))); + result = timesNum(CAR(parameters), + inverseNum(multiply(CDR(parameters)))); } else { - TYPE(result) = errorObject; - ERR(result) = divisionByZeroError; + result = check; } + result = shortenFractionNum(result); } return result; diff --git a/print.c b/print.c index 11fada7..46a673c 100644 --- a/print.c +++ b/print.c @@ -23,7 +23,7 @@ void print(object input) } else { - printf("\n; Value: "); + printf("\n"); printValue(input); printf("\n\n"); } @@ -37,7 +37,18 @@ void printValue(object input) } else if (input.type == numberObject) { - printf("%lld", NUM(input)); + if (NUM_TYPE(input) == fractionNum) + { + printf("%lld", NUM_NUMER(input)); + if (NUM_DENOM(input) != 1) + { + printf("/%lld", NUM_DENOM(input)); + } + } + else + { + printf("%LF", NUM_REAL(input)); + } } else if (input.type == symbolObject) { diff --git a/read.c b/read.c index 7f0192a..1aaacb2 100644 --- a/read.c +++ b/read.c @@ -11,7 +11,8 @@ typedef enum { undefinedToken, - numberToken, + numberFracToken, + numberRealToken, symbolToken, quoteToken, lParenthesisToken, @@ -131,8 +132,8 @@ void append(token **head, token *tail) *current = tail; } -regex_t regNumber, regSymbol, regLParenthesis, regRParenthesis, regSpace, - regQuote; +regex_t regNumberFrac, regNumberReal, regLParenthesis, regRParenthesis, + regSpace, regSymbol, regQuote; token *lex1Token(char *input, int *i) { @@ -141,7 +142,10 @@ token *lex1Token(char *input, int *i) regcomp(®Space, "^[[:space:]]*", REG_EXTENDED); - regcomp(®Number, "^[-+]?[[:digit:]]+", REG_EXTENDED); + regcomp(®NumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?", + REG_EXTENDED); + regcomp(®NumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+", + REG_EXTENDED); regcomp(®Symbol, "^[-+/*_\\\\=<>!&?[:alnum:]]+", REG_EXTENDED); regcomp(®Quote, "^'", REG_EXTENDED); regcomp(®LParenthesis, "^\\(", REG_EXTENDED); @@ -154,16 +158,20 @@ token *lex1Token(char *input, int *i) *i += a[0].rm_eo; /* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */ - if (!regexec(®Symbol, input + *i, nmatches, a, 0)) + if (!regexec(®NumberReal, input + *i, nmatches, a, 0)) + { + result->type = numberRealToken; + } + else if (!regexec(®Symbol, input + *i, nmatches, a, 0)) { int tmp = a[0].rm_eo; - if (!regexec(®Number, input + *i, nmatches, a, 0) && + if (!regexec(®NumberFrac, input + *i, nmatches, a, 0) && tmp == a[0].rm_eo) /* симбол може садржати цифре на било којој позицији али не може сам бити број * не постоји погодан начина да се ово путем regex-a запише стога овај if * исказ */ { - result->type = numberToken; + result->type = numberFracToken; } else { @@ -173,6 +181,10 @@ token *lex1Token(char *input, int *i) result->type = symbolToken; } } + else if (!regexec(®NumberFrac, input + *i, nmatches, a, 0)) + { + result->type = numberFracToken; + } else if (!regexec(®Quote, input + *i, nmatches, a, 0)) { result->type = quoteToken; @@ -202,7 +214,8 @@ token *lex1Token(char *input, int *i) skipStringCopy: regfree(®Space); - regfree(®Number); + regfree(®NumberFrac); + regfree(®NumberReal); regfree(®Symbol); regfree(®LParenthesis); regfree(®RParenthesis); @@ -249,11 +262,22 @@ object parseExpression(token **inputList) /* скида први преостали токен са листе унесених, да би се могао * прерадити */ - if (input.type == numberToken) + if (input.type == numberFracToken) { TYPE(result) = numberObject; - NUM(result) = atoll(input.lexeme); - return result; + NUM_TYPE(result) = fractionNum; + NUM_NUMER(result) = atoll(input.lexeme); + char *tmp; + NUM_DENOM(result) = (tmp = strchr(input.lexeme, '/')) == NULL ? + 1 : atoll(tmp + 1); + + result = shortenFractionNum(result); + } + else if (input.type == numberRealToken) + { + TYPE(result) = numberObject; + NUM_TYPE(result) = realNum; + NUM_REAL(result) = strtold(input.lexeme, NULL); } else if (input.type == symbolToken) { @@ -262,7 +286,6 @@ object parseExpression(token **inputList) * sizeof(char)); /* Алокација стринга */ strcpy(SYM(result), input.lexeme); - return result; } else if (input.type == quoteToken) { diff --git a/util.c b/util.c index f520c58..a8e03f7 100644 --- a/util.c +++ b/util.c @@ -70,3 +70,178 @@ object copyObject(object input) return result; } + +object longlongToNumber(long long int input) +{ + object result; + TYPE(result) = numberObject; + NUM_TYPE(result) = fractionNum; + NUM_NUMER(result) = input; + NUM_DENOM(result) = 1LL; + + return result; +} + +object exactToInexactNum(object a) +{ + object result = copyObject(a); + + if (TYPE(result) == numberObject && NUM_TYPE(result) == fractionNum) + { + NUM_TYPE(result) = realNum; + NUM_REAL(result) = (long double) NUM_NUMER(result) / + (long double) NUM_DENOM(result); + } + + return result; +} + +long long int gcd(long long int a, long long int b) +/* највећи заједнички делилац */ +{ + if (b == 0LL) + { + return a; + } + else + { + return gcd(b, a - b * (a / b)); + } +} +long long int lcm(long long int a, long long int b) +/* најмањи заједнички садржалац */ +{ + if (a == 0LL && b == 0LL) + { + return 0L; + } + else + { + return llabs(a * b) / gcd(a, b); + } +} + +object shortenFractionNum(object a) +/* скраћује разломак, враћа грешку ако је неправилан разломак, уколико улаз + * заиста јесте дат као разломак */ +{ + object result = copyObject(a); + + if (TYPE(result) == numberObject && NUM_TYPE(result) == fractionNum) + { + if (NUM_DENOM(result) == 0) + { + deleteObject(result); + TYPE(result) = errorObject; + ERR(result) = divisionByZeroError; + } + else if (NUM_NUMER(result) == 0) + { + NUM_DENOM(result) = 1; + } + else + { + long long int divisor = + gcd(NUM_NUMER(result), NUM_DENOM(result)); + NUM_NUMER(result) /= divisor; + NUM_DENOM(result) /= divisor; + } + } + + return result; +} + +object plusNum(object a, object b) +{ + object result; + TYPE(result) = numberObject; + + if (NUM_TYPE(a) == fractionNum && NUM_TYPE(b) == fractionNum) + { + NUM_TYPE(result) = fractionNum; + NUM_NUMER(result) = NUM_NUMER(a) * NUM_DENOM(b) + NUM_NUMER(b) * NUM_DENOM(a); + NUM_DENOM(result) = NUM_DENOM(a) * NUM_DENOM(b); +/* + * TODO: имплементирати оптималнији начин множења разломака + long long int denominator = lcm(NUM_DENOM(a), NUM_DENOM(b)); + NUM_NUMER(result) = + NUM_NUMER(a) * (denominator / NUM_DENOM(a)) + + NUM_NUMER(b) * (denominator / NUM_DENOM(b)); +*/ + result = shortenFractionNum(result); + } + else + { + NUM_TYPE(result) = realNum; + NUM_REAL(result) = NUM_REAL(exactToInexactNum(a)) + + NUM_REAL(exactToInexactNum(b)); + } + + return result; +} + +object minusNum(object a) +{ + object result; + result = copyObject(a); + + if (NUM_TYPE(result) == fractionNum) + { + NUM_NUMER(result) = -NUM_NUMER(result); + } + else if (NUM_TYPE(result) == realNum) + { + NUM_REAL(result) = -NUM_REAL(result); + } + + return result; +} + +object timesNum(object a, object b) +{ + object result; + TYPE(result) = numberObject; + + if (NUM_TYPE(a) == fractionNum && NUM_TYPE(b) == fractionNum) + { + NUM_TYPE(result) = fractionNum; + NUM_NUMER(result) = NUM_NUMER(a) * NUM_NUMER(b); + NUM_DENOM(result) = NUM_DENOM(a) * NUM_DENOM(b); + result = shortenFractionNum(result); + } + else + { + NUM_TYPE(result) = realNum; + NUM_REAL(result) = NUM_REAL(exactToInexactNum(a)) * + NUM_REAL(exactToInexactNum(b)); + } + + return result; +} + +object inverseNum(object a) +{ + object result; + result = copyObject(a); + + if (NUM_TYPE(result) == fractionNum) + { + if (NUM_NUMER(result) == 0) + { + deleteObject(result); + TYPE(result) = errorObject; + ERR(result) = divisionByZeroError; + } + else + { + NUM_NUMER(result) = NUM_DENOM(a); + NUM_DENOM(result) = NUM_NUMER(a); + } + } + else if (NUM_TYPE(result) == realNum) + { + NUM_REAL(result) = 1.0L/NUM_REAL(result); + } + + return result; +} diff --git a/util.h b/util.h index 204e6b2..eda3c07 100644 --- a/util.h +++ b/util.h @@ -5,10 +5,15 @@ #define CONS(x) ((x).value.consCell) #define CAR(x) (((x).value.consCell)->car) #define CDR(x) (((x).value.consCell)->cdr) -#define NUM(x) ((x).value.number) #define SYM(x) ((x).value.symbol) #define ERR(x) ((x).value.err) +#define NUM(x) ((x).value.num) +#define NUM_TYPE(x) ((x).value.num.type) +#define NUM_NUMER(x) ((x).value.num.value.fraction.numerator) +#define NUM_DENOM(x) ((x).value.num.value.fraction.denominator) +#define NUM_REAL(x) ((x).value.num.value.real) + typedef enum { nilObject, @@ -32,6 +37,26 @@ typedef enum typedef struct object object; typedef struct cons cons; +typedef enum +{ + fractionNum, + realNum +} numType; + +typedef struct number +{ + numType type; + union + { + long double real; + struct + { + long long int numerator; + long long int denominator; + } fraction; + } value; +} number; + struct object { dataType type; @@ -39,8 +64,8 @@ struct object { error err; char *symbol; - long long int number; cons *consCell; + number num; } value; }; @@ -54,3 +79,12 @@ int properList(object list); int listLength(object list); void deleteObject(object input); object copyObject(object input); + + +object longlongToNumber(long long int input); +object shortenFractionNum(object a); +object exactToInexactNum(object a); +object plusNum(object a, object b); +object minusNum(object a); +object timesNum(object a, object b); +object inverseNum(object a);