From fb9f396240e54dda79193cc24e16d0e68eddc4f1 Mon Sep 17 00:00:00 2001 From: Curtis F Kaylor Date: Thu, 2 Aug 2018 00:15:32 -0400 Subject: [PATCH] Added logical operators 'and' and 'or' to conditionals --- doc/c02.txt | 64 +++++++++++++++++++++++++------------ doc/c02vsC.txt | 16 ++++++++++ py65/testif.c02 | 84 ++++++++++++++++++++++++++++++++++++++++++++----- src/c02.c | 1 + src/common.c | 17 +++++++++- src/common.h | 8 +++-- src/cond.c | 79 ++++++++++++++++++++++++++++++++-------------- src/cond.h | 5 +++ src/label.h | 1 + src/parse.h | 2 +- src/stmnt.c | 6 ++-- 11 files changed, 226 insertions(+), 57 deletions(-) diff --git a/doc/c02.txt b/doc/c02.txt index 8c895b9..e96ec17 100644 --- a/doc/c02.txt +++ b/doc/c02.txt @@ -420,10 +420,10 @@ Accumulator. However, due to the 6502 having only one Accumulatorm which is used for all operations between two bytes, there is no simple system agnostic method for allowing function calls in subsequent terms. -EVALUATIONS +CONTENTIONS -An evaluation is a construct which generates either TRUE or FALSE condition. -It may be an expression, a comparison, or a test. +An contention is a construct which generates either TRUE or FALSE condition, +and may be an expressions, comparisons, or test. A stand-alone expression evaluates to TRUE if the result is non-zero, or FALSE if the result is zero. @@ -455,8 +455,8 @@ a positive value upon succesful completion and a negative value if an error was encounters. They compile into smaller code than would be generated using the equivalent comparison operators. -A comparison may be preceded by negation operator (a ! character), which -reverses the meaning of the entire comparison. For example, +An contention may be preceded by negation operator (the ! character), which +reverses the result of the entire contention. For example: ! expr evaluates to TRUE if expr is zero, or FALSE if it is non-zero; while ! expr = term @@ -464,7 +464,7 @@ evaluates to TRUE if expr and term are not equal, or FALSE if they are; and ! expr :+ evaluates to TRUE if expr is negative, or FALSE if it is positive -Note: Evaluations are compiled directly into 6502 conditional branch +Note: Contentions are compiled directly into 6502 conditional branch instructions, which precludes their use inside expressions. Standalone expressions and test-ops generate a single branch instruction, and therefore result in the most efficient code. Comparisons generate a @@ -473,6 +473,29 @@ generate one, while <= and > generate two). A preceding negation operator will switch the number of branch instructions used in a comparison, but otherwise does not change the size of the generated code. +CONDITIONALS + +A conditional consists of one or more contentions joined with the +conjunctors "and" and "or". + +If only one contention is present, the result of the conditional is the +same as the result of the contention. + +If two contentions are joined with "and", then the conditional is true only +if both of the contentions are true. If either or both of the contentions +are false, then the conditional is false. + +If two contentions are joined with "or", then the conditional is true if +either or both of the contentions are true. If both of the contentions are +false, then the conditional is false. + +When more three or more contentions are chained together, the conjunctors +are evaluated in left to right order, using short-circuit evaluation. If +the contention to the left of an "and" is false, then the entire conditional +evaluates to false, and if the contention to the left of an "or" is true, +then the entire conditional evaluates to true. In either case, no further +contentions in the conditional are evaluated. + ARRAY SUBSCRIPTS Individual elements of an array are accessed using subscript notation. @@ -665,9 +688,9 @@ Examples: SHORTCUT-IFS -A shortcut-if is a special form of assignment consisting of an evaluation +A shortcut-if is a special form of assignment consisting of an contention and two expressions, of which one will be assigned based on the result -of the evaluation. A shortcut-if is written as a condition surrounded +of the contention. A shortcut-if is written as a condition surrounded by ( and ) characters, followed by a ? character, the expression to be evaluated if the condition was true, a : character, and the expression to be evaluated if the condition was false. @@ -757,12 +780,13 @@ IF AND ELSE STATEMENTS The if then and else statements are used to conditionally execute blocks of code. -When using the if keyword, it is followed by an evaluation (surrounded by -parenthesis) and the block of code to be executed if the evaluation was true. +When using the if keyword, it is followed by a conditional (surrounded by +parenthesis) and the block of code to be executed if the conditional was +true. An else statement may directly follow an if statement (with no other executable code intervening). The else keyword is followed by the block -of code to be executed if the evaluation was false. +of code to be executed if the conditional was false. Examples: if (c = 27) goto end; @@ -850,13 +874,13 @@ select expression will be executed. WHILE LOOPS The while statement is used to conditionally execute code in a loop. When -using the while keyword, it is followed by an evaluation (surrounded by -parenthesis) and the the block of code to be executed while the evaluation -is true. If the evaluation is false when the while statement is entered, +using the while keyword, it is followed by a conditional (surrounded by +parenthesis) and the the block of code to be executed while the conditional +is true. If the conditional is false when the while statement is entered, the code in the block will never be executed. Alternatively, the while keyword may be followed by a pair of empty -parenthesis, in which case an evaluation of true is implied. +parenthesis, in which case a conditional of true is implied. Examples: c = 'A' ; while (c <= 'Z') {putc(c); c++;} //Print letters A-Z @@ -869,10 +893,10 @@ DO WHILE LOOPS The do statement used with to conditionally execute code in a loop at least once. When using the do keyword, it is followed by the block of -code to be executed, a while statement, an evaluation (surrounded +code to be executed, a while statement, a conditional (surrounded by parenthesis), and a terminating semicolon. -A while statement that follows a do loop must contain an evaluation. +A while statement that follows a do loop must contain a conditional. The while statement is evaluated after each iteration of the loop, and if it is true, the code block is repeated. @@ -893,7 +917,7 @@ a set of values. When using the if keyword, it is followed by a pair of parenthesis containing an initialization assignment statement (which is executed once), -a semicolon separator, an evaluation (which determines if the code block +a semicolon separator, a conditional (which determines if the code block is executed), another semicolon separator, and an increment assignment (which is executed after each iteration of the code block). This is then followed by the block of code to be conditionally executed. @@ -926,8 +950,8 @@ break keyword, it is followed with a trailing semicolon. When a continue statement is encountered, program execution is transferred to the beginning of the block associated with the innermost do, for, or while statement. In the case of a for statement, the increment assignment -is executed, followed by the evaluation, and in the case of a while -statement, the evaluation is executed. When using the continue keyword, it +is executed, followed by the conditional, and in the case of a while +statement, the conditional is executed. When using the continue keyword, it is followed with a trailing semicolon. Examples: diff --git a/doc/c02vsC.txt b/doc/c02vsC.txt index cc2f985..0594ce0 100644 --- a/doc/c02vsC.txt +++ b/doc/c02vsC.txt @@ -78,6 +78,22 @@ a function call as the first term). The sizeof operator in C02 is the at sign @. It may only be used with declared variables and struct members, not types. +CONDITIONALS + +Conditional operations, including comparisons and logical operators, +may not be used within or in place of normal expressions, and are only +allowed in if, while, and for statements, as well as shortcut-ifs. + +The comparison operators are ==, <, <=, >=, >, and <>. Unlike standard +C, the not-equals operator is <> instead of !=. In addition, = may be +substituted for ==. The logical not operator ! negates an entire +comparison rather than an individual term within the comparison, thus +the C02 code !b +#include +#include #include +#include +#include -char true = $FF; -char false = $00; -char pass = " Passed"; -char fail = " Failed"; +char n0=0, n1=1, n2=2, nff=255; +char r,s; //Result & Summary main: -puts("if (true): "); if (true) putln(&pass); else putln(&fail); -puts("if (false): "); if (false) putln(&fail); else putln(&pass); + +puts( "if (#TRUE): "); if (#TRUE) passed(); else failed(); +puts("; if (#FALSE):"); if (#FALSE) failln(); else passln(); +newlin(); + +puts( "if (#TRUE and #TRUE ):"); if (#TRUE and #TRUE) passed(); else failed(); +puts("; if (#TRUE and #FALSE):"); if (#TRUE and #FALSE) failln(); else passln(); +puts( "if (#FALSE and #TRUE ):"); if (#FALSE and #TRUE) failed(); else passed(); +puts("; if (#FALSE and #FALSE):"); if (#FALSE and #FALSE) failln(); else passln(); +newlin(); + +puts( "if (#TRUE or #TRUE) :"); if (#TRUE or #TRUE) passed(); else failed(); +puts("; if (#TRUE or #FALSE):"); if (#TRUE or #FALSE) passln(); else failln(); +puts( "if (#FALSE or #TRUE) :"); if (#FALSE or #TRUE) passed(); else failed(); +puts("; if (#FALSE or #FALSE):"); if (#FALSE or #FALSE) failln(); else passln(); +newlin(); + +puts( "if (#TRUE and #TRUE and #TRUE) :"); if (#TRUE and #TRUE and #TRUE) passed(); else failed(); +puts("; if (#TRUE and #TRUE and #FALSE):"); if (#TRUE and #TRUE and #FALSE) failln(); else passln(); +puts( "if (#TRUE and #FALSE and #TRUE) :"); if (#TRUE and #FALSE and #TRUE) failed(); else passed(); +puts("; if (#TRUE and #FALSE and #FALSE):"); if (#TRUE and #FALSE and #FALSE) failln(); else passln(); +puts( "if (#FALSE and #TRUE and #TRUE) :"); if (#FALSE and #TRUE and #TRUE) failed(); else passed(); +puts("; if (#FALSE and #TRUE and #FALSE):"); if (#FALSE and #TRUE and #FALSE) failln(); else passln(); +puts( "if (#FALSE and #FALSE and #TRUE) :"); if (#FALSE and #FALSE and #TRUE) failed(); else passed(); +puts("; if (#FALSE and #FALSE and #FALSE):"); if (#FALSE and #FALSE and #FALSE) failln(); else passln(); +newlin(); + +puts( "if (#TRUE or #TRUE or #TRUE) :"); if (#TRUE or #TRUE or #TRUE) passed(); else failed(); +puts("; if (#TRUE or #TRUE or #FALSE):"); if (#TRUE or #TRUE or #FALSE) passln(); else failln(); +puts( "if (#TRUE or #FALSE or #TRUE) :"); if (#TRUE or #FALSE or #TRUE) passed(); else failed(); +puts("; if (#TRUE or #FALSE or #FALSE):"); if (#TRUE or #FALSE or #FALSE) passln(); else failln(); +puts( "if (#FALSE or #TRUE or #TRUE) :"); if (#FALSE or #TRUE or #TRUE) passed(); else failed(); +puts("; if (#FALSE or #TRUE or #FALSE):"); if (#FALSE or #TRUE or #FALSE) passln(); else failln(); +puts( "if (#FALSE or #FALSE or #TRUE) :"); if (#FALSE or #FALSE or #TRUE) passed(); else failed(); +puts("; if (#FALSE or #FALSE or #FALSE):"); if (#FALSE or #FALSE or #FALSE) failln(); else passln(); +anykey(); + +puts( "if (n0n1): "); if (n2>n1) passln(); else failln(); +puts( "if (n1==n1):"); if (n1==n1) passed(); else failed(); +puts("; if (n0<>n2):"); if (n0<>n2) passln(); else failln(); +newlin(); + +puts( "if (n0=n1):"); if (n2>=n1) passed(); else failed(); +puts("; if (n2>=n1):"); if (n2>=n1) passed(); else failed(); +puts("; if (n2>n1): "); if (n2>n1) passln(); else failln(); +newlin(); + +puts( "if (n0): "); if (n0) failed(); else passed(); +puts("; if (n1): "); if (n1) passed(); else failed(); +puts("; if (n2): "); if (n1) passed(); else failed(); +puts("; if (nff): "); if (nff) passln(); else failln(); + +puts( "if (n0:+): "); if (n0:+) passed(); else failed(); +puts("; if (n1:+): "); if (n1:+) passed(); else failed(); +puts("; if (n2:+): "); if (n1:+) passed(); else failed(); +puts("; if (nff:+):"); if (nff:+) failln(); else passln(); + +puts( "if (n0:-): "); if (n0:-) failed(); else passed(); +puts("; if (n1:-): "); if (n1:-) failed(); else passed(); +puts("; if (n2:-): "); if (n1:-) failed(); else passed(); +puts("; if (nff:-):"); if (nff:-) passln(); else failln(); +newlin(); goto exit; - diff --git a/src/c02.c b/src/c02.c index 86c33bc..c91b46f 100644 --- a/src/c02.c +++ b/src/c02.c @@ -27,6 +27,7 @@ /* Initilize Compiler Variables */ void init(void) { + initim(); //Initialize Elapsed Time DEBUG("Initializing Compiler Variables\n",0) concnt = 0; //Number of Constants Defined varcnt = 0; //Number of Variables in Table diff --git a/src/common.c b/src/common.c index ebb52aa..1d48f6c 100644 --- a/src/common.c +++ b/src/common.c @@ -7,9 +7,11 @@ #include #include #include - +#include #include "common.h" +struct timespec curtim; //Current Time + /* Error - Print Input File name & position and exit */ void exterr(int errnum) { fprintf(stderr, "Line %d Column %d of File %s\n", curlin, curcol, inpnam); @@ -27,6 +29,19 @@ void expctd(char *expstr) { /* Print current position in file */ void prtpos(void) { printf("(%s: %d,%d) ", inpnam, curlin, curcol); } +/* Initialize elapsed time counter */ +void initim(void) { + timespec_get (&curtim, TIME_UTC); + bgntim = curtim.tv_sec; +} + +/* Print elapsed time */ +void prttim(void) { + timespec_get (&curtim, TIME_UTC); + printf("[%d", curtim.tv_sec - bgntim); + printf(".%06d]",curtim.tv_nsec/1000); +} + /* Set comment to string */ void setcmt(char *s) { strcpy(cmtasm, s); } diff --git a/src/common.h b/src/common.h index 53c743d..eeb2a9f 100644 --- a/src/common.h +++ b/src/common.h @@ -36,8 +36,10 @@ #define TRUE -1 #define FALSE 0 -void prtpos(); //Print current file name and position -#define DEBUG(fmt, val) {if (debug) {prtpos(); printf(fmt, val);}} +void initim(); //Initialize elapsed time counter +void prtpos(); //Print current file name and position +void prttim(); //Print elapsed time +#define DEBUG(fmt, val) {if (debug) {prtpos(); prttim(); printf(fmt, val);}} #define DETAIL(fmt, val) {if (debug) printf(fmt, val);} #define ERROR(fmt, val, err) {fprintf(stderr, fmt, val);exterr(err);} @@ -49,6 +51,8 @@ char asmcmt[LINELEN]; //Processed Assembly Language Comment int curcol, curlin; //Position in Source Code int savcol, savlin; //Save Position in Source Code +int bgntim; //Starting Time + int nxtchr; //Next Character of Source File to Process int nxtupc; //Next Character Converted to Uppercase int savchr; //Holds nxtchr when switching input files diff --git a/src/cond.c b/src/cond.c index aaf526f..ce46885 100644 --- a/src/cond.c +++ b/src/cond.c @@ -39,24 +39,31 @@ int enccmp(char c) { * Uses: term - Term Being Compared Against * * label - Branch Target if Comparison is FALSE */ void prccmp(void) { - DEBUG("Processing comparator %d\n", cmprtr) + DEBUG("Processing comparator %d", cmprtr) DETAIL(" with REVCMP=%d\n", revcmp) + if (cmprtr > 7) { //Process Flag + cmprtr = (cmprtr ^ revcmp) & 1; //Apply Reversal + if (cmprtr) asmlin("BPL", cmplbl); + else asmlin("BMI", cmplbl); + return; + } + cmprtr = (cmprtr ^ revcmp) & 7; //Apply reversal switch(cmprtr) { case 0: // Raw Expression (Skip) - asmlin("BEQ", cndlbl); break; + asmlin("BEQ", cmplbl); break; case 1: // = or == - asmlin("CMP", term); asmlin("BNE", cndlbl); break; + asmlin("CMP", term); asmlin("BNE", cmplbl); break; case 2: // < - asmlin("CMP", term); asmlin("BCS", cndlbl); break; + asmlin("CMP", term); asmlin("BCS", cmplbl); break; case 3: // <= or =< - asmlin("CLC", ""); asmlin("SBC", term); asmlin("BCS", cndlbl); break; + asmlin("CLC", ""); asmlin("SBC", term); asmlin("BCS", cmplbl); break; case 4: // > - asmlin("CLC", ""); asmlin("SBC", term); asmlin("BCC", cndlbl); break; + asmlin("CLC", ""); asmlin("SBC", term); asmlin("BCC", cmplbl); break; case 5: // >= or => - asmlin("CMP", term); asmlin("BCC", cndlbl); break; + asmlin("CMP", term); asmlin("BCC", cmplbl); break; case 6: // <> or >< - asmlin("CMP", term); asmlin("BEQ", cndlbl); break; + asmlin("CMP", term); asmlin("BEQ", cmplbl); break; case 7: // Raw Expression (Normal) - asmlin("BNE", cndlbl); break; + asmlin("BNE", cmplbl); break; default: ERROR("Unsupported comparison operator index %d\n", cmprtr, EXIT_FAILURE) } @@ -73,33 +80,59 @@ void prscmp(int revrse) { } skpspc(); if (cmprtr) prstrm(); - cmprtr = (cmprtr ^ revrse) & 7; //Apply reversal - prccmp(); + //prccmp(); - Do after check for logical operator DEBUG("Parsed comparator %d\n", cmprtr) } /* Parse Flag Operator */ void prsflg(int revrse) { DEBUG("Parsing Flag Operator '%c'\n", nxtchr) - if (match('+')) cmprtr = 0; - else if (match('-')) cmprtr = 1; + if (match('+')) cmprtr = 8; //Bit 0 = 0 + else if (match('-')) cmprtr = 9; //Bit 1 = 1 else expctd("Flag operator"); skpchr(); - cmprtr = (cmprtr ^ revrse) & 1; //Apply Reversal - if (cmprtr) asmlin("BPL", cndlbl); - else asmlin("BMI", cndlbl); +} + +/* Parse Logical Operator * + * Sets: logops */ +void prslop(void) { + DEBUG("Checking for Logical Operator\n", 0) + logopr = LOPNONE; + skpspc(); + if (isalph()) { + getwrd(); //Get Logical Operator + DEBUG("Parsing Logical Operator %s\n", word) + if (wordis("AND")) logopr = LOPAND; + else if (wordis("OR")) logopr = LOPOR; + else ERROR("Encountered invalid token \"%s\"\n", word, EXIT_FAILURE) + } + DEBUG("Set LOGOPR to %d\n", logopr) } /* Parse and Compile Conditional Expression * * Condition = */ void prscnd(char trmntr, int revrse) { DEBUG("Parsing condition with REVRSE=%d\n", revrse) - if (look('!')) { - revrse = (revrse) ? FALSE: TRUE; - DEBUG("Set REVRSE to %d\n", revrse) - } - if (!look('*')) prsxpr(0); - if (look(':')) prsflg(revrse); //Parse Flag Operator - else prscmp(revrse); //Parse Comparison Operator + tmplbl[0] = 0; + do { + strcpy(cmplbl, cndlbl); DEBUG("Set CMPLBL to \"%s\"\n", cmplbl); + revcmp = revrse; + if (look('!')) revcmp = (revcmp) ? FALSE: TRUE; + DEBUG("Set REVCMP to %d\n", revcmp) + if (!look('*')) prsxpr(0); + if (look(':')) prsflg(revcmp); //Parse Flag Operator + else prscmp(revcmp); //Parse Comparison Operator + prslop(); //Parse Logical Operator + if (logopr == LOPOR) { + revcmp = (revcmp) ? FALSE: TRUE; + DEBUG("Set REVCMP to %d\n", revcmp) + } + if (logopr && revcmp) { + if (!tmplbl[0]) newlbl(tmplbl); + strcpy(cmplbl, tmplbl); DEBUG("Set CMPLBL to \"%s\"\n", cmplbl); + } + prccmp(); //Process Comparison/Flag Operator + } while (logopr); + if (tmplbl[0]) setlbl(tmplbl); expect(trmntr); } diff --git a/src/cond.h b/src/cond.h index 424fb31..d57196a 100644 --- a/src/cond.h +++ b/src/cond.h @@ -2,4 +2,9 @@ * C02 Conditional Parsing Routines * ************************************/ +enum LOGOPS {LOPNONE, LOPAND, LOPOR}; + +int revcmp; //Reverse Comparison +int logopr; //Logical Operator (set to LOGOPS) + void prscnd(char trmntr, int revrse); //Parse Conditional Expression diff --git a/src/label.h b/src/label.h index f2392e7..93fa270 100644 --- a/src/label.h +++ b/src/label.h @@ -3,6 +3,7 @@ ******************************************************/ char curlbl[LABLEN+1]; //Most recently generated label +char cmplbl[LABLEN+1]; //Label for Comparison char cndlbl[LABLEN+1]; //Label for Conditional Code char endlbl[LABLEN+1]; //End Label char forlbl[LABLEN+1]; //For Loop Label diff --git a/src/parse.h b/src/parse.h index 23cd6c5..5a73270 100644 --- a/src/parse.h +++ b/src/parse.h @@ -28,10 +28,10 @@ int isanum(); //Is Next Character AlphaNumeric int isapos(); //Is Next Character an Apostrophe int isbin(); //Is Next Character a Binary Digit int isbpre(); //Is Next Character a Binary Prefix -int islpre(); //Is Next Character a Literal Prefix int isdec(); //Is Next Character a Decimal Digit int iscpre(); //Is Next Character a Constant Prefix int ishexd(); //Is Next Character a Hexadecimal Digit +int islpre(); //Is Next Character a Literal Prefix int isnl(); //Is Next Character a NewLine int isnpre(); //Is Next Character a Numeric Prfix int isoper(); //Is Next Character an Operator diff --git a/src/stmnt.c b/src/stmnt.c index 1a3d418..9a0a370 100644 --- a/src/stmnt.c +++ b/src/stmnt.c @@ -48,11 +48,11 @@ void prssif(char trmntr) { prscnd(')', FALSE); //Parse Condition expect('?'); prsxpr(':'); //Parse "if TRUE" expression - newlbl(tmplbl); //Create End of Expression Label - asmlin("JMP", tmplbl); //Jump over "if FALSE" expression + newlbl(skplbl); //Create End of Expression Label + asmlin("JMP", skplbl); //Jump over "if FALSE" expression setlbl(cndlbl); //Emit "if FALSE" label prsxpr(trmntr); //Parse "if FALSE" expression - setlbl(tmplbl); //Emit End of Expression Label + setlbl(skplbl); //Emit End of Expression Label } /* Process Array Index */