Implementing a C like program in racket using lex/yacc - parsing

I was looking at these two resources (https://github.com/racket/parser-tools/blob/master/parser-tools-lib/parser-tools/examples/calc.rkt and https://gist.github.com/gcr/1318240) and although I don't fully understand yet how the main calc function works I wondered if it's possible to extend this to work for a simple c like program just without functions? So it will lex, parse and evaluate ifs, whiles and print statements. So something like (define-empty-tokens op-tokens ( newline = OC CC (open-curly/closed-curly for block statements) DEL PRINT WHILE (WHILE exp S) S IF S1 S2 (IF exp S1 S2) OP CP + - * / || % or && == != >= <= > < EOF ))
Here is how I've extended it (the code of the first link) so far to also work with booleans:
So in calcl I added these two lines:
[ (:= 2 #\|) (token-||)]
[(:or "=" "+" "-" "*" "/" "%" "&&" "==" "!=" ">=" "<=" ">" "<") (string->symbol lexeme)]
And then later:
(define calcp
(parser
(start start)
(end newline EOF)
(tokens value-tokens op-tokens)
(error (lambda (a b c) (void)))
(precs (right =)
(left ||)
(left &&)
(left == !=)
(left <= >= < >)
(left - +)
(left * / %)
)
(grammar
(start [() #f]
[(error start) $2]
[(exp) $1])
(exp [(NUM) $1]
[(VAR) (hash-ref vars $1 (lambda () 0))]
[(VAR = exp) (begin (hash-set! vars $1 $3)
$3)]
[(exp || exp) (if (not(and (equal? $1 0) (equal? $3 0) )) 1 0) ]
[(exp && exp) (and $1 $3)]
[(exp == exp) (equal? $1 $3)]
[(exp != exp) (not(equal? $1 $3))]
[(exp < exp) (< $1 $3)]
[(exp > exp) (> $1 $3)]
[(exp >= exp) (>= $1 $3)]
[(exp <= exp) (<= $1 $3)]
[(exp + exp) (+ $1 $3)]
[(exp - exp) (- $1 $3)]
[(exp * exp) (* $1 $3)]
[(exp / exp) (/ $1 $3)]
[(exp % exp) (remainder $1 $3)]
[(OP exp CP) $2]))))
But I'm struggling to understand the above code as well as the below. I would lile to change it so that it also to works for ifs and whiles etc. if it's at all possible?
(define (calc ip)
(port-count-lines! ip)
(letrec ((one-line
(lambda ()
(let ((result (calcp (lambda () (calcl ip)) )))
(when result (printf "~a\n" result) (one-line))
)
) ))
(one-line))
)
Also, this guy seems to be relying on newlines to mark the end of a statement. i.e. you can't have more than 1 statement on one line. I want the program to recognise two statements on one line and evaluate them separately by somehow looking ahead and checking whether there's a new undeclared variable, special keyword or open/closed bracket etc.
Update:
I managed, with the below rules, to build in brag an AST for arith expressions but how do I get rid of all but the important parens so that I can evaluate it?
Eg: with input list: (list (token 'NUM 17) '+ (token 'NUM 1) '* (token 'NUM 3) '/ 'OP (token 'NUM 6) '- (token 'NUM 5) 'CP)
I'm getting back:
'(exp (((((factor 17)))) + (((((factor 1))) * ((factor 3))) / ((((((factor 6)))) - (((factor 5))))))))
Here are my rules:
exp : add
/add : add ('+' mul)+ | add ('-' mul)+ | mul
/mul : mul ('*' atom)+ | mul ('/' atom)+ | mul ('%' atom)+ | atom
/atom : /OP add /CP | factor
factor : NUM | ID

You cannot easily implement a language with conditionals and looping constructs using an evaluator based on immediate evaluation.
That should be clear at least for loops. If you have something like (using a super-simplified syntax):
repeat 3 { i = i + 1 }
If you evaluate during the parse, i = i + 1 will be evaluated exactly once, since the string is parsed exactly once. In order for it to be evaluated several times, the parser needs to convert i = i + 1 into something that can be evaluated several times when the repeat is evaluated.
This something is usually an Abstract Syntax Tree (AST), or possibly a list of virtual machine operations. With Scheme, you could also just turn the expression being parsed into a functional.
All of this is totally practical and not even particularly difficult, but you do need to be prepared to do some reading, both about parsing and about generating executables. For the latter, I highly recommend the classic Structure and Interpretation of Computer Programs (Abelson & Sussman).

Based on what I've seen here and here I managed to implement the lex and parse phase of an interpreter for a simple c-like program. It doesn't have functions in it but it has assignments, variables, conditionals, loops and print statements (As well as arithmetic, it also contains logical expressions.). I'm posting it below, in case others may find it useful (The whole thing including the evaluation phase and samples of input is here):
(require parser-tools/yacc //provides you with the lexer, the parser and the lexeme tools eg. string-> symbol, string->number etc - In general, with the ability to map the literals in the input
parser-tools/lex
(prefix-in : parser-tools/lex-sre))
(define-tokens value-tokens (NUM VAR ))
(define-empty-tokens op-tokens ( newline = OC CC DEL OP CP + - * / || % or && == != >= <= > < EOF PRINT WHILE IF ELSE ))
(define vars (make-hash)) ;to store the values in the variables
(define-lex-abbrevs
(lower-letter (:/ "a" "z"))
(upper-letter (:/ #\A #\Z))
(digit (:/ "0" "9")))
(define calcl ;lexer is mapping the literals to their tokens or values
(lexer
[(eof) 'EOF]
[(:or #\tab #\space #\return #\newline ) (calcl input-port)]
[ (:= 2 #\|) (token-||)]
[(:or "=" "+" "-" "*" "/" "%" "&&" "==" "!=" ">=" "<=" ">" "<") (string->symbol lexeme)]
["(" 'OP]
[")" 'CP]
["{" 'OC]
["}" 'CC]
[ "print" 'PRINT ]
[#\, 'DEL ]
[ "while" 'WHILE ]
[ "if" 'IF ]
[ "else" 'ELSE ]
[(:+ (:or lower-letter upper-letter)) (token-VAR (string->symbol lexeme))]
[(:+ digit) (token-NUM (string->number lexeme))]
))
(define calcp ;defines how to parse the program and how the program structure is made up (recursively)
(parser
(start start);refers to the block named 'start' below. Every parser has a start, end, tokens definition, optional error message when needed and operator precedence def.
(end EOF)
(tokens value-tokens op-tokens )
(error (Ξ»(ok? name value) (if (boolean? value) (printf "Couldn't parse: ~a\n" name) (printf "Couldn't parse: ~a\n" value))))
(precs ;sets the precedence of the operators in relation to each other - that is, to which operand they bind stronger
(left DEL);from lowest to highest
(right =)
(left ||)
(left &&)
(left == !=)
(left <= >= < >)
(left - +)
(left * / %)
(right OP)
(left CP)
(right OC)
(left CC)
)
(grammar ;what is the grammar of my program?
(start
[() '()] ; returns empty list when it matches onto nothing
[(statements) `(,$1)]
[(statements start) `(,$1,$2)] ;we can have more than one statement - one example of the recursiveness
)
(statements /what type of major statements we might have
[(var = exp ) `(assign ,$1 ,$3)]
[(IF ifState) $2]
[(WHILE while) $2]
[(PRINT printVals) `(print ,$2)]
)
(ifState ; It's assumed that you cannot have an if inside an if unless it's within curly braces
[(OP exp CP statements) `(if ,$2 ,$4)] /combinations of different major statements
[(OP exp CP block) `(if ,$2 ,$4)]
[(OP exp CP block ELSE statements ) `(if ,$2 ,$4 ,$6)]
[(OP exp CP statements ELSE block ) `(if ,$2 ,$4 ,$6)]
[(OP exp CP statements ELSE statements ) `(if ,$2 ,$4 ,$6)]
[(OP exp CP block ELSE block ) `(if ,$2 ,$4 ,$6)]
)
(while
[(OP exp CP block) `(while ,$2, $4)]
)
(block
[(OC start CC) $2] ;we can statements or entire program wrapped into curly braces - a block
)
(var
[(VAR) $1]
)
(printVals
[(exp DEL printVals ) `(,$1 ,$3)]
[(exp) $1]
)
(exp [(NUM) $1] ; smallest (most reducible) chunk in an expression when the chunk is an integer
[(VAR) $1] ; smallest (most reducible) chunk in an expression when the chunk is a variable
[(exp || exp) `((lambda (a b) (or a b)) ,$1 ,$3) ]
[(exp && exp) `((lambda (a b) (and a b)) ,$1 ,$3)]
[(exp == exp) `(equal? ,$1 ,$3)]
[(exp != exp) `(not(equal? ,$1 ,$3))]
[(exp < exp) `(< ,$1 ,$3)]
[(exp > exp) `(> ,$1 ,$3)]
[(exp >= exp) `(>= ,$1 ,$3)]
[(exp <= exp) `(<= ,$1 ,$3)]
[(exp + exp) `(+ ,$1 ,$3)]
[(exp - exp) `(- ,$1 ,$3)]
[(exp * exp) `(* ,$1 ,$3)]
[(exp / exp) `(quotient ,$1 ,$3)]
[(exp % exp) `(modulo ,$1 ,$3)]
[(OP exp CP) $2]) ;when the expressions are wrapped parentheses
)
)
)
Procedure calls the parser passing it a lambda (that calls the lexer providing it with the input) which it will use to get values from the lexer, as many values at a time as it sees fit.
; i.e. according to what the parsing rules above, prescribe
(define (calceval ip)
(calcp (lambda () (calcl ip))))
To see the evaluator (unfortunately without much comments yet) see here

Related

Type checker problem while writing a parser in racket

I hava a language PLANG that supports evaluating a
polynomial on a sequence of points (numbers).
the language allows expressions of the
form {{ π’‘π’π’π’š π‘ͺ𝟏 π‘ͺ𝟐 … π‘ͺπ’Œ} {π‘·πŸ π‘·πŸ … 𝑷𝓡}} where all 𝐢𝑖 and all 𝑃𝑗 are
valid AE expressions (and both π‘˜ β‰₯ 1 and β„“ β‰₯ 1).
I was trying to write a parse for this language here is what I have so far:
(define-type PLANG
[Poly (Listof AE) (Listof AE)])
(define-type AE
[Num Number]
[Add AE AE]
[Sub AE AE]
[Mul AE AE]
[Div AE AE])
(: parse-sexpr : Sexpr -> AE)
;; to convert s-expressions into AEs
(define (parse-sexpr sexpr)
(match sexpr
[(number: n) (Num n)]
[(list '+ lhs rhs) (Add (parse-sexpr lhs)
(parse-sexpr rhs))]
[(list '- lhs rhs) (Sub (parse-sexpr lhs)
(parse-sexpr rhs))]
[(list '* lhs rhs) (Mul (parse-sexpr lhs)
(parse-sexpr rhs))]
[(list '/ lhs rhs) (Div (parse-sexpr lhs)
(parse-sexpr rhs))]
[else (error 'parse-sexpr "bad syntax in ~s"
sexpr)]))
(: parse : String -> PLANG)
;; parses a string containing a PLANG expression to a PLANG AST
(define (parse str)
(let ([code (string->sexpr str)])
(parse-sexpr (code) )))
(test (parse "{{poly 1 2 3} {1 2 3}}")
=> (Poly (list (Num 1) (Num 2) (Num 3))
(list (Num 1) (Num 2) (Num 3))))
(test (parse "{{poly } {1 2} }")
=error> "parse: at least one coefficient is
required in ((poly) (1 2))")
(test (parse "{{poly 1 2} {} }")
=error> "parse: at least one point is
required in ((poly 1 2) ())")
when I'm trying to make it run I get the errors:
Type Checker: Cannot apply expression of type (U (Listof Sexpr) Boolean Real String Symbol), since it is not a function type in: (code)
. Type Checker: type mismatch
expected: Poly
given: AE in: (parse-sexpr (code))
. Type Checker: Summary: 2 errors encountered in:
(code)
(parse-sexpr (code))
>
Any help would be appreciated..
The first problem is caused by an extra pair of parentheses. Keep in mind that in Racket, Typed Racket, and #lang pl, parentheses usually mean function application like this:
(function argument ...)
So when you write (code), it tries to interpret code as a function, to call it with zero arguments.
You can fix this problem by replacing (code) with code in the body of the parse function.
(define (parse str)
(let ([code (string->sexpr str)])
(parse-sexpr code)))
The second problem happens because you specified that the parse function should return a PLANG, but it instead returns the result of parse-sexpr which returns an AE.
Another way of wording this is that you've implemented parsing for AEs, but not for PLANGs.

Make script work both in DrRacket and (x)repl

I am trying to make a script work from both DrRacket and the repl, having this as my starting point: Racket calculator
Here is my current code:
#lang racket
(provide (all-defined-out))
(require parser-tools/lex
(prefix-in re: parser-tools/lex-sre)
parser-tools/yacc)
(define-tokens value-tokens (INT ANY))
(define-empty-tokens empty-tokens
(PLUS MINUS MULTIPLY DIVIDE NEWLINE EOF))
(define basic-lexer
(lexer
((re:+ numeric) (token-INT lexeme))
(#\+ (token-PLUS))
(#\- (token-MINUS))
(#\* (token-MULTIPLY))
(#\/ (token-DIVIDE))
((re:or #\tab #\space) (basic-lexer input-port))
(#\newline (token-NEWLINE))
((eof) (token-EOF))
(any-char (token-ANY lexeme))))
(define (display-plus expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (+ left right)))
(newline))
(define (display-minus expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (- left right)))
(newline))
(define (display-multiply expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (* left right)))
(newline))
(define (display-divide expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (/ left right)))
(newline))
(define basic-parser
(parser
(start start)
(end NEWLINE EOF)
(tokens value-tokens empty-tokens)
(error (lambda (ok? name value)
(printf "Couldn't parse: ~a\n" name)))
(grammar
(start ((expr) $1)
((expr start) $2))
(expr ((INT PLUS INT) (display-plus (list $1 $3)))
((INT MINUS INT) (display-minus (list $1 $3)))
((INT MULTIPLY INT) (display-multiply (list $1 $3)))
((INT DIVIDE INT) (display-divide (list $1 $3)))
((ANY) (displayln $1))))))
(define input1 (open-input-string "123 + 456"))
(define input2 (open-input-string "123 *456"))
(basic-parser (lambda() (basic-lexer input1)))
(basic-parser (lambda() (basic-lexer input2)))
;(define (my-repl)
; (display ">>> ")
; (let* ((input (read-line))
; (input-port (open-input-string
; (list->string
; (drop-right
; (string->list input) 1)))))
; (cond
; ((not (equal? "\r" input)
; (print (basic-parser
; (lambda () (basic-lexer input-port))))))))
; (my-repl))
(define (calc str)
(let* ([port (open-input-string str)]
[result (basic-parser (lambda() (basic-lexer port)))])
(displayln result)))
(define (repl)
(display ">>> ")
(let ((input (read-line)))
(print input)
(cond
((eof-object? input) (displayln "eof"))
((eq? input #\newline) (displayln "new line"))
(else (calc (read-line))))
(newline))
(repl))
A test from DrRacket is shown here:
Welcome to DrRacket, version 7.1 [3m].
Language: racket, with debugging; memory limit: 512 MB.
Result: 579
Result: 56088
> (repl)
>>> 1+1
"1+1"2+2
Result: 4
#<void>
>>> 3+3
"3+3"4+4
Result: 8
#<void>
And from the repl:
Welcome to Racket v7.1.
> (require "untitled7.rkt")
Result: 579
Result: 56088
> (repl)
>>> "\r"
#<void>
>>> 1+1
"1+1\r"2+2
Result: 4
#<void>
>>> 3+3
"3+3\r"4+4
Result: 8
#<void>
>>> #<eof>eof
>>> ; user break [,bt for context]
It only displays every second calculation. It appears that read-line returns a new line before waiting for user input, which I tried to check with (eof-object? input) and (eq? input #\newline) but now I get only every second result.
There are two problems:
First, you're reading a line, (let ((input (read-line))), but you're not sending that input to the calculator, you'r sending another one – (calc (read-line)).
You should pass input to calc for evaluation instead.
Second, you have a lot of #<void>s in your output.
This is because calc assumes that your parser produces a value that it can print:
(displayln result)
but the parser does not produce any value, it only prints one.
Either remove the output of result, or rewrite the parser to return the value to its caller.
Replace (calc (read-line)) with (calc input).

Thinking in Clojure: Avoid OOP for simple string parser

I'm currently implementing a small parser in Clojure that takes an input string like:
aaa (bbb(ccc)ddd(eee)) fff (ggg) hhh
and returns the string without characters that are not in brackets, i.e.
(bbb(ccc)ddd(eee))(ggg)
I've written the following function:
(defn- parse-str [input]
(let [bracket (atom 0)
output (atom [])]
(doseq [ch (seq input)]
(case ch
\( (swap! bracket inc)
\) (swap! bracket dec)
nil)
(if (or (> #bracket 0) (= ch \)))
(swap! output conj ch)))
(apply str #output)))
which works for me:
(parse-str "aaa (bbb(ccc)ddd(eee)) fff (ggg) hhh")
"(bbb(ccc)ddd(eee))(ggg)"
I am however concerned that my approach is a too object oriented since it uses atoms as some kind of local variables to keep the current state of the parser.
Is it possible to write the same function from a more functional programming perspective? (avoiding the atoms?)
Any comments to improve my code are appreciated as well.
Two ways: You can use explicit recursion or reduce.
(defn parse-str [input]
(letfn [(parse [input bracket result]
(if (seq input)
(let [[ch & rest] input]
(case ch
\( (recur rest (inc bracket) (conj result ch))
\) (recur rest (dec bracket) (conj result ch))
(recur rest bracket (if (> bracket 0)
(conj result ch)
result))))
result))]
(clojure.string/join (parse input 0 []))))
(defn parse-str [input]
(clojure.string/join
(second (reduce (fn [acc ch]
(let [[bracket result] acc]
(case ch
\( [(inc bracket) (conj result ch)]
\) [(dec bracket) (conj result ch)]
[bracket (if (> bracket 0)
(conj result ch)
result)])))
[0 []]
input))))
In a lot of cases where you would use local variables, you just put any variable that changes as a parameter to loop, thereby using recursion instead of mutation.
(defn- parse-str [input]
;; Instead of using atoms to hold the state, use parameters in loop
(loop [output []
bracket 0
;; The [ch & tail] syntax is called destructuring,
;; it means let ch be the first element of (seq input),
;; and tail the rest of the elements
[ch & tail] (seq input)]
;; If there's no elements left, ch will be nil, which is logical false
(if ch
(let [bracket* (case ch
\( (inc bracket)
\) (dec bracket)
bracket)
output* (if (or (> bracket* 0) (= ch \)))
(conj output ch)
output)]
;; Recurse with the updated values
(recur output* bracket* tail))
;; If there's no characters left, apply str to the output
(apply str output))))
This is an iterative version of your function; but it's still functionally pure. I find having the code laid out like this makes it easy to read. Remember, when using recursion, always check your termination condition first.
(defn parse-str [s]
(loop [[x & xs] (seq s), acc [], depth 0]
(cond
(not x) (clojure.string/join acc)
(= x \() (recur xs (conj acc x) (inc depth))
(= x \)) (recur xs (conj acc x) (dec depth))
(<= depth 0) (recur xs acc depth)
:else (recur xs (conj acc x) depth))))

Racket - define one character with token-char

I am working on a project for a class and we are tasked with writing a scanner for numbers, symbols, comments, arithmetic operators, parenthesis, and EOF in both Python and Racket. I am working on the racket version and I have written the following line to define one or more character as a symbol:
[(any-char) (token-CHAR (string->character lexeme))]
I have the following line to define on or more digits as a number:
[(:+ digit) (token-NUM (string->number lexeme))]
I am very new to Racket, this is my third program, so I am not exactly sure how to approach this, so any suggestions are greatly appreciated. I have scoured the Racket documentation, but I wasn't able to find what I was looking for.
Thanks!
Here is a minimal getting-started example - heavily commented.
#lang racket
;;; IMPORT
;; Import the lexer tools
(require parser-tools/yacc
parser-tools/lex
(prefix-in : parser-tools/lex-sre) ; names from lex-sre are prefixed with :
; to avoid name collisions
syntax/readerr)
;;; REGULAR EXPRESSIONS
;; Names for regular expressions matching letters and digits.
;; Note that :or are prefixed with a : due to (prefix-in : ...) above
(define-lex-abbrevs
[letter (:or (:/ "a" "z") (:/ #\A #\Z) )]
[digit (:/ #\0 #\9)])
;;; TOKENS
;; Tokens such as numbers (and identifiers and strings) carry a value
;; In the example only the NUMBER token is used, but you may need more.
(define-tokens value-tokens (NUMBER IDENTIFIER STRING))
;; Tokens that don't carry a value.
(define-empty-tokens op-tokens (newline := = < > + - * / ^ EOF))
;;; LEXER
;; Here the lexer (aka the scanner) is defined.
;; The construct lexer-src-pos evaluates to a function which scans an input port
;; returning one position-token at a time.
;; A position token contains besides the actual token also source location information
;; (i.e. you can see where in the file the token was read)
(define lex
(lexer-src-pos
[(eof) ; input: eof of file
'EOF] ; output: the symbol EOF
[(:or #\tab #\space #\newline) ; input: whitespace
(return-without-pos (lex input-port))] ; output: the next token
; (i.e. skip the whitespace)
[#\newline ; input: newline
(token-newline)] ; ouput: a newline-token
; ; note: (token-newline) returns 'newline
[(:or ":=" "+" "-" "*" "/" "^" "<" ">" "=") ; input: an operator
(string->symbol lexeme)] ; output: corresponding symbol
[(:+ digit) ; input: digits
(token-NUMBER (string->number lexeme))])) ; outout: a NUMBER token whose value is
; ; the number
; ; note: (token-value token)
; returns the number
;;; TEST
(define input (open-input-string "123+456"))
(lex input) ; (position-token (token 'NUMBER 123) (position 1 #f #f) (position 4 #f #f))
(lex input) ; (position-token '+ (position 4 #f #f) (position 5 #f #f))
(lex input) ; (position-token (token 'NUMBER 456) (position 5 #f #f) (position 8 #f #f))
(lex input) ; (position-token 'EOF (position 8 #f #f) (position 8 #f #f))
;; Let's make it a little easier to play with the lexer.
(define (string->tokens s)
(port->tokens (open-input-string s)))
(define (port->tokens in)
(define token (lex in))
(if (eq? (position-token-token token) 'EOF)
'()
(cons token (port->tokens in))))
(map position-token-token (string->tokens "123*45/3")) ; strip positions
; Output:
; (list (token 'NUMBER 123)
; '*
; (token 'NUMBER 45)
; '/
; (token 'NUMBER 3))

Pretty print expression with as few parentheses as possible?

My Question: What is the cleanest way to pretty print an expression without redundant parentheses?
I have the following representation of lambda expressions:
Term ::= Fun(String x, Term t)
| App(Term t1, Term t2)
| Var(String x)
By convention App is left associative, that is a b c is interpreted as (a b) c and function bodies stretch as far to the right as possible, that is, Ξ» x. x y is interpreted as Ξ» x. (x y).
I have a parser that does a good job, but now I want a pretty printer. Here's what I currently have (pseudo scala):
term match {
case Fun(v, t) => "(Ξ» %s.%s)".format(v, prettyPrint(t))
case App(s, t) => "(%s %s)".format(prettyPrint(s), prettyPrint(t))
case Var(v) => v
}
The above printer always puts ( ) around expressions (except for atomic variables). Thus for Fun(x, App(Fun(y, x), y)) it produces
(Ξ» x.((Ξ» y.x) y))
I would like to have
Ξ» x.(Ξ» y.x) y
Here I'll use a simple grammar for infix expressions with the associativity and precedence defined by the following grammar whose operators are listed in ascending order of precedence
E -> E + T | E - T | T left associative
T -> T * F | T / F | F left associative
F -> G ^ F | G right associative
G -> - G | ( E ) | NUM
Given an abstract syntax tree (AST) we convert the AST to a string with only the necessary parenthesis as described in the pseudocode below. We examine relative precedence and associativity as we recursively descend the tree to determine when parenthesis are necessary. Note that all decisions to wrap parentheses around an expression must be made in the parent node.
toParenString(AST) {
if (AST.type == NUM) // simple atomic type (no operator)
return toString(AST)
else if (AST.TYPE == UNARY_MINUS) // prefix unary operator
if (AST.arg.type != NUM AND
precedence(AST.op) > precedence(AST.arg.op))
return "-(" + toParenString(AST.arg) + ")"
else
return "-" + toParenString(AST.arg)
else { // binary operation
var useLeftParen =
AST.leftarg.type != NUM AND
(precedence(AST.op) > precedence(AST.leftarg.op) OR
(precedence(AST.op) == precedence(AST.leftarg.op) AND
isRightAssociative(AST.op)))
var useRightParen =
AST.rightarg.type != NUM AND
(precedence(AST.op) > precedence(AST.rightarg.op) OR
(precedence(AST.op) == precedence(AST.rightarg.op) AND
isLeftAssociative(AST.op)))
var leftString;
if (useLeftParen) {
leftString = "(" + toParenString(AST.leftarg) + ")"
else
leftString = toParenString(AST.leftarg)
var rightString;
if (useRightParen) {
rightString = "(" + toParenString(AST.rightarg) + ")"
else
rightString = toParenString(AST.rightarg)
return leftString + AST.op + rightString;
}
}
Isn't it that you just have to check the types of the arguments of App?
I'm not sure how to write this in scala..
term match {
case Fun(v: String, t: Term) => "Ξ» %s.%s".format(v, prettyPrint(t))
case App(s: Fun, t: App) => "(%s) (%s)".format(prettyPrint(s), prettyPrint(t))
case App(s: Term, t: App) => "%s (%s)".format(prettyPrint(s), prettyPrint(t))
case App(s: Fun, t: Term) => "(%s) %s".format(prettyPrint(s), prettyPrint(t))
case App(s: Term, t: Term) => "%s %s".format(prettyPrint(s), prettyPrint(t))
case Var(v: String) => v
}

Resources