Jim's
Tutorials

Fall 2016
course
navigation

Parsers, BRAG, and ragg

I spent the last week reading about Rackets parser generator.
After a few days of reader the docs and a "very simple" example I decided that I didn't like it.
I poked around and found two alternative parser generators: ragg and brag
Brag seems to be more modern and up to date so I went with that one.
I worked through the examples to get a VERY BASIC ast generated from the LISP lexer I made a few weeks ago.
I attached a zip with all the files below as well as output.
Here is the code.

The lexer/parser
#lang racket #| toy_lisp_lex.rkt Description: A simple LISP/Rack Tokenizer/parser. To use: $ racket toy_lisp_lex.rkt <lisp file to parse> Racket 6.3 | 9/23/16 | License MIT |# (require parser-tools/lex) (require parser-tools/lex-sre) (require brag/support) (require "lisp_ast.rkt") (define token-stream '()) (define-lex-abbrev comment? (or (: #\; (* (char-complement #\newline))) (: "#|" (complement (: any-string "|#" any-string)) "|#"))) (define-lex-abbrev meta-word? (: #\# (* alphabetic) #\space)) (define-lex-abbrev literal? (or (+ (char-range #\0 #\9)) (: (* (char-range #\0 #\9)) "." (or (* (char-range #\0 #\9)) (* (char-range #\a #\z)))) (: #\" (* any-char) #\") (: #\' any-char #\') (or "#f" "#t"))) (define-lex-abbrev identifier? (: (+ alphabetic) (* (or alphabetic #\- #\> #\< #\_ #\+ #\. #\/ #\= #\! #\? #\: #\$ #\% #\& #\~ #\^)))) (define-lex-abbrev lparen? (or #\( #\[ #\{)) (define-lex-abbrev rparen? (or #\) #\] #\})) (define-lex-abbrev symbol? (: #\' (: (+ alphabetic) (* (or alphabetic #\- #\> #\< #\_ #\+ #\. #\/ #\= #\! #\? #\: #\$ #\% #\& #\~ #\^))))) (define-lex-abbrev keyword? (or "=>" "do" "or" "and" "else" "quasiquote" "begin" "if" "quote" "case" "lambda" "set!" "cond" "let" "unquote" "define" "let*" "unquote-splicing" "delay" "letrec" "+" "-" "*" "/" "mod" "not" "equal?" ">" "<" ">=" ">=" "=")) ; A lexer to tokenize LISP files (define tokenize (lambda (file-port) (port-count-lines! file-port) (define lisp-lexer (lexer [comment? (token 'COMMENT lexeme #:skip? #t)] [meta-word? (token 'META-WORD lexeme)] [lparen? (token 'LPAREN lexeme)] [rparen? (token 'RPAREN lexeme)] [symbol? (token 'SYM lexeme)] [#\' (token 'QUOTE "'")] ;; Quote for next s-exp [literal? (token 'LIT lexeme)] [keyword? (token 'KEYWORD lexeme)] [identifier? (token 'ID lexeme)] [(or #\newline #\space "") (token 'WHITESPACE lexeme #:skip? #t)] ;; Ignore [(eof) (void)])) (define (next-token) (lisp-lexer file-port)) next-token)) (define test '()) ; Run over a file specified at the command line call (with-handlers ([exn:fail? (lambda (exn) (printf "Please provide a file name to lex...\n"))]) (set! test (open-input-file (vector-ref (current-command-line-arguments) 0))) (set! token-stream (tokenize test)) (define ast (parse token-stream)) (syntax->datum ast))

The parser grammar #lang brag program: lang-name s-exp* s-exp: LPAREN (KEYWORD | ID | s-exp| QUOTE s-exp | LIT | SYM | COMMENT)* RPAREN lang-name: META-WORD ID

The test file #lang racket #| primefinder.rkt ---------------------------------------------------------- Description: A simple prime finder to solver problem 7 - "10001st prime": https://projecteuler.net/problem=7 Use: import or run a promt with these definitions and call (main). ---------------------------------------------------------- References: https://docs.racket-lang.org/ https://learnxinyminutes.com/docs/racket/ ---------------------------------------------------------- Contact: ldavis@marlboro.edu https://github.com/SafeCamp https://safecamp.github.io/ Logan Davis | 1/23/16 | DrRacket:6.2/Lang:racket |# (define prime? #| A simple, but reasonably fast prime test that returns true if the test was positive and false otherwise. Arguments = 2: - num: the number to test. - lst-of-tests: prime numbers used to test num_to_test. |# (lambda (num lst-of-primes) (cond [(null? lst-of-primes) #t] [(< (+ (sqrt num) 1) (first lst-of-primes)) #t] [(= (remainder num (first lst-of-primes)) 0) #f] [else (prime? num (rest lst-of-primes))]))) (define finder #| Finds what prime number corresponders to a given index. This function is 0 indexing, E.G. passing '0' to the index arg will result in '2' being returned. Arguments = 3: current-num: The number to be tested. index: The desired index of prime to be found. lst-of-primes: prime numbers found. |# (lambda (current-num index lst-of-primes) (cond [(= index 0) (last lst-of-primes)] [(prime? current-num lst-of-primes) (finder (+ current-num 2) (- index 1) (append lst-of-primes (list current-num)))] [else (finder (+ current-num 2) index lst-of-primes)]))) (define main #| Prompts the user to enter a the index of the prime they wish to know and then calulates it using (finder). |# (lambda () (print "Please enter the desired index of the number you wish to find:") (define target (read-line)) (define answer (finder 3 (- (string->number target) 1) '(2))) answer))
http://cs.marlboro.edu/ courses/ fall2016/jims_tutorials/ ldavis/ Oct_12
last modified Tuesday October 11 2016 11:30 pm EDT

attachments [paper clip]

     name last modified size
   ast.output Oct 11 2016 11:30 pm 1.53kB    lisp_Ast.zip Oct 11 2016 11:28 pm 2.79kB