Data update

This commit is contained in:
Ingy döt Net 2026-02-01 16:33:20 -08:00
parent 5150844a7d
commit 4bb20c9b71
7735 changed files with 38060 additions and 199180 deletions

View file

@ -3,35 +3,35 @@
#include <stdio.h>
typedef struct {
const char *s;
int len, prec, assoc;
const char *s;
int len, prec, assoc;
} str_tok_t;
typedef struct {
const char * str;
int assoc, prec;
regex_t re;
const char * str;
int assoc, prec;
regex_t re;
} pat_t;
enum assoc { A_NONE, A_L, A_R };
pat_t pat_eos = {"", A_NONE, 0};
pat_t pat_ops[] = {
{"^\\)", A_NONE, -1},
{"^\\*\\*", A_R, 3},
{"^\\^", A_R, 3},
{"^\\*", A_L, 2},
{"^/", A_L, 2},
{"^\\+", A_L, 1},
{"^-", A_L, 1},
{0}
{"^\\)", A_NONE, -1},
{"^\\*\\*", A_R, 3},
{"^\\^", A_R, 3},
{"^\\*", A_L, 2},
{"^/", A_L, 2},
{"^\\+", A_L, 1},
{"^-", A_L, 1},
{0}
};
pat_t pat_arg[] = {
{"^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"},
{"^[a-zA-Z_][a-zA-Z_0-9]*"},
{"^\\(", A_L, -1},
{0}
{"^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"},
{"^[a-zA-Z_][a-zA-Z_0-9]*"},
{"^\\(", A_L, -1},
{0}
};
str_tok_t stack[256]; /* assume these are big enough */
@ -43,16 +43,16 @@ int l_queue, l_stack;
void display(const char *s)
{
int i;
printf("\033[1;1H\033[JText | %s", s);
printf("\nStack| ");
for (i = 0; i < l_stack; i++)
printf("%.*s ", stack[i].len, stack[i].s); // uses C99 format strings
printf("\nQueue| ");
for (i = 0; i < l_queue; i++)
printf("%.*s ", queue[i].len, queue[i].s);
puts("\n\n<press enter>");
getchar();
int i;
printf("\033[1;1H\033[JText | %s", s);
printf("\nStack| ");
for (i = 0; i < l_stack; i++)
printf("%.*s ", stack[i].len, stack[i].s); // uses C99 format strings
printf("\nQueue| ");
for (i = 0; i < l_queue; i++)
printf("%.*s ", queue[i].len, queue[i].s);
puts("\n\n<press enter>");
getchar();
}
int prec_booster;
@ -61,125 +61,125 @@ int prec_booster;
int init(void)
{
int i;
pat_t *p;
int i;
pat_t *p;
for (i = 0, p = pat_ops; p[i].str; i++)
if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED))
fail("comp", p[i].str);
for (i = 0, p = pat_ops; p[i].str; i++)
if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED))
fail("comp", p[i].str);
for (i = 0, p = pat_arg; p[i].str; i++)
if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED))
fail("comp", p[i].str);
for (i = 0, p = pat_arg; p[i].str; i++)
if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED))
fail("comp", p[i].str);
return 1;
return 1;
}
pat_t* match(const char *s, pat_t *p, str_tok_t * t, const char **e)
{
int i;
regmatch_t m;
int i;
regmatch_t m;
while (*s == ' ') s++;
*e = s;
while (*s == ' ') s++;
*e = s;
if (!*s) return &pat_eos;
if (!*s) return &pat_eos;
for (i = 0; p[i].str; i++) {
if (regexec(&(p[i].re), s, 1, &m, REG_NOTEOL))
continue;
t->s = s;
*e = s + (t->len = m.rm_eo - m.rm_so);
return p + i;
}
return 0;
for (i = 0; p[i].str; i++) {
if (regexec(&(p[i].re), s, 1, &m, REG_NOTEOL))
continue;
t->s = s;
*e = s + (t->len = m.rm_eo - m.rm_so);
return p + i;
}
return 0;
}
int parse(const char *s) {
pat_t *p;
str_tok_t *t, tok;
pat_t *p;
str_tok_t *t, tok;
prec_booster = l_queue = l_stack = 0;
display(s);
while (*s) {
p = match(s, pat_arg, &tok, &s);
if (!p || p == &pat_eos) fail("parse arg", s);
prec_booster = l_queue = l_stack = 0;
display(s);
while (*s) {
p = match(s, pat_arg, &tok, &s);
if (!p || p == &pat_eos) fail("parse arg", s);
/* Odd logic here. Don't actually stack the parens: don't need to. */
if (p->prec == -1) {
prec_booster += 100;
continue;
}
qpush(tok);
display(s);
/* Odd logic here. Don't actually stack the parens: don't need to. */
if (p->prec == -1) {
prec_booster += 100;
continue;
}
qpush(tok);
display(s);
re_op: p = match(s, pat_ops, &tok, &s);
if (!p) fail("parse op", s);
re_op: p = match(s, pat_ops, &tok, &s);
if (!p) fail("parse op", s);
tok.assoc = p->assoc;
tok.prec = p->prec;
tok.assoc = p->assoc;
tok.prec = p->prec;
if (p->prec > 0)
tok.prec = p->prec + prec_booster;
else if (p->prec == -1) {
if (prec_booster < 100)
fail("unmatched )", s);
tok.prec = prec_booster;
}
if (p->prec > 0)
tok.prec = p->prec + prec_booster;
else if (p->prec == -1) {
if (prec_booster < 100)
fail("unmatched )", s);
tok.prec = prec_booster;
}
while (l_stack) {
t = stack + l_stack - 1;
if (!(t->prec == tok.prec && t->assoc == A_L)
&& t->prec <= tok.prec)
break;
qpush(spop());
display(s);
}
while (l_stack) {
t = stack + l_stack - 1;
if (!(t->prec == tok.prec && t->assoc == A_L)
&& t->prec <= tok.prec)
break;
qpush(spop());
display(s);
}
if (p->prec == -1) {
prec_booster -= 100;
goto re_op;
}
if (p->prec == -1) {
prec_booster -= 100;
goto re_op;
}
if (!p->prec) {
display(s);
if (prec_booster)
fail("unmatched (", s);
return 1;
}
if (!p->prec) {
display(s);
if (prec_booster)
fail("unmatched (", s);
return 1;
}
spush(tok);
display(s);
}
spush(tok);
display(s);
}
if (p->prec > 0)
fail("unexpected eol", s);
if (p->prec > 0)
fail("unexpected eol", s);
return 1;
return 1;
}
int main()
{
int i;
const char *tests[] = {
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3", /* RC mandated: OK */
"123", /* OK */
"3+4 * 2 / ( 1 - 5 ) ^ 2 ^ 3.14", /* OK */
"(((((((1+2+3**(4 + 5))))))", /* bad parens */
"a^(b + c/d * .1e5)!", /* unknown op */
"(1**2)**3", /* OK */
"2 + 2 *", /* unexpected eol */
0
};
int i;
const char *tests[] = {
"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3", /* RC mandated: OK */
"123", /* OK */
"3+4 * 2 / ( 1 - 5 ) ^ 2 ^ 3.14", /* OK */
"(((((((1+2+3**(4 + 5))))))", /* bad parens */
"a^(b + c/d * .1e5)!", /* unknown op */
"(1**2)**3", /* OK */
"2 + 2 *", /* unexpected eol */
0
};
if (!init()) return 1;
for (i = 0; tests[i]; i++) {
printf("Testing string `%s' <enter>\n", tests[i]);
getchar();
if (!init()) return 1;
for (i = 0; tests[i]; i++) {
printf("Testing string `%s' <enter>\n", tests[i]);
getchar();
printf("string `%s': %s\n\n", tests[i],
parse(tests[i]) ? "Ok" : "Error");
}
printf("string `%s': %s\n\n", tests[i],
parse(tests[i]) ? "Ok" : "Error");
}
return 0;
return 0;
}

View file

