Added logical operators 'and' and 'or' to conditionals

This commit is contained in:
Curtis F Kaylor 2018-08-02 00:15:32 -04:00
parent f35762abda
commit fb9f396240
11 changed files with 226 additions and 57 deletions

View File

@ -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:

View File

@ -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<x is equivalent to the C code !(b<x).
C02 uses the logical operators "and" and "or" instead of "&&" and "||",
and performs short-circuit evaluation of logical operators, making them
effectively right associative.
FUNCTIONS
Parameter passing uses the 6502's A, Y, and X registers. This limits

View File

@ -3,16 +3,86 @@
****************************************/
#include <py65.h02>
#include <stddef.h02>
#include <stdlib.h02>
#include <stdio.h02>
#include <stdiox.h02>
#include <test.h02>
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 (n0<n1): "); if (n0<n1) passed(); else failed();
puts("; if (n2>n1): "); 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 (n0<n1) passed(); else failed();
puts("; if (n1<n2): "); if (n1<n2) passed(); else failed();
puts("; if (n0<n2): "); if (n1<n2) passln(); else failln();
puts( "if (n1<=n1):"); if (n1<=n1) passed(); else failed();
puts("; if (n1<=n2):"); if (n1<=n2) passed(); else failed();
puts("; if (n1<n2): "); if (n1<=n2) passln(); else failln();
puts( "if (n1>=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;

View File

@ -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

View File

@ -7,9 +7,11 @@
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#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); }

View File

@ -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

View File

@ -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 = <expression> <comparator> <term> */
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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */