Guest User

Untitled

a guest
Dec 10th, 2018
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scheme 13.48 KB | None | 0 0
  1. #|
  2. Tax Form Language Design
  3. Ross Foley
  4. |#
  5.  
  6. (require test-engine/racket-gui)
  7.  
  8. ;;;;;;;;;;;;;;;;;;;;;;;;; LANGUAGE - DATA DEFINITIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  9.  
  10. ;; a tax-form is (make-tax-form list[command])
  11. (define-struct tax-form (commands))
  12.  
  13. #|
  14. a command is either
  15.  - output, or
  16.  - display, or
  17.  - request-input, or
  18.  - multiple-choice, or
  19.  - yes-no, or
  20.  - request-list, or
  21.  - conditional, or
  22.  - compute-field, or
  23.  - add-fields, or
  24.  - tax-form, or
  25.  - 'none
  26. |#
  27.  
  28. ;; a conditional is (make-conditional list[symbol] (values... -> boolean) command command))
  29. #|
  30. a conditional passes the values of the specified fields into the specified function.
  31. If the function returns true, true-command is run, otherwise false-command is run.
  32. |#
  33. (define-struct conditional (fields run? true-command false-command))
  34.  
  35. ;; an output is (make-output symbol string)
  36. #|
  37. an output is used to display the value of a field.
  38. The value of the field will be inserted into the string
  39. using the format function.
  40. |#
  41. (define-struct output (field string))
  42.  
  43. ;; a display is (make-display string)
  44. #|
  45. a display outputs the specified text onto the screen.
  46. |#
  47. (define-struct display (text))
  48.  
  49. ;; a request-input is (make-request-input string symbol)
  50. #|
  51. a request-input displays the text prompt, reads user input, and stores the result in the specified field
  52. |#
  53. (define-struct request-input (text field))
  54.  
  55. ;; a multiple-choice is (make-multiple-choice string list[string] symbol)
  56. #|
  57. a multiple-choice displays the text prompt and choices, reads user input, and stores the result in the specified field
  58. |#
  59. (define-struct multiple-choice (text choices field))
  60.  
  61. ;; a yes-no is (make-yes-no symbol)
  62. #|
  63. a yes-no displays the text prompt, reads user input, and stores the result in the specified field
  64. ("yes" will be stored as true and "no" will be stored as false)
  65. |#
  66. (define-struct yes-no (text field))
  67.  
  68. ;; a request-list is (make-request-list string list[string] symbol)
  69. #|
  70. a request-list displays the list of prompts and records
  71. the answers in a list.  The prompts are repeated until the
  72. user specifies to stop.
  73. |#
  74. (define-struct request-list (name questions field))
  75.  
  76. ;; a compute-field is (make-compute-field list[symbol] (values... -> value) symbol)
  77. #|
  78. a compute-field will pass the values of the needed-fields into the function
  79. and store the result into the specified field.
  80. |#
  81. (define-struct compute-field (needed-fields func field))
  82.  
  83.  
  84. ;;;;;;;;;;;;;;;;;;;;;;;;; LANGUAGE - MACROS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  85.  
  86. ;; macro to wrap list around commands for tax-forms
  87. (define-syntax tax-form
  88.   (syntax-rules ()
  89.     [(tax-form cmd1 ...)
  90.      (make-tax-form (list cmd1 ...))]))
  91.  
  92. ;; macro for outputing data to the screen (with or without field data)
  93. (define-syntax output
  94.   (syntax-rules ()
  95.     [(output text)
  96.      (make-display text)]
  97.     [(output text field)
  98.      (make-output 'field text)]))
  99.  
  100. ;; macro for conditional statements (with or without false-command)
  101. (define-syntax conditional
  102.   (syntax-rules ()
  103.     [(conditional field1 ... func true-cmd)
  104.      (make-conditional (list 'field1 ...) func true-cmd 'none)]
  105.     [(conditional field1 ... func true-cmd false-cmd)
  106.      (make-conditional (list 'field1 ...) func true-cmd false-cmd)]))
  107.  
  108. ;; macro for simplifying compute-field syntax
  109. (define-syntax compute
  110.   (syntax-rules (->)
  111.     [(compute (field1 ...) -> func field)
  112.      (make-compute-field (list 'field1 ...) func 'field)]))
  113.  
  114. ;; macro for easy way to add fields without using compute
  115. (define-syntax add
  116.   (syntax-rules (->)
  117.     [(add (field1 ...) -> result)
  118.      (make-compute-field (list 'field1 ...) + 'result)]))
  119.  
  120. ;; macro for simplifying multiple-choice syntax
  121. (define-syntax multiple-choice
  122.   (syntax-rules (:)
  123.     [(multiple-choice text : (q1 ...) field)
  124.      (make-multiple-choice text (list q1 ...) 'field)]))
  125.  
  126. ;; macro for simplifying yes-no syntax
  127. (define-syntax yes-no
  128.   (syntax-rules ()
  129.     [(yes-no text field)
  130.      (make-yes-no text 'field)]))
  131.  
  132. ;; macro for simplifying input requests (both for normal input and lists of input)
  133. (define-syntax request
  134.   (syntax-rules (:)
  135.     [(request name : (q1 ...) field)
  136.      (make-request-list name (list q1 ...) 'field)]
  137.     [(request text field)
  138.      (make-request-input text 'field)]))
  139.  
  140.  
  141. ;;;;;;;;;;;;;;;;;;;;;;;;; PROGRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  142.  
  143. #|
  144. Schedule B Part I
  145. |#
  146. (define schedule-b-i
  147.   (tax-form
  148.    (request "payer" : ("Payer" "Amount") lineb1)
  149.    (compute (lineb1) -> (lambda (b1)
  150.                           (apply + (map (lambda (x) (list-ref x 1)) b1)))
  151.             lineb4)
  152.    (output "Total Taxable Interest: ~a" lineb4)))
  153.  
  154. #|
  155. Schedule B Part II
  156. |#
  157. (define schedule-b-ii
  158.   (tax-form
  159.    (request "payer" : ("Payer" "Amount") lineb5)
  160.    (compute (lineb5) -> (lambda (b5)
  161.                           (apply + (map (lambda (x) (list-ref x 1)) b5)))
  162.             lineb6)
  163.    (output "Total Ordinary Dividends: ~a" lineb6)))
  164.  
  165. #|
  166. Form 1040
  167. |#
  168. (define form1040
  169.   (tax-form
  170.    ;; Personal Information
  171.    (request "First name" first-name)
  172.    (request "Middle initial" middle-initial)
  173.    (request "Last name" last-name)
  174.    (request "Social security number" ssn)
  175.    (request "Home address (number and street)" address)
  176.    (yes-no "Do you want $3 going to the Presidential Election Campaign fund?" fund)
  177.    
  178.    ;; Filing Status
  179.    (multiple-choice "Filing Status" : ("Single" "Head of household" "Qualifying widow(er)") filing-status)
  180.    
  181.    ;; Exemptions
  182.    (yes-no "Are you an exemption?" line6a)
  183.    (yes-no "Is your spouse an exemption?" line6b)
  184.    (request "dependent" :
  185.             ("First name" "Last name" "Dependent's social security number" "Dependent's relationship to you")
  186.             line6c)
  187.    (compute (line6a line6b line6c) -> (lambda (a b c)
  188.                                         (let ([ex1 (if a 1 0)]
  189.                                               [ex2 (if b 1 0)]
  190.                                               [ex3 (length c)])
  191.                                           (+ ex1 ex2 ex3)))
  192.             line6d)
  193.    
  194.    ;; Income
  195.    (request "Wages, salaries, tips, etc." line7)
  196.    (request "Taxable interest" line8a)
  197.    (conditional line8a (lambda (line8a) (> line8a 400)) schedule-b-i)
  198.    (request "Ordinary dividends" line9a)
  199.    (conditional line9a (lambda (line9a) (> line9a 400)) schedule-b-ii)
  200.    (request "Qualified dividends" line9b)
  201.    (request "IRA distributions" line15a)
  202.    (request "Pensions and annuities" line16a)
  203.    (request "Social security benefits" line20a)
  204.    (request "Other income type" line21type)
  205.    (request "Other income amount" line21amount)
  206.    (add (line7 line8a line9a line9b line15a line16a line20a line21amount) -> line22)
  207.    (output "Total Income: ~a" line22)))
  208.  
  209. #|
  210. Schedule A
  211. |#
  212. (define schedule-a
  213.   (tax-form
  214.    ;; Medical and Dental Expenses
  215.    (request "Medical and dental expenses" linea1)
  216.    (compute (linea1 line38) -> (lambda (a1 line38)
  217.                                  (if (> (* line38 0.075) a1)
  218.                                      0
  219.                                      (- a1 (* line38 0.075))))
  220.             linea4)
  221.    (output "Total Medical and Dental Expenses: ~a" linea4)
  222.    
  223.    ;; Taxes You Paid
  224.    (request "State and local income taxes" linea5)
  225.    (request "Real estate taxes" linea6)
  226.    (request "Personal property taxes" linea7)
  227.    (add (linea5 linea6 linea7) -> linea9)
  228.    (output "Total Taxes You Paid: ~a" linea9)
  229.    
  230.    ;; Interest You Paid
  231.    (request "Home mortgage interest and points reported to you on Form 1098" linea10)
  232.    (request "Investment interest" linea13)
  233.    (add (linea10 linea13) -> linea14)
  234.    (output "Total Interest You Paid: ~a" linea14)
  235.    
  236.    ;; Gifts to Charity
  237.    (request "Gifts by cash or check" linea15)
  238.    (request "Other than by cash or check" linea16)
  239.    (request "Carryover from prior year" linea17)
  240.    (add (linea15 linea16 linea17) -> linea18)
  241.    (output "Total Gifts to Charity: ~a" linea18)))
  242.  
  243.  
  244. ;;;;;;;;;;;;;;;;;;;;;;;;;;;; INTERPRETER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  245.  
  246. #|
  247. Variable Functions
  248. |#
  249. ;; A var is (make-var symbol number)
  250. (define-struct var (name val))
  251.  
  252. ;; vars is a list[var]
  253. (define vars empty)
  254.  
  255. ;; lookup-var : symbol -> number
  256. ;; produces number associated with given variable name
  257. (define (lookup-var varname)
  258.   (let ([varstructs (filter (lambda (avar) (symbol=? varname (var-name avar))) vars)])
  259.     (cond [(cons? varstructs) (var-val (first varstructs))]
  260.           [else (error 'lookup-var (format "variable ~a not defined" varname))])))
  261.  
  262. ;; update-var : symbol number -> void
  263. ;; change value of named var to given number
  264. ;; if the specified var doesn't exist, it will be created
  265. (define (update-var varname newval)
  266.   (cond [(empty? (filter (lambda (x) (symbol=? varname (var-name x))) vars))
  267.          (set! vars (cons (make-var varname newval) vars))]
  268.         [else (set! vars
  269.                     (map (lambda (avar)
  270.                            (cond [(symbol=? varname (var-name avar)) (make-var varname newval)]
  271.                                  [else avar]))
  272.                          vars))]))
  273.  
  274. ;--------------------------------------------------------------
  275.  
  276. ;; run-tax-form: tax-form -> void
  277. ;; executes the commands in the specified tax-form
  278. (define (run-tax-form a-tax-form)
  279.   (begin
  280.     (run-command-list (tax-form-commands a-tax-form))))
  281. ;;(set! vars empty)))
  282.  
  283. ;; run-command-list: list[command] -> void
  284. ;; executes all commands in the specified list
  285. (define (run-command-list command-list)
  286.   (for-each run-command command-list))
  287.  
  288. ;; run-command: command -> void
  289. ;; executes the specified command
  290. (define (run-command cmd)
  291.   (cond [(output? cmd)
  292.          (print-string (format (output-string cmd) (lookup-var (output-field cmd))))]
  293.        
  294.         [(display? cmd) (print-string (display-text cmd))]
  295.        
  296.         [(request-input? cmd)
  297.          (get-user-input (format "~a: " (request-input-text cmd))
  298.                          (request-input-field cmd))]
  299.        
  300.         [(multiple-choice? cmd)
  301.          (get-user-input (format "~a:~n~a"
  302.                                  (multiple-choice-text cmd)
  303.                                  (create-choices (multiple-choice-choices cmd) 1))
  304.                          (multiple-choice-field cmd))]
  305.        
  306.         [(yes-no? cmd)
  307.          (begin
  308.            (get-user-input (format "~a (enter yes or no):" (yes-no-text cmd))
  309.                            (yes-no-field cmd))
  310.            (convert-yes-no (yes-no-field cmd)))]
  311.        
  312.         [(request-list? cmd)
  313.          (begin
  314.            (update-var (request-list-field cmd) empty)
  315.            (handle-request-list (request-list-name cmd)
  316.                                 (request-list-questions cmd)
  317.                                 (request-list-field cmd)))]
  318.        
  319.         [(conditional? cmd)
  320.          (cond [(apply (conditional-run? cmd) (map lookup-var (conditional-fields cmd)))
  321.                 (run-command (conditional-true-command cmd))]
  322.                [else (run-command (conditional-false-command cmd))])]
  323.        
  324.         [(compute-field? cmd)
  325.          (update-var (compute-field-field cmd)
  326.                      (apply (compute-field-func cmd) (map lookup-var (compute-field-needed-fields cmd))))]
  327.        
  328.         [(tax-form? cmd) (run-command-list (tax-form-commands cmd))]
  329.        
  330.         [(symbol=? cmd 'none) (void)]))
  331.  
  332. ;; get-user-input: string symbol -> void
  333. ;; prompts the user for input and stores the result in the specified field
  334. (define (get-user-input prompt field)
  335.   (begin
  336.     (printf prompt)
  337.     (let ([user-input (read)])
  338.       (update-var field user-input))))
  339.  
  340. ;; create-choices: list[string] number -> string
  341. ;; generates a string with a numbered list of choices
  342. (check-expect (create-choices empty 1) "")
  343. (check-expect (create-choices '("hi") 1) "1. hi\n")
  344. (check-expect (create-choices '("a" "b" "c") 2) "2. a\n3. b\n4. c\n")
  345.  
  346. (define (create-choices choices label)
  347.   (cond [(empty? choices) ""]
  348.         [(cons? choices)
  349.          (string-append (format "~a. ~a~n" label (first choices))
  350.                         (create-choices (rest choices) (+ 1 label)))]))
  351.  
  352. ;; handle-request-list: string list[string] symbol -> void
  353. ;; repeats a series of questions to the user until the user
  354. ;; specifies that they are done
  355. (define (handle-request-list name questions field)
  356.   (begin
  357.     (let ([another (symbol->string (ask-question (format "Do you have another ~a? (enter yes or no)" name)))])
  358.       (cond [(string=? "yes" another)
  359.              (begin
  360.                (update-var field (cons (map ask-question questions) (lookup-var field)))
  361.                (handle-request-list name questions field))]
  362.             [else (void)]))))
  363.  
  364. ;; ask-question: string -> value
  365. ;; prompts the user for the answer to the specified question
  366. ;; returns the answer to the question
  367. ;; since this relies on user input, it can't be tested with check-expect
  368. (define (ask-question question)
  369.   (begin
  370.     (printf "~a: " question)
  371.     (read)))
  372.  
  373. ;; convert-yes-no: symbol -> void
  374. ;; converts the value of the specified field from
  375. ;; "yes" or "no" to "true" or "false"
  376. (define (convert-yes-no field)
  377.   (let ([ans (lookup-var field)])
  378.     (cond [(symbol=? ans 'yes) (update-var field true)]
  379.           [(symbol=? ans 'no) (update-var field false)])))
  380.  
  381.  
  382. ;-------------------------------------------------------------
  383.  
  384. ;; print-string : string -> void
  385. ;; prints string and a newline to the screen
  386. (define (print-string str)
  387.   (printf "~a~n" str))
  388.  
  389. ;; print-newline : -> void
  390. ;; prints a newline on the screen
  391. (define (print-newline) (printf "~n"))
  392.  
  393. (test)
Add Comment
Please, Sign In to add comment