@ -1,106 +1,106 @@
import ceylon.collection {
ArrayList
ArrayList
}
abstract class Token(String|Integer data) of IntegerLiteral | Operator | leftParen | rightParen {
string => data.string;
string => data.string;
}
class IntegerLiteral(shared Integer integer) extends Token(integer) {}
class Operator extends Token {
shared Integer precedence;
shared Boolean rightAssoc;
shared new plus extends Token("+") {
precedence = 2;
rightAssoc = false;
}
shared new minus extends Token("-") {
precedence = 2;
rightAssoc = false;
}
shared new times extends Token("*") {
precedence = 3;
rightAssoc = false;
}
shared new divides extends Token("/") {
precedence = 3;
rightAssoc = false;
}
shared new power extends Token("^") {
precedence = 4;
rightAssoc = true;
}
shared Integer precedence;
shared Boolean rightAssoc;
shared Boolean below(Operator other) =>
!rightAssoc && precedence <= other.precedence || rightAssoc && precedence < other.precedence;
shared new plus extends Token("+") {
precedence = 2;
rightAssoc = false;
}
shared new minus extends Token("-") {
precedence = 2;
rightAssoc = false;
}
shared new times extends Token("*") {
precedence = 3;
rightAssoc = false;
}
shared new divides extends Token("/") {
precedence = 3;
rightAssoc = false;
}
shared new power extends Token("^") {
precedence = 4;
rightAssoc = true;
}
shared Boolean below(Operator other) =>
!rightAssoc && precedence <= other.precedence || rightAssoc && precedence < other.precedence;
}
object leftParen extends Token("(") {}
object rightParen extends Token(")") {}
shared void run() {
function shunt(String input) {
function tokenize(String input) =>
input.split().map((String element) =>
switch(element.trimmed)
case("(") leftParen
case(")") rightParen
case("+") Operator.plus
case("-") Operator.minus
case("*") Operator.times
case("/") Operator.divides
case("^") Operator.power
else IntegerLiteral(parseInteger(element) else 0)); // no error handling
value outputQueue = ArrayList<Token>();
value operatorStack = ArrayList<Token>();
void report(String action) {
print("``action.padTrailing(22)`` | ``" ".join(outputQueue).padTrailing(25)`` | ``" ".join(operatorStack).padTrailing(10)``");
}
print("input is ``input``\n");
print("Action | Output Queue | Operators' Stack
-----------------------|---------------------------|-----------------");
for(token in tokenize(input)) {
switch(token)
case(is IntegerLiteral) {
outputQueue.offer(token);
report("``token`` from input to queue");
}
case(leftParen) {
operatorStack.push(token);
report("``token`` from input to stack");
}
case(rightParen){
while(exists top = operatorStack.pop(), top != leftParen) {
outputQueue.offer(top);
report("``top`` from stack to queue");
}
}
case(is Operator) {
while(exists top = operatorStack.top, is Operator top, token.below(top)) {
operatorStack.pop();
outputQueue.offer(top);
report("``top`` from stack to queue");
}
operatorStack.push(token);
report("``token`` from input to stack");
}
}
while(exists top = operatorStack.pop()) {
outputQueue.offer(top);
report("``top`` from stack to queue");
}
return " ".join(outputQueue);
}
value rpn = shunt("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3");
assert(rpn == "3 4 2 * 1 5 - 2 3 ^ ^ / +");
print("\nthe result is ``rpn``");
function shunt(String input) {
function tokenize(String input) =>
input.split().map((String element) =>
switch(element.trimmed)
case("(") leftParen
case(")") rightParen
case("+") Operator.plus
case("-") Operator.minus
case("*") Operator.times
case("/") Operator.divides
case("^") Operator.power
else IntegerLiteral(parseInteger(element) else 0)); // no error handling
value outputQueue = ArrayList<Token>();
value operatorStack = ArrayList<Token>();
void report(String action) {
print("``action.padTrailing(22)`` | ``" ".join(outputQueue).padTrailing(25)`` | ``" ".join(operatorStack).padTrailing(10)``");
}
print("input is ``input``\n");
print("Action | Output Queue | Operators' Stack
-----------------------|---------------------------|-----------------");
for(token in tokenize(input)) {
switch(token)
case(is IntegerLiteral) {
outputQueue.offer(token);
report("``token`` from input to queue");
}
case(leftParen) {
operatorStack.push(token);
report("``token`` from input to stack");
}
case(rightParen){
while(exists top = operatorStack.pop(), top != leftParen) {
outputQueue.offer(top);
report("``top`` from stack to queue");
}
}
case(is Operator) {
while(exists top = operatorStack.top, is Operator top, token.below(top)) {
operatorStack.pop();
outputQueue.offer(top);
report("``top`` from stack to queue");
}
operatorStack.push(token);
report("``token`` from input to stack");
}
}
while(exists top = operatorStack.pop()) {
outputQueue.offer(top);
report("``top`` from stack to queue");
}
return " ".join(outputQueue);
}
value rpn = shunt("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3");
assert(rpn == "3 4 2 * 1 5 - 2 3 ^ ^ / +");
print("\nthe result is ``rpn``");
}

View file

@ -1,20 +1,20 @@
MODULE COMPILER !At least of arithmetic expressions.
INTEGER KBD,MSG !I/O units.
MODULE COMPILER !At least of arithmetic expressions.
INTEGER KBD,MSG !I/O units.
INTEGER ENUFF !How long s a piece of string?
PARAMETER (ENUFF = 66) !This long.
CHARACTER*(ENUFF) RP !Holds the Reverse Polish Notation.
INTEGER LR !And this is its length.
INTEGER ENUFF !How long s a piece of string?
PARAMETER (ENUFF = 66) !This long.
CHARACTER*(ENUFF) RP !Holds the Reverse Polish Notation.
INTEGER LR !And this is its length.
INTEGER OPSYMBOLS !Recognised operator symbols.
PARAMETER (OPSYMBOLS = 11) !There are also some associates.
TYPE SYMB !To recognise symbols and carry associated information.
CHARACTER*1 IS !Its text. Careful with the trailing space and comparisons.
INTEGER*1 PRECEDENCE !Controls the order of evaluation.
CHARACTER*48 USAGE !Description.
END TYPE SYMB !The cross-linkage of precedences is tricky.
TYPE(SYMB) SYMBOL(0:OPSYMBOLS) !Righto, I'll have some.
PARAMETER (SYMBOL =(/ !Note that "*" is not to be seen as a match to "**".
INTEGER OPSYMBOLS !Recognised operator symbols.
PARAMETER (OPSYMBOLS = 11) !There are also some associates.
TYPE SYMB !To recognise symbols and carry associated information.
CHARACTER*1 IS !Its text. Careful with the trailing space and comparisons.
INTEGER*1 PRECEDENCE !Controls the order of evaluation.
CHARACTER*48 USAGE !Description.
END TYPE SYMB !The cross-linkage of precedences is tricky.
TYPE(SYMB) SYMBOL(0:OPSYMBOLS) !Righto, I'll have some.
PARAMETER (SYMBOL =(/ !Note that "*" is not to be seen as a match to "**".
o SYMB(" ", 0,"Not recognised as an operator's symbol."),
1 SYMB(" ", 1,"separates symbols and aids legibility."),
2 SYMB(")", 4,"opened with ( to bracket a sub-expression."),
@ -28,190 +28,190 @@
o SYMB("÷",12,"division for those with a fancy keyboard."),
C 13 is used so that stacked ^ will have lower priority than incoming ^, thus delivering right-to-left evaluation.
1 SYMB("^",14,"raise to power. Not recognised is **.")/))
CHARACTER*3 BRAOPEN,BRACLOSE !Three types are allowed.
PARAMETER (BRAOPEN = "([{", BRACLOSE = ")]}") !These.
INTEGER BRALEVEL !In and out, in and out. That's the game.
INTEGER PRBRA,PRPOW !Special double values.
PARAMETER (PRBRA = SYMBOL( 3).PRECEDENCE) !Bracketing
PARAMETER (PRPOW = SYMBOL(11).PRECEDENCE) !And powers refer leftwards.
CHARACTER*3 BRAOPEN,BRACLOSE !Three types are allowed.
PARAMETER (BRAOPEN = "([{", BRACLOSE = ")]}") !These.
INTEGER BRALEVEL !In and out, in and out. That's the game.
INTEGER PRBRA,PRPOW !Special double values.
PARAMETER (PRBRA = SYMBOL( 3).PRECEDENCE) !Bracketing
PARAMETER (PRPOW = SYMBOL(11).PRECEDENCE) !And powers refer leftwards.
CHARACTER*10 DIGIT !Numberish is a bit more complex.
PARAMETER (DIGIT = "0123456789") !But this will do for integers.
CHARACTER*10 DIGIT !Numberish is a bit more complex.
PARAMETER (DIGIT = "0123456789") !But this will do for integers.
INTEGER STACKLIMIT !How high is a stack?
PARAMETER (STACKLIMIT = 66) !This should suffice.
TYPE DEFERRED !I need a siding for lower-precedence operations.
CHARACTER*1 OPC !The operation code.
INTEGER*1 PRECEDENCE !Its precedence in the siding may differ.
END TYPE DEFERRED !Anyway, that's enough.
TYPE(DEFERRED) OPSTACK(0:STACKLIMIT) !One siding, please.
INTEGER OSP !The operation stack pointer.
INTEGER STACKLIMIT !How high is a stack?
PARAMETER (STACKLIMIT = 66) !This should suffice.
TYPE DEFERRED !I need a siding for lower-precedence operations.
CHARACTER*1 OPC !The operation code.
INTEGER*1 PRECEDENCE !Its precedence in the siding may differ.
END TYPE DEFERRED !Anyway, that's enough.
TYPE(DEFERRED) OPSTACK(0:STACKLIMIT) !One siding, please.
INTEGER OSP !The operation stack pointer.
INTEGER INCOMING,TOKENTYPE,NOTHING,ANUMBER,OPENBRA,HUH !Some mnemonics.
PARAMETER (NOTHING = 0, ANUMBER = -1, OPENBRA = -2, HUH = -3) !The ordering is not arbitrary.
CONTAINS !Now to mess about.
SUBROUTINE EMIT(STUFF) !The objective is to produce some RPN text.
CHARACTER*(*) STUFF !The term of the moment.
INTEGER L !A length.
WRITE (MSG,1) STUFF !Announce.
1 FORMAT ("Emit ",A) !Whatever it is.
IF (STUFF.EQ."") RETURN !Ha ha.
L = LEN(STUFF) !So, how much is there to append?
IF (LR + L.GE.ENUFF) STOP "Too much RPN for RP!" !Perhaps too much.
IF (LR.GT.0) THEN !Is there existing stuff?
LR = LR + 1 !Yes. Advance one,
RP(LR:LR) = " " !And place a space.
END IF !So much for separators.
RP(LR + 1:LR + L) = STUFF !Place the stuff.
LR = LR + L !Count it in.
END SUBROUTINE EMIT !Simple enough, if a bit finicky.
INTEGER INCOMING,TOKENTYPE,NOTHING,ANUMBER,OPENBRA,HUH !Some mnemonics.
PARAMETER (NOTHING = 0, ANUMBER = -1, OPENBRA = -2, HUH = -3) !The ordering is not arbitrary.
CONTAINS !Now to mess about.
SUBROUTINE EMIT(STUFF) !The objective is to produce some RPN text.
CHARACTER*(*) STUFF !The term of the moment.
INTEGER L !A length.
WRITE (MSG,1) STUFF !Announce.
1 FORMAT ("Emit ",A) !Whatever it is.
IF (STUFF.EQ."") RETURN !Ha ha.
L = LEN(STUFF) !So, how much is there to append?
IF (LR + L.GE.ENUFF) STOP "Too much RPN for RP!" !Perhaps too much.
IF (LR.GT.0) THEN !Is there existing stuff?
LR = LR + 1 !Yes. Advance one,
RP(LR:LR) = " " !And place a space.
END IF !So much for separators.
RP(LR + 1:LR + L) = STUFF !Place the stuff.
LR = LR + L !Count it in.
END SUBROUTINE EMIT !Simple enough, if a bit finicky.
SUBROUTINE STACKOP(C,P) !Push an item into the siding.
CHARACTER*1 C !The operation code.
INTEGER P !Its precedence.
OSP = OSP + 1 !Stacking up...
IF (OSP.GT.STACKLIMIT) STOP "OSP overtopped!" !Perhaps not.
OPSTACK(OSP).OPC = C !Righto,
OPSTACK(OSP).PRECEDENCE = P !The deed is simple.
WRITE (MSG,1) C,OPSTACK(1:OSP) !Announce.
SUBROUTINE STACKOP(C,P) !Push an item into the siding.
CHARACTER*1 C !The operation code.
INTEGER P !Its precedence.
OSP = OSP + 1 !Stacking up...
IF (OSP.GT.STACKLIMIT) STOP "OSP overtopped!" !Perhaps not.
OPSTACK(OSP).OPC = C !Righto,
OPSTACK(OSP).PRECEDENCE = P !The deed is simple.
WRITE (MSG,1) C,OPSTACK(1:OSP) !Announce.
1 FORMAT ("Stack ",A1,9X,",OpStk=",33(A1,I2:","))
END SUBROUTINE STACKOP !So this is more for mnemonic ease.
END SUBROUTINE STACKOP !So this is more for mnemonic ease.
LOGICAL FUNCTION COMPILE(TEXT) !A compiler confronts a compiler!
CHARACTER*(*) TEXT !To be inspected.
INTEGER L1,L2 !Fingers for the scan.
CHARACTER*1 C !Character of the moment.
INTEGER HAPPY !Ah, shades of mood.
LR = 0 !No output yet.
OSP = 0 !Nothing stacked.
OPSTACK(0).OPC = "" !Prepare a bouncer.
OPSTACK(0).PRECEDENCE = 0 !So that loops won't go past.
BRALEVEL = 0 !None seen.
HAPPY = +1 !Nor any problems.
L2 = 1 !Syncopation: one past the end of the previous token.
LOGICAL FUNCTION COMPILE(TEXT) !A compiler confronts a compiler!
CHARACTER*(*) TEXT !To be inspected.
INTEGER L1,L2 !Fingers for the scan.
CHARACTER*1 C !Character of the moment.
INTEGER HAPPY !Ah, shades of mood.
LR = 0 !No output yet.
OSP = 0 !Nothing stacked.
OPSTACK(0).OPC = "" !Prepare a bouncer.
OPSTACK(0).PRECEDENCE = 0 !So that loops won't go past.
BRALEVEL = 0 !None seen.
HAPPY = +1 !Nor any problems.
L2 = 1 !Syncopation: one past the end of the previous token.
Chew into an operand, possibly obstructed by an open bracket.
100 CALL FORASIGN !Find something to inspect.
IF (TOKENTYPE.EQ.NOTHING) THEN !Run off the end?
IF (OSP.GT.0) CALL GRUMP("Another operand or one of " !E.g. "1 +".
1 //BRAOPEN//" is expected.") !Give a hint, because stacked stuff awaits.
ELSE IF (TOKENTYPE.EQ.ANUMBER) THEN !If a number,
CALL EMIT(TEXT(L1:L2 - 1)) !Roll all its digits.
ELSE IF (TOKENTYPE.EQ.OPENBRA) THEN !Starting a sub-expression?
CALL STACKOP(C,PRBRA - 1) !Thus ( has less precedence than ).
GO TO 100 !And I still want an operand.
C ELSE IF (TOKENTYPE.EQ.ANAME) THEN !Name of something?
C CALL EMIT(TEXT(L1:L2 - 1)) !Roll it.
ELSE !No further options.
CALL GRUMP("Huh? Unexpected "//C) !Probably something like "1 + +"
END IF !Righto, finished with operands.
100 CALL FORASIGN !Find something to inspect.
IF (TOKENTYPE.EQ.NOTHING) THEN !Run off the end?
IF (OSP.GT.0) CALL GRUMP("Another operand or one of " !E.g. "1 +".
1 //BRAOPEN//" is expected.") !Give a hint, because stacked stuff awaits.
ELSE IF (TOKENTYPE.EQ.ANUMBER) THEN !If a number,
CALL EMIT(TEXT(L1:L2 - 1)) !Roll all its digits.
ELSE IF (TOKENTYPE.EQ.OPENBRA) THEN !Starting a sub-expression?
CALL STACKOP(C,PRBRA - 1) !Thus ( has less precedence than ).
GO TO 100 !And I still want an operand.
C ELSE IF (TOKENTYPE.EQ.ANAME) THEN !Name of something?
C CALL EMIT(TEXT(L1:L2 - 1)) !Roll it.
ELSE !No further options.
CALL GRUMP("Huh? Unexpected "//C) !Probably something like "1 + +"
END IF !Righto, finished with operands.
Chase after an operator, possibly interrupted by a close bracket,.
200 CALL FORASIGN !Find something to inspect.
IF (TOKENTYPE.LT.0) THEN !But, have I an operand-like token instead?
CALL GRUMP("Operator expected, not "//C) !It seems so.
ELSE !Normally, an operator is to hand. Possibly a NOTHING, though.
WRITE (MSG,201) C,INCOMING,OPSTACK(1:OSP) !Document it.
201 FORMAT ("Oprn=>",A1,"< Prec=",I2, !Try to align with other output.
1 ",OpStk=",33(A1,I2:",")) !So as not to clutter the display.
DO WHILE(OPSTACK(OSP).PRECEDENCE .GE. INCOMING) !Shunt higher-precedence stuff out.
IF (OPSTACK(OSP).PRECEDENCE .EQ. PRBRA - 1) !Only opening brackets have this precedence.
1 CALL GRUMP("Unbalanced "//OPSTACK(OSP).OPC) !And they vanish only when meeting their closing bracket.
CALL EMIT(OPSTACK(OSP).OPC) !Otherwise we have an operator.
OSP = OSP - 1 !It has gone forth.
END DO !On to the next.
IF (TOKENTYPE.GT.NOTHING) THEN !Now, only lower-precedence items are still in the stack.
IF (INCOMING.EQ.PRBRA) THEN !And this is a special arrival.
CALL BALANCEBRA(C) !It should match an earlier entry.
BRALEVEL = BRALEVEL - 1 !Count it out.
GO TO 200 !And I still haven't got an operator.
ELSE !All others are normal operators.
IF (C.EQ."^") INCOMING = PRPOW - 1 !Special trick to cause leftwards association of x^2^3.
CALL STACKOP(C,INCOMING) !Shunt aside, to await the next arrival.
END IF !So much for that operator.
END IF !Providing it was not just an end-of-input flusher.
END IF !And not a misplaced operand.
200 CALL FORASIGN !Find something to inspect.
IF (TOKENTYPE.LT.0) THEN !But, have I an operand-like token instead?
CALL GRUMP("Operator expected, not "//C) !It seems so.
ELSE !Normally, an operator is to hand. Possibly a NOTHING, though.
WRITE (MSG,201) C,INCOMING,OPSTACK(1:OSP) !Document it.
201 FORMAT ("Oprn=>",A1,"< Prec=",I2, !Try to align with other output.
1 ",OpStk=",33(A1,I2:",")) !So as not to clutter the display.
DO WHILE(OPSTACK(OSP).PRECEDENCE .GE. INCOMING) !Shunt higher-precedence stuff out.
IF (OPSTACK(OSP).PRECEDENCE .EQ. PRBRA - 1) !Only opening brackets have this precedence.
1 CALL GRUMP("Unbalanced "//OPSTACK(OSP).OPC) !And they vanish only when meeting their closing bracket.
CALL EMIT(OPSTACK(OSP).OPC) !Otherwise we have an operator.
OSP = OSP - 1 !It has gone forth.
END DO !On to the next.
IF (TOKENTYPE.GT.NOTHING) THEN !Now, only lower-precedence items are still in the stack.
IF (INCOMING.EQ.PRBRA) THEN !And this is a special arrival.
CALL BALANCEBRA(C) !It should match an earlier entry.
BRALEVEL = BRALEVEL - 1 !Count it out.
GO TO 200 !And I still haven't got an operator.
ELSE !All others are normal operators.
IF (C.EQ."^") INCOMING = PRPOW - 1 !Special trick to cause leftwards association of x^2^3.
CALL STACKOP(C,INCOMING) !Shunt aside, to await the next arrival.
END IF !So much for that operator.
END IF !Providing it was not just an end-of-input flusher.
END IF !And not a misplaced operand.
Carry on?
IF (HAPPY .GT. 0) GO TO 100 !No problems, and not a nothing from the end of the text.
IF (HAPPY .GT. 0) GO TO 100 !No problems, and not a nothing from the end of the text.
Completed.
COMPILE = HAPPY.GE.0 !One hopes so.
CONTAINS !Now for some assistants.
SUBROUTINE GRUMP(GROWL) !There might be a problem.
CHARACTER*(*) GROWL !The fault.
WRITE (MSG,1) GROWL !Say it.
IF (L1.GT. 1) WRITE (MSG,1) "Tasty:",TEXT( 1:L1 - 1) !Now explain the context.
IF (L2.GT.L1) WRITE (MSG,1) "Nasty:",TEXT(L1:L2 - 1) !This is the token when trouble was found.
IF (L2.LE.LEN(TEXT)) WRITE (MSG,1) "Misty:",TEXT(L2:) !And this remains to be seen.
1 FORMAT (4X,A,1X,A) !A simple layout works nicely for reasonable-length texts.
HAPPY = -1 . !Just so.
END SUBROUTINE GRUMP !Enuogh said.
COMPILE = HAPPY.GE.0 !One hopes so.
CONTAINS !Now for some assistants.
SUBROUTINE GRUMP(GROWL) !There might be a problem.
CHARACTER*(*) GROWL !The fault.
WRITE (MSG,1) GROWL !Say it.
IF (L1.GT. 1) WRITE (MSG,1) "Tasty:",TEXT( 1:L1 - 1) !Now explain the context.
IF (L2.GT.L1) WRITE (MSG,1) "Nasty:",TEXT(L1:L2 - 1) !This is the token when trouble was found.
IF (L2.LE.LEN(TEXT)) WRITE (MSG,1) "Misty:",TEXT(L2:) !And this remains to be seen.
1 FORMAT (4X,A,1X,A) !A simple layout works nicely for reasonable-length texts.
HAPPY = -1 . !Just so.
END SUBROUTINE GRUMP !Enuogh said.
SUBROUTINE BALANCEBRA(B) !Perhaps a happy meeting.
CHARACTER*1 B !The closer.
CHARACTER*1 O !The putative opener.
INTEGER IT,L !Fingers.
CHARACTER*88 GROWL !A scratchpad.
O = OPSTACK(OSP).OPC !This should match B.
WRITE (MSG,1) O,B !Perhaps.
1 FORMAT ("Match ",2A) !Show what I've got, anyway.
IT = INDEX(BRAOPEN,O) !So, what sort did I save?
IF (IT .EQ. INDEX(BRACLOSE,B)) THEN !A friend?
OSP = OSP - 1 !Yes. They vanish together.
ELSE !Otherwise, something is out of place.
GROWL = "Unbalanced {[(...)]} bracketing! The closing " !Alas.
1 //B//" is unmatched." !So, a mess.
IF (IT.GT.0) GROWL(62:) = "A "//BRACLOSE(IT:IT) !Perhaps there had been no opening bracket.
1 //" would be better." !But if there had, this would be its friend.
CALL GRUMP(GROWL) !Take that!
END IF !So much for discrepancies.
END SUBROUTINE BALANCEBRA !But, hopefully, amity prevails.
SUBROUTINE BALANCEBRA(B) !Perhaps a happy meeting.
CHARACTER*1 B !The closer.
CHARACTER*1 O !The putative opener.
INTEGER IT,L !Fingers.
CHARACTER*88 GROWL !A scratchpad.
O = OPSTACK(OSP).OPC !This should match B.
WRITE (MSG,1) O,B !Perhaps.
1 FORMAT ("Match ",2A) !Show what I've got, anyway.
IT = INDEX(BRAOPEN,O) !So, what sort did I save?
IF (IT .EQ. INDEX(BRACLOSE,B)) THEN !A friend?
OSP = OSP - 1 !Yes. They vanish together.
ELSE !Otherwise, something is out of place.
GROWL = "Unbalanced {[(...)]} bracketing! The closing " !Alas.
1 //B//" is unmatched." !So, a mess.
IF (IT.GT.0) GROWL(62:) = "A "//BRACLOSE(IT:IT) !Perhaps there had been no opening bracket.
1 //" would be better." !But if there had, this would be its friend.
CALL GRUMP(GROWL) !Take that!
END IF !So much for discrepancies.
END SUBROUTINE BALANCEBRA !But, hopefully, amity prevails.
SUBROUTINE FORASIGN !See what comes next.
INTEGER I !A stepper.
L1 = L2 !Pick up where the previous scan left off.
10 IF (L1.GT.LEN(TEXT)) THEN !Are we off the end yet?
C = "" !Yes. Scrub the marker.
L2 = L1 !TEXT(L1:L2 - 1) will be null.
TOKENTYPE = NOTHING !But this is to be checked first.
INCOMING = SYMBOL(1).PRECEDENCE !For flushing sidetracked operators.
HAPPY = 0 !Fading away.
ELSE !Otherwise, there is grist.
SUBROUTINE FORASIGN !See what comes next.
INTEGER I !A stepper.
L1 = L2 !Pick up where the previous scan left off.
10 IF (L1.GT.LEN(TEXT)) THEN !Are we off the end yet?
C = "" !Yes. Scrub the marker.
L2 = L1 !TEXT(L1:L2 - 1) will be null.
TOKENTYPE = NOTHING !But this is to be checked first.
INCOMING = SYMBOL(1).PRECEDENCE !For flushing sidetracked operators.
HAPPY = 0 !Fading away.
ELSE !Otherwise, there is grist.
Check for spaces and move past them.
C = TEXT(L1:L1) !Grab the first character of the prospective token.
IF (C.LE." ") THEN !Boring?
L1 = L1 + 1 !Yes. Step past it.
GO TO 10 !And look afresh.
END IF !Otherwise, L1 now fingers the start.
C = TEXT(L1:L1) !Grab the first character of the prospective token.
IF (C.LE." ") THEN !Boring?
L1 = L1 + 1 !Yes. Step past it.
GO TO 10 !And look afresh.
END IF !Otherwise, L1 now fingers the start.
Caught something to inspect.
L2 = L1 + 1 !This is one beyond. Just for digit strings.
IF (INDEX(DIGIT,C).GT.0) THEN !So, has one started?
TOKENTYPE = ANUMBER !Yep.
20 IF (L2.LE.LEN(TEXT)) THEN !Probe ahead.
IF (INDEX(DIGIT,TEXT(L2:L2)).GT.0) THEN !Another digit?
L2 = L2 + 1 !Yes. Leaving L1 fingering its start,
GO TO 20 !Chase its end.
END IF !So much for another digit.
END IF !And checking against the end.
C ELSE IF (INDEX(LETTERS,C).GT.0) THEN !Some sort of name?
L2 = L1 + 1 !This is one beyond. Just for digit strings.
IF (INDEX(DIGIT,C).GT.0) THEN !So, has one started?
TOKENTYPE = ANUMBER !Yep.
20 IF (L2.LE.LEN(TEXT)) THEN !Probe ahead.
IF (INDEX(DIGIT,TEXT(L2:L2)).GT.0) THEN !Another digit?
L2 = L2 + 1 !Yes. Leaving L1 fingering its start,
GO TO 20 !Chase its end.
END IF !So much for another digit.
END IF !And checking against the end.
C ELSE IF (INDEX(LETTERS,C).GT.0) THEN !Some sort of name?
C advance L2 while in NAMEISH.
ELSE IF (INDEX(BRAOPEN,C).GT.0) THEN !An open bracket?
TOKENTYPE = OPENBRA !Yep.
ELSE !Otherwise, anything else.
DO I = OPSYMBOLS,1,-1 !Scan backwards, to find ** before *, if present.
IF (SYMBOL(I).IS .EQ. C) EXIT !Found?
END DO !On to the next. A linear search will do.
IF (I.LE.0) THEN !Is it identified?
TOKENTYPE = HUH !No.
INCOMING = SYMBOL(0).PRECEDENCE !And this might provoke a flush.
ELSE !If it is identified,
TOKENTYPE = I !Then this is a positive number.
INCOMING = SYMBOL(I).PRECEDENCE !And this is of interest.
END IF !Righto, anything has been identified, possibly as HUH.
END IF !So much for classification.
END IF !If there is something to see.
WRITE (MSG,30) C,INCOMING,TOKENTYPE !Announce.
30 FORMAT ("Next=>",A1,"< Prec=",I2,",Ttype=",I2) !C might be blank.
END SUBROUTINE FORASIGN !I call for a sign, and I see what?
END FUNCTION COMPILE !That's the main activity.
END MODULE COMPILER !So, enough of this.
ELSE IF (INDEX(BRAOPEN,C).GT.0) THEN !An open bracket?
TOKENTYPE = OPENBRA !Yep.
ELSE !Otherwise, anything else.
DO I = OPSYMBOLS,1,-1 !Scan backwards, to find ** before *, if present.
IF (SYMBOL(I).IS .EQ. C) EXIT !Found?
END DO !On to the next. A linear search will do.
IF (I.LE.0) THEN !Is it identified?
TOKENTYPE = HUH !No.
INCOMING = SYMBOL(0).PRECEDENCE !And this might provoke a flush.
ELSE !If it is identified,
TOKENTYPE = I !Then this is a positive number.
INCOMING = SYMBOL(I).PRECEDENCE !And this is of interest.
END IF !Righto, anything has been identified, possibly as HUH.
END IF !So much for classification.
END IF !If there is something to see.
WRITE (MSG,30) C,INCOMING,TOKENTYPE !Announce.
30 FORMAT ("Next=>",A1,"< Prec=",I2,",Ttype=",I2) !C might be blank.
END SUBROUTINE FORASIGN !I call for a sign, and I see what?
END FUNCTION COMPILE !That's the main activity.
END MODULE COMPILER !So, enough of this.
PROGRAM POKE
USE COMPILER

View file

@ -1,21 +1,21 @@
Caution! The apparent gaps in the sequence of precedence values in this table are *not* unused!
Cunning ploys with precedence allow parameter evaluation, and right-to-left order as in x**y**z.
INTEGER OPSYMBOLS !Recognised operator symbols.
PARAMETER (OPSYMBOLS = 25) !There are also some associates.
TYPE SYMB !To recognise symbols and carry associated information.
CHARACTER*2 IS !Its text. Careful with the trailing space and comparisons.
INTEGER*1 PRECEDENCE !Controls the order of evaluation.
INTEGER*1 POPCOUNT !Stack activity: a+b means + requires two in.
CHARACTER*48 USAGE !Description.
END TYPE SYMB !The cross-linkage of precedences is tricky.
CHARACTER*5 IFPARTS(0:4) !These appear when an operator would otherwise be expected.
PARAMETER (IFPARTS = (/"IF","THEN","ELSE","OWISE","FI"/)) !So, bend the usage of "operator".
TYPE(SYMB) SYMBOL(-4:OPSYMBOLS) !Righto, I'll have some.
PARAMETER (SYMBOL =(/ !Note that "*" is not to be seen as a match to "**".
4 SYMB("FI", 2,0,"the FI that ends an IF-statement."), !These negative entries are not for name matching
3 SYMB("Ow", 3,0,"the OWISE part of an IF-statement."), !Which is instead done via IFPARTS
2 SYMB("El", 3,0,"the ELSE part of an IF-statement."), !But are here to take advantage of the structure in place.
1 SYMB("Th", 3,0,"the THEN part of an IF-statement."), !The IF is recognised separately, when expecting an operand.
INTEGER OPSYMBOLS !Recognised operator symbols.
PARAMETER (OPSYMBOLS = 25) !There are also some associates.
TYPE SYMB !To recognise symbols and carry associated information.
CHARACTER*2 IS !Its text. Careful with the trailing space and comparisons.
INTEGER*1 PRECEDENCE !Controls the order of evaluation.
INTEGER*1 POPCOUNT !Stack activity: a+b means + requires two in.
CHARACTER*48 USAGE !Description.
END TYPE SYMB !The cross-linkage of precedences is tricky.
CHARACTER*5 IFPARTS(0:4) !These appear when an operator would otherwise be expected.
PARAMETER (IFPARTS = (/"IF","THEN","ELSE","OWISE","FI"/)) !So, bend the usage of "operator".
TYPE(SYMB) SYMBOL(-4:OPSYMBOLS) !Righto, I'll have some.
PARAMETER (SYMBOL =(/ !Note that "*" is not to be seen as a match to "**".
4 SYMB("FI", 2,0,"the FI that ends an IF-statement."), !These negative entries are not for name matching
3 SYMB("Ow", 3,0,"the OWISE part of an IF-statement."), !Which is instead done via IFPARTS
2 SYMB("El", 3,0,"the ELSE part of an IF-statement."), !But are here to take advantage of the structure in place.
1 SYMB("Th", 3,0,"the THEN part of an IF-statement."), !The IF is recognised separately, when expecting an operand.
o SYMB(" ", 0,0,"Not recognised as an operator's symbol."),
1 SYMB(" ", 1,0,"separates symbols and aids legibility."),
C 2 and 3 are used for the parts of an IF-statement. See PRIF.
@ -43,6 +43,6 @@ C SYMB(":=", 6,0,"marks an on-the-fly assignment of a result"), Identified
1 SYMB("÷ ",12,2,"division for those with a fancy keyboard."),
2 SYMB("\ ",12,2,"remainder a\b = a - truncate(a/b)*b; 11\3 = 2"),
C 13 is used so that stacked ** will have lower priority than incoming **, thus delivering right-to-left evaluation.
3 SYMB("^ ",14,2,"raise to power: also recognised is **."), !Uses the previous precedence level also!
3 SYMB("^ ",14,2,"raise to power: also recognised is **."), !Uses the previous precedence level also!
4 SYMB("**",14,2,"raise to power: also recognised is ^."),
5 SYMB("! ",15,1,"factorial, sortof, just for fun.")/))

View file

@ -1,84 +1,82 @@
(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">show_workings</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
with javascript_semantics
bool show_workings = true
<span style="color: #008080;">constant</span> <span style="color: #000000;">operators</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"^"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"*"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"/"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"+"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"-"</span><span style="color: #0000FF;">},</span>
<span style="color: #000000;">precedence</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span> <span style="color: #000000;">4</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">2</span> <span style="color: #0000FF;">}</span>
constant operators = {"^","*","/","+","-"},
precedence = { 4, 3, 3, 2, 2 }
<span style="color: #008080;">procedure</span> <span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">infix</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">rpn</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">stack_top</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">stack</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span>
<span style="color: #000000;">ops</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">substitute_all</span><span style="color: #0000FF;">(</span><span style="color: #000000;">infix</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"("</span><span style="color: #0000FF;">,</span><span style="color: #008000;">")"</span><span style="color: #0000FF;">},{</span><span style="color: #008000;">" ( "</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" ) "</span><span style="color: #0000FF;">}),</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Infix input: %-32s%s"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">infix</span><span style="color: #0000FF;">,</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">show_workings</span><span style="color: #0000FF;">?</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">:</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">)})</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ops</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">op</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ops</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">op</span><span style="color: #0000FF;">=</span><span style="color: #008000;">"("</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">stack</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stack</span><span style="color: #0000FF;">,</span><span style="color: #000000;">op</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">op</span><span style="color: #0000FF;">=</span><span style="color: #008000;">")"</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">stack_top</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">stack</span><span style="color: #0000FF;">[$]</span>
<span style="color: #000000;">stack</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">stack</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">stack_top</span><span style="color: #0000FF;">=</span><span style="color: #008000;">"("</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">sep</span><span style="color: #0000FF;">&</span><span style="color: #000000;">stack_top</span>
<span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">else</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">op</span><span style="color: #0000FF;">,</span><span style="color: #000000;">operators</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">prec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">precedence</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">while</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stack</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">stack_top</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">stack</span><span style="color: #0000FF;">[$]</span>
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stack_top</span><span style="color: #0000FF;">,</span><span style="color: #000000;">operators</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #000000;">prec</span><span style="color: #0000FF;">></span><span style="color: #000000;">precedence</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">or</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">prec</span><span style="color: #0000FF;">=</span><span style="color: #000000;">precedence</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">and</span> <span style="color: #000000;">stack_top</span><span style="color: #0000FF;">=</span><span style="color: #008000;">"^"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">stack</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">stack</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">sep</span><span style="color: #0000FF;">&</span><span style="color: #000000;">stack_top</span>
<span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">stack</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stack</span><span style="color: #0000FF;">,</span><span style="color: #000000;">op</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">sep</span><span style="color: #0000FF;">&</span><span style="color: #000000;">op</span>
<span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">show_workings</span> <span style="color: #008080;">then</span>
<span style="color: #0000FF;">?{</span><span style="color: #000000;">op</span><span style="color: #0000FF;">,</span><span style="color: #000000;">stack</span><span style="color: #0000FF;">,</span><span style="color: #000000;">res</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stack</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">op</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">stack</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">sep</span><span style="color: #0000FF;">&</span><span style="color: #000000;">op</span>
<span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"result: %-22s [%s]\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">=</span><span style="color: #000000;">rpn</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"ok"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"**ERROR**"</span><span style="color: #0000FF;">)})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
procedure shunting_yard(string infix, rpn)
string res = "", sep = "", stack_top
sequence stack = {},
ops = split(substitute_all(infix,{"(",")"},{" ( "," ) "}),' ')
printf(1,"Infix input: %-32s%s", {infix,iff(show_workings?'\n':' ')})
for i=1 to length(ops) do
string op = ops[i]
if op="(" then
stack = append(stack,op)
elsif op=")" then
while 1 do
stack_top = stack[$]
stack = stack[1..$-1]
if stack_top="(" then exit end if
res &= sep&stack_top
sep = " "
end while
else
integer k = find(op,operators)
if k!=0 then
integer prec = precedence[k]
while length(stack) do
stack_top = stack[$]
k = find(stack_top,operators)
if k=0 or prec>precedence[k]
or (prec=precedence[k] and stack_top="^") then
exit
end if
stack = stack[1..$-1]
res &= sep&stack_top
sep = " "
end while
stack = append(stack,op)
else
res &= sep&op
sep = " "
end if
end if
if show_workings then
?{op,stack,res}
end if
end for
for i=length(stack) to 1 by -1 do
string op = stack[i]
res &= sep&op
sep = " "
end for
printf(1,"result: %-22s [%s]\n", {res,iff(res=rpn?"ok","**ERROR**")})
end procedure
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"3 + 4 * 2 / (1 - 5) ^ 2 ^ 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 4 2 * 1 5 - 2 3 ^ ^ / +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">show_workings</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"((1 + 2) ^ (3 + 4)) ^ (5 + 6)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 + 3 4 + ^ 5 6 + ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(1 + 2) ^ (3 + 4) ^ (5 + 6)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 + 3 4 + 5 6 + ^ ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"((3 ^ 4) ^ 2 ^ 9) ^ 2 ^ 5"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 4 ^ 2 9 ^ ^ 2 5 ^ ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(1 + 4) * (5 + 3) * 2 * 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 4 + 5 3 + * 2 * 3 *"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 * 2 * 3 * 4"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 * 3 * 4 *"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 + 2 + 3 + 4"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 + 3 + 4 +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(1 + 2) ^ (3 + 4)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 + 3 4 + ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(5 ^ 6) ^ 7"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"5 6 ^ 7 ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"5 ^ 4 ^ 3 ^ 2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"5 4 3 2 ^ ^ ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 + 2 + 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 + 3 +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 ^ 2 ^ 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 3 ^ ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(1 ^ 2) ^ 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 ^ 3 ^"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 - 1 + 3"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 1 - 3 +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"3 + 1 - 1"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 1 + 1 -"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1 - (2 + 3)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1 2 3 + -"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"4 + 3 + 2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"4 3 + 2 +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"5 + 4 + 3 + 2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"5 4 + 3 + 2 +"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"5 * 4 * 3 * 2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"5 4 * 3 * 2 *"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"5 + 4 - (3 + 2)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"5 4 + 3 2 + -"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"3 - 4 * 5"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 4 5 * -"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"3 * (4 - 5)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 4 5 - *"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"(3 - 4) * 5"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"3 4 - 5 *"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"4 * 2 + 1 - 5"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"4 2 * 1 + 5 -"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shunting_yard</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"4 * 2 / (1 - 5) ^ 2"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"4 2 * 1 5 - 2 ^ /"</span><span style="color: #0000FF;">)</span>
<!--
shunting_yard("3 + 4 * 2 / (1 - 5) ^ 2 ^ 3","3 4 2 * 1 5 - 2 3 ^ ^ / +")
show_workings = false
shunting_yard("((1 + 2) ^ (3 + 4)) ^ (5 + 6)","1 2 + 3 4 + ^ 5 6 + ^")
shunting_yard("(1 + 2) ^ (3 + 4) ^ (5 + 6)","1 2 + 3 4 + 5 6 + ^ ^")
shunting_yard("((3 ^ 4) ^ 2 ^ 9) ^ 2 ^ 5","3 4 ^ 2 9 ^ ^ 2 5 ^ ^")
shunting_yard("(1 + 4) * (5 + 3) * 2 * 3","1 4 + 5 3 + * 2 * 3 *")
shunting_yard("1 * 2 * 3 * 4","1 2 * 3 * 4 *")
shunting_yard("1 + 2 + 3 + 4","1 2 + 3 + 4 +")
shunting_yard("(1 + 2) ^ (3 + 4)","1 2 + 3 4 + ^")
shunting_yard("(5 ^ 6) ^ 7","5 6 ^ 7 ^")
shunting_yard("5 ^ 4 ^ 3 ^ 2","5 4 3 2 ^ ^ ^")
shunting_yard("1 + 2 + 3","1 2 + 3 +")
shunting_yard("1 ^ 2 ^ 3","1 2 3 ^ ^")
shunting_yard("(1 ^ 2) ^ 3","1 2 ^ 3 ^")
shunting_yard("1 - 1 + 3","1 1 - 3 +")
shunting_yard("3 + 1 - 1","3 1 + 1 -")
shunting_yard("1 - (2 + 3)","1 2 3 + -")
shunting_yard("4 + 3 + 2","4 3 + 2 +")
shunting_yard("5 + 4 + 3 + 2","5 4 + 3 + 2 +")
shunting_yard("5 * 4 * 3 * 2","5 4 * 3 * 2 *")
shunting_yard("5 + 4 - (3 + 2)","5 4 + 3 2 + -")
shunting_yard("3 - 4 * 5","3 4 5 * -")
shunting_yard("3 * (4 - 5)","3 4 5 - *")
shunting_yard("(3 - 4) * 5","3 4 - 5 *")
shunting_yard("4 * 2 + 1 - 5","4 2 * 1 + 5 -")
shunting_yard("4 * 2 / (1 - 5) ^ 2","4 2 * 1 5 - 2 ^ /")

View file

@ -3,198 +3,198 @@ import Foundation
// Using arrays for both stack and queue
struct Stack<T> {
private(set) var elements = [T]()
var isEmpty: Bool {
elements.isEmpty
}
var top: T? {
elements.last
}
mutating func push(_ newElement: T) {
elements.append(newElement)
}
mutating func pop() -> T? {
self.isEmpty ? nil : elements.removeLast()
}
private(set) var elements = [T]()
var isEmpty: Bool {
elements.isEmpty
}
var top: T? {
elements.last
}
mutating func push(_ newElement: T) {
elements.append(newElement)
}
mutating func pop() -> T? {
self.isEmpty ? nil : elements.removeLast()
}
}
struct Queue<T> {
private(set) var elements = [T]()
var isEmpty: Bool {
elements.isEmpty
}
mutating func enqueue(_ newElement: T) {
elements.append(newElement)
}
mutating func dequeue() -> T {
return elements.removeFirst()
}
private(set) var elements = [T]()
var isEmpty: Bool {
elements.isEmpty
}
mutating func enqueue(_ newElement: T) {
elements.append(newElement)
}
mutating func dequeue() -> T {
return elements.removeFirst()
}
}
enum Associativity {
case Left, Right
case Left, Right
}
// Protocol can be used to restrict Set extension
protocol OperatorType: Comparable, Hashable {
var name: String { get }
var precedence: Int { get }
var associativity: Associativity { get }
var name: String { get }
var precedence: Int { get }
var associativity: Associativity { get }
}
struct Operator: OperatorType {
let name: String
let precedence: Int
let associativity: Associativity
// Duplicate operator names are not allowed
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
self.name = name; self.precedence = precedence; self.associativity = associativity
}
let name: String
let precedence: Int
let associativity: Associativity
// Duplicate operator names are not allowed
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
self.name = name; self.precedence = precedence; self.associativity = associativity
}
}
func ==(x: Operator, y: Operator) -> Bool {
// Identified by name
x.name == y.name
// Identified by name
x.name == y.name
}
func <(x: Operator, y: Operator) -> Bool {
// Take precedence and associavity into account
(x.associativity == .Left && x.precedence == y.precedence) || x.precedence < y.precedence
// Take precedence and associavity into account
(x.associativity == .Left && x.precedence == y.precedence) || x.precedence < y.precedence
}
extension Set where Element: OperatorType {
func contains(_ operatorName: String) -> Bool {
contains { $0.name == operatorName }
}
subscript (operatorName: String) -> Element? {
get {
filter { $0.name == operatorName }.first
}
}
func contains(_ operatorName: String) -> Bool {
contains { $0.name == operatorName }
}
subscript (operatorName: String) -> Element? {
get {
filter { $0.name == operatorName }.first
}
}
}
// Convenience
extension String {
var isNumber: Bool { return Double(self) != nil }
var isNumber: Bool { return Double(self) != nil }
}
struct ShuntingYard {
enum ParseError: Error {
case MismatchedParenthesis(parenthesis: String, expression: String)
case UnrecognizedToken(token: String, expression: String)
case ExtraneousToken(token: String, expression: String)
}
static func parse(_ input: String, operators: Set<Operator>) throws -> String {
var stack = Stack<String>()
var output = Queue<String>()
let tokens = input.components(separatedBy: " ")
for token in tokens {
// Number?
if token.isNumber {
// Add it to the output queue
output.enqueue(token)
continue
}
// Operator?
if operators.contains(token) {
// While there is a operator on top of the stack and has lower precedence than current operator (token)
while let top = stack.top,
operators.contains(top) && Self.hasLowerPrecedence(token, top, operators) {
// Pop it off to the output queue
output.enqueue(stack.pop()!)
}
// Push current operator (token) onto the operator stack
stack.push(token)
continue
}
// Left parenthesis?
if token == "(" {
// Push it onto the stack
stack.push(token)
continue
}
// Right parenthesis?
if token == ")" {
// Until the token at the top of the stack is a left parenthesis
while let top = stack.top, top != "(" {
// Pop operators off the stack onto the output queue.
output.enqueue(stack.pop()!)
}
// Pop the left parenthesis from the stack without putting it onto the output queue
guard let _ = stack.pop() else {
// If the stack runs out, than there is no matching left parenthesis
throw ParseError.MismatchedParenthesis(parenthesis: ")", expression: input)
}
continue
}
// Token not recognized!
throw ParseError.UnrecognizedToken(token: token, expression: token)
}
// No more tokens
// Still operators on the stack?
while let top = stack.top,
operators.contains(top) {
// Put them onto the output queue
output.enqueue(stack.pop()!)
}
// If the top of the stack is a (left) parenthesis, then there is no matching right parenthesis
// Note: Assume that all operators has been popped and put onto the output queue
if let top = stack.pop() {
throw (
top == "("
? ParseError.MismatchedParenthesis(parenthesis: "(", expression: input)
: ParseError.ExtraneousToken(token: top, expression: input)
)
}
return output.elements.joined(separator: " ")
}
static private func hasLowerPrecedence(_ firstToken: String, _ secondToken: String, _ operators: Set<Operator>) -> Bool {
guard let firstOperator = operators[firstToken],
let secondOperator = operators[secondToken] else {
return false
}
return firstOperator < secondOperator
}
enum ParseError: Error {
case MismatchedParenthesis(parenthesis: String, expression: String)
case UnrecognizedToken(token: String, expression: String)
case ExtraneousToken(token: String, expression: String)
}
static func parse(_ input: String, operators: Set<Operator>) throws -> String {
var stack = Stack<String>()
var output = Queue<String>()
let tokens = input.components(separatedBy: " ")
for token in tokens {
// Number?
if token.isNumber {
// Add it to the output queue
output.enqueue(token)
continue
}
// Operator?
if operators.contains(token) {
// While there is a operator on top of the stack and has lower precedence than current operator (token)
while let top = stack.top,
operators.contains(top) && Self.hasLowerPrecedence(token, top, operators) {
// Pop it off to the output queue
output.enqueue(stack.pop()!)
}
// Push current operator (token) onto the operator stack
stack.push(token)
continue
}
// Left parenthesis?
if token == "(" {
// Push it onto the stack
stack.push(token)
continue
}
// Right parenthesis?
if token == ")" {
// Until the token at the top of the stack is a left parenthesis
while let top = stack.top, top != "(" {
// Pop operators off the stack onto the output queue.
output.enqueue(stack.pop()!)
}
// Pop the left parenthesis from the stack without putting it onto the output queue
guard let _ = stack.pop() else {
// If the stack runs out, than there is no matching left parenthesis
throw ParseError.MismatchedParenthesis(parenthesis: ")", expression: input)
}
continue
}
// Token not recognized!
throw ParseError.UnrecognizedToken(token: token, expression: token)
}
// No more tokens
// Still operators on the stack?
while let top = stack.top,
operators.contains(top) {
// Put them onto the output queue
output.enqueue(stack.pop()!)
}
// If the top of the stack is a (left) parenthesis, then there is no matching right parenthesis
// Note: Assume that all operators has been popped and put onto the output queue
if let top = stack.pop() {
throw (
top == "("
? ParseError.MismatchedParenthesis(parenthesis: "(", expression: input)
: ParseError.ExtraneousToken(token: top, expression: input)
)
}
return output.elements.joined(separator: " ")
}
static private func hasLowerPrecedence(_ firstToken: String, _ secondToken: String, _ operators: Set<Operator>) -> Bool {
guard let firstOperator = operators[firstToken],
let secondOperator = operators[secondToken] else {
return false
}
return firstOperator < secondOperator
}
}
/* Include in tests
func testParse() throws {
let input = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
let operators: Set<Operator> = [
Operator("^", 4, .Right),
Operator("*", 3, .Left),
Operator("/", 3, .Left),
Operator("+", 2, .Left),
Operator("-", 2, .Left)
]
let output = try! ShuntingYard.parse(input, operators: operators)
XCTAssertEqual(output, "3 4 2 * 1 5 - 2 3 ^ ^ / +")
let input = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
let operators: Set<Operator> = [
Operator("^", 4, .Right),
Operator("*", 3, .Left),
Operator("/", 3, .Left),
Operator("+", 2, .Left),
Operator("-", 2, .Left)
]
let output = try! ShuntingYard.parse(input, operators: operators)
XCTAssertEqual(output, "3 4 2 * 1 5 - 2 3 ^ ^ / +")
}
*/

View file

@ -14,42 +14,42 @@ proc associativity op {
proc shunting {expression} {
set stack {}
foreach token [tokenize $expression] {
if {[string is double $token]} {
puts "add to output: $token"
lappend output $token
} elseif {$token eq "("} {
puts "push parenthesis"
lappend stack $token
} elseif {$token eq ")"} {
puts "popping to parenthesis"
while {[lindex $stack end] ne "("} {
lappend output [lindex $stack end]
set stack [lreplace $stack end end]
puts "...popped [lindex $output end] to output"
}
set stack [lreplace $stack end end]
puts "...found parenthesis"
} else {
puts "adding operator: $token"
set p [precedence $token]
set a [associativity $token]
while {[llength $stack]} {
set o2 [lindex $stack end]
if {
$o2 ne "(" &&
(($a eq "left" && $p <= [precedence $o2]) ||
($a eq "right" && $p < [precedence $o2]))
} then {
puts "...popped operator $o2 to output"
lappend output $o2
set stack [lreplace $stack end end]
} else {
break
}
}
lappend stack $token
}
puts "\t\tOutput:\t$output\n\t\tStack:\t$stack"
if {[string is double $token]} {
puts "add to output: $token"
lappend output $token
} elseif {$token eq "("} {
puts "push parenthesis"
lappend stack $token
} elseif {$token eq ")"} {
puts "popping to parenthesis"
while {[lindex $stack end] ne "("} {
lappend output [lindex $stack end]
set stack [lreplace $stack end end]
puts "...popped [lindex $output end] to output"
}
set stack [lreplace $stack end end]
puts "...found parenthesis"
} else {
puts "adding operator: $token"
set p [precedence $token]
set a [associativity $token]
while {[llength $stack]} {
set o2 [lindex $stack end]
if {
$o2 ne "(" &&
(($a eq "left" && $p <= [precedence $o2]) ||
($a eq "right" && $p < [precedence $o2]))
} then {
puts "...popped operator $o2 to output"
lappend output $o2
set stack [lreplace $stack end end]
} else {
break
}
}
lappend stack $token
}
puts "\t\tOutput:\t$output\n\t\tStack:\t$stack"
}
puts "transferring tokens from stack to output"
lappend output {*}[lreverse $stack]

View file

@ -1,48 +1,48 @@
Token: 3
Output: 3
Operators:
Output: 3
Operators:
Token: +
Output: 3
Operators: +
Output: 3
Operators: +
Token: 4
Output: 3 4
Operators: +
Output: 3 4
Operators: +
Token: *
Output: 3 4
Operators: + *
Output: 3 4
Operators: + *
Token: 2
Output: 3 4 2
Operators: + *
Output: 3 4 2
Operators: + *
Token: /
Output: 3 4 2
Operators: + * /
Output: 3 4 2
Operators: + * /
Token: (
Output: 3 4 2
Operators: + * / (
Output: 3 4 2
Operators: + * / (
Token: 1
Output: 3 4 2 1
Operators: + * / (
Output: 3 4 2 1
Operators: + * / (
Token: -
Output: 3 4 2 1
Operators: + * / ( -
Output: 3 4 2 1
Operators: + * / ( -
Token: 5
Output: 3 4 2 1 5
Operators: + * / ( -
Output: 3 4 2 1 5
Operators: + * / ( -
Token: )
Output: 3 4 2 1 5 -
Operators: + * /
Output: 3 4 2 1 5 -
Operators: + * /
Token: ^
Output: 3 4 2 1 5 -
Operators: + * / ^
Output: 3 4 2 1 5 -
Operators: + * / ^
Token: 2
Output: 3 4 2 1 5 - 2
Operators: + * / ^
Output: 3 4 2 1 5 - 2
Operators: + * / ^
Token: ^
Output: 3 4 2 1 5 - 2
Operators: + * / ^ ^
Output: 3 4 2 1 5 - 2
Operators: + * / ^ ^
Token: 3
Output: 3 4 2 1 5 - 2 3
Operators: + * / ^ ^
Output: 3 4 2 1 5 - 2 3
Operators: + * / ^ ^
End parsing
Output: 3 4 2 1 5 - 2 3 ^ ^ / * +
Operators:
Output: 3 4 2 1 5 - 2 3 ^ ^ / * +
Operators:

View file

@ -1,7 +1,7 @@
var input="3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3";
var opa=Dictionary("^",T(4,True), "*",T(3,False), // op:(prec,rAssoc)
"/",T(3,False), "+",T(2,False), "-",T(2,False),
"/",T(3,False), "+",T(2,False), "-",T(2,False),
);
"infix: ".println(input);
@ -13,30 +13,30 @@ fcn parseInfix(e){
foreach tok in (e.split(" ")){
switch(tok){
case("("){ stack.append(tok) } // push "(" to stack
case(")"){
case(")"){
while(True){ // pop item ("(" or operator) from stack
op:=stack.pop();
if(op=="(") break; // discard "("
rpn+=" " + op; // add operator to result
}
}
if(op=="(") break; // discard "("
rpn+=" " + op; // add operator to result
}
}
else{ // default
o1:=opa.find(tok); // op or Void
if(o1){ // token is an operator
while(stack){
o1:=opa.find(tok); // op or Void
if(o1){ // token is an operator
while(stack){
// consider top item on stack
op:=stack[-1]; o2:=opa.find(op);
if(not o2 or o1[0]>o2[0] or
op:=stack[-1]; o2:=opa.find(op);
if(not o2 or o1[0]>o2[0] or
(o1[0]==o2[0] and o1[1])) break;
// top item is an operator that needs to come off
stack.pop();
rpn+=" " + op; // add it to result
}
// push operator (the new one) to stack
stack.append(tok);
}else // token is an operand
rpn+=(rpn and " " or "") + tok; // add operand to result
}
// top item is an operator that needs to come off
stack.pop();
rpn+=" " + op; // add it to result
}
// push operator (the new one) to stack
stack.append(tok);
}else // token is an operand
rpn+=(rpn and " " or "") + tok; // add operand to result
}
} // switch
display(tok,rpn,stack);
} // foreach