#lang typed/racket
(require typed/rackunit)

; Type and Structure Definitions
(define-type ExprC (U NumC IdC AppC BinopC leq0?))
(struct NumC ([n : Real]) #:transparent)
(struct IdC ([s : Symbol]) #:transparent)
(struct AppC ([fname : Symbol] [arg : ExprC]) #:transparent)
(struct leq0? ([if : ExprC] [a : ExprC] [b : ExprC]) #:transparent)
(struct BinopC ([s : Symbol] [l : ExprC] [r : ExprC]) #:transparent)
(struct FunDefC ([fname : Symbol] [arg : Symbol] [body : ExprC]) #:transparent)
;ch-op?
;;Checks if the symbol passed is a valid mathematical operator
(define (ch-op? [s : Symbol]) : Boolean
  (cond
    [(member s '(+ - * /)) #t]
    [else #f]))

(check-equal? (ch-op? '+) #t)
(check-equal? (ch-op? 'true) #f)


;;ch-not-keyword?
;;Checks if the passed object is a keyword in the IKEU language
(define (ch-not-keyword? [s : Any]) : Boolean
  (cond
    [(not (symbol? s)) #t]
    [else (cond
            [(and  (not (ch-op? s)) (not (member s '(leq0? fundef)))) #t]
            [else #f])]))

(check-equal? (ch-not-keyword? 0) #t)
(check-equal? (ch-not-keyword? '+) #f)
(check-equal? (ch-not-keyword? 'True) #t)

;; ######### PARSERS ############

; Function that parses an s-expression (Sexp)and returns an ExprC represented as an AST
; parse: Sexp -> ExprC
(define (parse [expr : Sexp]) : ExprC
  (match expr
    [(? real? r) (NumC r)]
    [(? symbol? s) (match s
                     [(or '+ '- '* '/ 'leq0? 'fundef ': ) (error 'IKEU-parse "Illegal usage of operator: ~e" s)]
                     [else (IdC s)])] 
    [(list (? symbol? s) l r) (BinopC s (parse l) (parse r))]

 
    [(list 'leq0? if 'then a 'else b) (leq0? (parse if) (parse a) (parse b))]
    [(list (? symbol? id) exp) (AppC id (parse exp))]
    [else (error 'IKEU-parse "Wrong syntax. Expected a valid s-expression, given: ~e" expr)]))
 
; Testing Parse Function
(check-equal? (parse 'x) (IdC 'x)) 
(check-equal? (parse 3) (NumC 3)) 
(check-equal? (parse '{fundef 3}) (AppC 'fundef (NumC 3)))
(check-equal? (parse '{/ 5 {- -6 {* 4 1}}}) (BinopC '/ (NumC 5) (BinopC '- (NumC -6) (BinopC '* (NumC 4) (NumC 1)))))
(check-exn (regexp (regexp-quote "IKEU-parse: Wrong syntax. Expected a valid s-expression, given: \"String\""))
           (lambda () (parse "String"))) x
(check-exn (regexp (regexp-quote "IKEU-parse: Illegal usage of operator: '+"))
           (lambda () (parse '(+ + 2))))
(check-exn (regexp (regexp-quote "IKEU-parse: Illegal usage of operator: '-"))
           (lambda () (parse '(+ - 2))))
(check-exn (regexp (regexp-quote "IKEU-parse: Illegal usage of operator: '/"))
           (lambda () (parse '(+ / 2))))

(check-exn (regexp (regexp-quote "IKEU-parse: Illegal usage of operator: 'a"))
           (lambda () (parse '(a b c))))


; Helper function that checks if function name is valid given a symbol and returns Boolean
; validId?: Symbol -> Boolean
(define (validId? [s : Symbol]) : Boolean
   (match s
    [(or '+ '- '* '/ 'then 'else ': 'leq0? 'FunDefC) (error 'IKEU-validId "Invalid Function Name")]
    [else #t]))


; Function that parses a function definition and returns Sexp in FunDefC
; parse-fundef: Sexp -> FundefC
(define (parse-fundef [s : Sexp]) : FunDefC
(match s
    [(list 'fundef (? symbol? (? validId? fname)) (list(? symbol?(? validId? arg))) ': body)
     (FunDefC (cast fname Symbol) (cast arg Symbol) (parse body))]
    [else (error 'IKEU-parse-fundef "Incorrect function definition: ~e" s)]))

; Test Cases for parse-fundef
(check-equal? (parse-fundef '{fundef double {x} : {* 2 x}})
              (FunDefC 'double 'x (BinopC '* (NumC 2) (IdC 'x))))
(check-exn (regexp (regexp-quote "IKEU-parse-fundef: Incorrect function definition: '(parse '(+ * 2))"))
           (lambda () (parse-fundef '(parse '(+ * 2)))))
(check-exn (regexp (regexp-quote "IKEU-parse-fundef: Incorrect function definition: '(parse '())"))
           (lambda () (parse-fundef '(parse '()))))
(check-equal? (parse-fundef (quote (fundef f (x) : 6))) (FunDefC 'f 'x (NumC 6)))
(check-exn (regexp (regexp-quote "IKEU-validId: Invalid Function Name"))
           (lambda () (parse-fundef '(fundef + (x) : 13))))


; Function that  will parse an Sexp into a list of function definitions
; parse-prog: Sexp -> (Listof FundefC)
(define (parse-prog [s : Sexp]) : (Listof FunDefC)
  (match s
    [(cons f r) (cons (parse-fundef f) (parse-prog r))]
    ['() '()]
    [else (error 'IKEU-parse-prog "Incorrect function definition: ~e" s)]))

; Test Cases for parse-prog
(check-equal? (parse-prog '{{fundef triple {a} : {* 3 a}}     
                            {fundef main {init} : {triple 3}}})
              (list (FunDefC 'triple 'a (BinopC '* (NumC 3) (IdC 'a)))
                    (FunDefC 'main 'init (AppC 'triple (NumC 3)))))

(check-equal? (parse-prog '{{fundef f {x} : {+ x 14}} {fundef main {init} : {f 2}}})
              (list(FunDefC 'f 'x (BinopC '+ (IdC 'x) (NumC 14)))
                   (FunDefC 'main 'init (AppC 'f (NumC 2)))))
(check-exn (regexp (regexp-quote "IKEU-parse-prog: Incorrect function definition: 'x"))
           (lambda () (parse-prog 'x)))


;; ######### INTERPRETERS ############
; Function that substitutes what, for, and in and outputs to ExprC
; subst: ExprC, Symbol, ExprC -> ExprC
(define (subst [what : ExprC] [for : Symbol] [in : ExprC]) : ExprC
  (match in
    [(NumC n) in]
    [(IdC s) (cond
               [(symbol=? s for) what]
               [else in])]
    [(AppC f a) (AppC f (subst what for a))]
    [(BinopC op l r) (BinopC op (subst what for l) (subst what for r))]
    [(leq0? if a b) (leq0? (subst what for if) (subst what for a) (subst what for b))]))

; Test Cases for subst
(check-equal? (subst (NumC 5) 'x (BinopC '+ (IdC 'x) (NumC 1))) (BinopC '+ (NumC 5) (NumC 1)))
(check-equal? (subst (NumC 3) 'x (IdC 'x)) (NumC 3))
(check-equal? (subst (NumC 5) 'a (BinopC '/ (IdC 'a) (AppC 'f (BinopC '* (NumC 1) (BinopC '- (IdC 'b) (NumC 5))))))
              (BinopC '/ (NumC 5) (AppC 'f (BinopC '* (NumC 1) (BinopC '- (IdC 'b) (NumC 5))))))


; Function that takes in a symbol and finds a function within the list of FunDefC 
; get-fundef: Symbol (Listof FunDefC) -> FundefC
(define (get-fundef [n : Symbol] [funcs : (Listof FunDefC)]) : FunDefC
  (match funcs
    ['() (error 'IKEU-get-fundef "Function not defined: ~e" n)]
    [(cons f r) (cond
                  [(equal? n (FunDefC-fname f)) f]
                  [else (get-fundef n r)])]))

; Test Cases for get-fundef
(check-equal? (get-fundef 'f (list (FunDefC 'f 'x (NumC 7)))) (FunDefC 'f 'x (NumC 7)))
(check-equal? (get-fundef 'f (list (FunDefC 'a 'x (NumC 7))
                                   (FunDefC 'f 'x (BinopC '+ (NumC 3) (IdC 'x)))))
              (FunDefC 'f 'x (BinopC '+ (NumC 3) (IdC 'x))))
(check-exn (regexp (regexp-quote "IKEU-get-fundef: Function not defined: 'double"))
           (lambda () (get-fundef 'double (list (FunDefC 'a 'x (NumC 0))
                                                (FunDefC 'b 'x (BinopC '+ (NumC 1) (IdC 'x)))))))

; Function that evaluates binary operation given a symbol and two Real numbers
; get-binop: Symbol Real Real -> Real
(define (get-binop [op : Symbol] [a : Real] [b : Real]) : Real
  (match op
    ['+ (+ a b)]
    ['* (* a b)]
    ['- (- a b)]
    ['/ (cond
          [(zero? b) (error 'IKEU-get-binop "Division by 0")]
          [else (/ a b)])]
    [else (error 'IKEU-get-binop "Wrong Binop operator: ~e" op)]))

; Test cases for get-binop
(check-equal? (get-binop '+ 2 5) 7)
(check-equal? (get-binop '- 2 5) -3)
(check-equal? (get-binop '* 2 5) 10)
(check-equal? (get-binop '/ 2 5) 2/5)
(check-exn (regexp (regexp-quote "IKEU-get-binop: Division by 0"))
           (lambda () (get-binop '/ 2 0)))
(check-exn (regexp (regexp-quote "IKEU-get-binop: Wrong Binop operator: '?"))
           (lambda () (get-binop '? -3 4)))

; Interprets (evaluates) a IKEU expression into a real number
; interp: ExprC, (Listof FundefC) -> Real
(define (interp [exp : ExprC] [funs : (Listof FunDefC)]) : Real
  (match exp
    [(NumC n) n]
    [(leq0? if a b) (cond
                              [(<= (interp if funs) 0) (interp a funs)]
                              [else (interp b funs)])]
    [(BinopC op l r) #:when (and (equal? op '/) (zero? (interp r funs)))
                     (error 'interp "IKEU - Divide by zero")]
    [(BinopC op l r) (get-binop op (interp l funs) (interp r funs))]
    [(AppC fname arg) (define fdC (get-fundef fname funs))
                (interp (subst (NumC (interp arg funs))
                               (FunDefC-arg fdC)
                               (FunDefC-body fdC))
                        funs)]
    [else (error 'interp "IKEU - Invalid expression")]))

; Test Cases for interp
(check-equal? (interp (NumC 4) '()) 4)
(check-equal? (interp (BinopC '+ (BinopC '* (NumC 1) (NumC 2)) (BinopC '+ (NumC 2) (NumC 3))) '()) 7)
(check-equal? (interp (AppC 'double (NumC 21)) (list (FunDefC 'double 'x (BinopC '+ (IdC 'x) (IdC 'x))))) 42)
(check-equal? (interp (leq0? (BinopC '- (NumC 5) (NumC 5)) (NumC 42) (NumC 13)) '()) 42)
(check-equal? (interp (leq0? (BinopC '- (NumC 10) (NumC 5)) (NumC 42) (NumC 13)) '()) 13)
(check-exn (regexp (regexp-quote "IKEU - Invalid expression")) (lambda () (interp (IdC 'ohno) '())))
(check-exn (regexp (regexp-quote "IKEU - Divide by zero")) (lambda () (interp (BinopC '/ (NumC 1) (NumC 0)) '())))

; Interprets the function named 'main from the list of function definitions
; interp-fns: (Listof FundefC) -> Real
(define (interp-fns [funs : (Listof FunDefC)]) : Real
  (interp (AppC 'main (NumC 0)) funs))

; Test Cases for interp-fns
(check-equal? (interp-fns (list (FunDefC 'double 'x (BinopC '+ (IdC 'x) (IdC 'x)))
                                (FunDefC 'main 'x (AppC 'double (NumC 21))))) 42)

(check-equal? (interp-fns
               (parse-prog '{{fundef f {x} : {+ x 14}}
                             {fundef main {init} : {f 2}}}))
              16)

; Parses and interprets program
; Sexp -> Real
(: top-interp (Sexp -> Real))
(define (top-interp fun-sexps)
  (interp-fns (parse-prog fun-sexps)))

; Test Cases for top-interp
(check-equal? (top-interp '{{fundef main {init} : {+ 1 2}}})3)
(check-= (top-interp '(
                       (fundef abs (x) : (leq0? x then (- 0 x) else x))
                       (fundef get-decimal (x) : (leq0? x then (+ 1 x) else (get-decimal (- x 1))))
                       (fundef up-or-down (x) : (leq0? (- 0.5 x) then (- 1 x) else (- 0 x)))
                       (fundef round (x) : (+ x (up-or-down (get-decimal (abs x)))))
                       (fundef main (init) : (round 17.5))
                       )) 18 .001) 

Racket Online Compiler

Write, Run & Share Racket code online using OneCompiler's Racket online compiler for free. It's one of the robust, feature-rich online compilers for Racket language, running on the latest version 6.8. Getting started with the OneCompiler's Racket compiler is simple and pretty fast. The editor shows sample boilerplate code when you choose language as Racket and start coding.

Taking inputs (stdin)

OneCompiler's Racket online editor supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample Racket program which takes name as input and print your name with hello.

#lang racket/base
(define name (read))                
(printf "Hello ~a.\n" name)   

About Racket

Racket is a general-purpose programming language based on the Scheme dialect of Lisp. It is also used for scripting, computer science education, and research related applications.

Basics

ItemDecsription
;To comment a single line
;;to mark important comments
#;to comment the following s-expression

Data types

Data-typeDecsription
Numbersrepresents integers, float and complex numbers
Boolean#t and #f are the two boolean literals
StringsTo represent sequence of characters and double quotes("") are used to represent strings

Variables

let and define are used to declare variables

Syntax

(let ([id value-expression] ...) body ...+)

(let proc-id ([id init-expression] ...) body ...+)
define id expression

Example

> (let ([x 10]) x)
10

Loops and conditional statements

1. If family

If, If-else are used when you want to perform a certain set of operations based on conditional expressions.

If

(if cond-expr then-expr)

If-else

(if cond-expr then-expr else-expr)

2. For

For loop is used to iterate a set of statements based on a condition.

(for (for-clause ...) body-or-break ... body)
where 

for-clause = [id seq-expr] | [(id ...) seq-expr] | #:when guard-expr | #:unless guard-expr | break-clause
 	 	 	 	 
break-clause = #:break guard-expr | #:final guard-expr
 	 	 	 	 
body-or-break = body | break-clause

seq-expr : sequence?

Functions

A lambda expression is used to create a function.

(lambda (argument-id ...)
  body ...+)