# # rosettacode.py # # parser for language used by rosettacode.org (http://rosettacode.org/wiki/Compiler/syntax_analyzer) # # Copyright Paul McGuire, 2019 # BNF = """ stmt_list = {stmt} ; stmt = ';' | Identifier '=' expr ';' | 'while' paren_expr stmt | 'if' paren_expr stmt ['else' stmt] | 'print' '(' prt_list ')' ';' | 'putc' paren_expr ';' | '{' stmt_list '}' ; paren_expr = '(' expr ')' ; prt_list = string | expr {',' String | expr} ; expr = and_expr {'||' and_expr} ; and_expr = equality_expr {'&&' equality_expr} ; equality_expr = relational_expr [('==' | '!=') relational_expr] ; relational_expr = addition_expr [('<' | '<=' | '>' | '>=') addition_expr] ; addition_expr = multiplication_expr {('+' | '-') multiplication_expr} ; multiplication_expr = primary {('*' | '/' | '%') primary } ; primary = Identifier | Integer | '(' expr ')' | ('+' | '-' | '!') primary ; """ import pyparsing as pp pp.ParserElement.enablePackrat() LBRACE, RBRACE, LPAR, RPAR, SEMI = map(pp.Suppress, "{}();") EQ = pp.Literal("=") keywords = (WHILE, IF, PRINT, PUTC, ELSE) = map( pp.Keyword, "while if print putc else".split() ) any_keyword = pp.MatchFirst(keywords) identifier = ~any_keyword + pp.pyparsing_common.identifier integer = pp.pyparsing_common.integer string = pp.QuotedString('"', convertWhitespaceEscapes=False).setName("quoted string") char = pp.Regex(r"'\\?.'") # fmt: off expr = pp.infixNotation( identifier | integer | char, [ (pp.oneOf("+ - !"), 1, pp.opAssoc.RIGHT,), (pp.oneOf("* / %"), 2, pp.opAssoc.LEFT, ), (pp.oneOf("+ -"), 2, pp.opAssoc.LEFT, ), (pp.oneOf("< <= > >="), 2, pp.opAssoc.LEFT, ), (pp.oneOf("== !="), 2, pp.opAssoc.LEFT, ), (pp.oneOf("&&"), 2, pp.opAssoc.LEFT, ), (pp.oneOf("||"), 2, pp.opAssoc.LEFT, ), ], ) # fmt: on prt_list = pp.Group(pp.delimitedList(string | expr)) paren_expr = pp.Group(LPAR + expr + RPAR) stmt = pp.Forward() assignment_stmt = pp.Group(identifier + EQ + expr + SEMI) while_stmt = pp.Group(WHILE - paren_expr + stmt) if_stmt = pp.Group(IF - paren_expr + stmt + pp.Optional(ELSE + stmt)) print_stmt = pp.Group(PRINT - pp.Group(LPAR + prt_list + RPAR) + SEMI) putc_stmt = pp.Group(PUTC - paren_expr + SEMI) stmt_list = pp.Group(LBRACE + stmt[...] + RBRACE) stmt <<= ( pp.Group(SEMI) | assignment_stmt | while_stmt | if_stmt | print_stmt | putc_stmt | stmt_list ).setName("statement") code = stmt[...] code.ignore(pp.cppStyleComment) tests = [ r""" count = 1; while (count < 10) { print("count is: ", count, "\n"); count = count + 1; } """, r""" /* Simple prime number generator */ count = 1; n = 1; limit = 100; while (n < limit) { k=3; p=1; n=n+2; while ((k*k<=n) && (p)) { p=n/k*k!=n; k=k+2; } if (p) { print(n, " is prime\n"); count = count + 1; } } print("Total primes found: ", count, "\n"); """, r""" /* Hello world */ print("Hello, World!\n"); """, r""" /* Show Ident and Integers */ phoenix_number = 142857; print(phoenix_number, "\n"); """, r""" /*** test printing, embedded \n and comments with lots of '*' ***/ print(42); print("\nHello World\nGood Bye\nok\n"); print("Print a slash n - \\n.\n"); """, r""" /* 100 Doors */ i = 1; while (i * i <= 100) { print("door ", i * i, " is open\n"); i = i + 1; } """, r""" a = (-1 * ((-1 * (5 * 15)) / 10)); print(a, "\n"); b = -a; print(b, "\n"); print(-b, "\n"); print(-(1), "\n"); """, r""" print(---------------------------------+++5, "\n"); print(((((((((3 + 2) * ((((((2))))))))))))), "\n"); if (1) { if (1) { if (1) { if (1) { if (1) { print(15, "\n"); } } } } } """, r""" /* Compute the gcd of 1071, 1029: 21 */ a = 1071; b = 1029; while (b != 0) { new_a = b; b = a % b; a = new_a; } print(a); """, r""" /* 12 factorial is 479001600 */ n = 12; result = 1; i = 1; while (i <= n) { result = result * i; i = i + 1; } print(result); """, r""" /* fibonacci of 44 is 701408733 */ n = 44; i = 1; a = 0; b = 1; while (i < n) { w = a + b; a = b; b = w; i = i + 1; } print(w, "\n"); """, r""" /* FizzBuzz */ i = 1; while (i <= 100) { if (!(i % 15)) print("FizzBuzz"); else if (!(i % 3)) print("Fizz"); else if (!(i % 5)) print("Buzz"); else print(i); print("\n"); i = i + 1; } """, r""" /* 99 bottles */ bottles = 99; while (bottles > 0) { print(bottles, " bottles of beer on the wall\n"); print(bottles, " bottles of beer\n"); print("Take one down, pass it around\n"); bottles = bottles - 1; print(bottles, " bottles of beer on the wall\n\n"); } """, r""" { /* This is an integer ascii Mandelbrot generator */ left_edge = -420; right_edge = 300; top_edge = 300; bottom_edge = -300; x_step = 7; y_step = 15; max_iter = 200; y0 = top_edge; while (y0 > bottom_edge) { x0 = left_edge; while (x0 < right_edge) { y = 0; x = 0; the_char = ' '; i = 0; while (i < max_iter) { x_x = (x * x) / 200; y_y = (y * y) / 200; if (x_x + y_y > 800 ) { the_char = '0' + i; if (i > 9) { the_char = '@'; } i = max_iter; } y = x * y / 100 + y0; x = x_x - y_y + x0; i = i + 1; } putc(the_char); x0 = x0 + x_step; } putc('\n'); y0 = y0 - y_step; } } """, ] def main(): import sys sys.setrecursionlimit(2000) success, report = code.run_tests(tests) assert success if __name__ == '__main__': main()