{"body":"#lang racket\n\n;; =========================================================\n;; AST DEFINITIONS\n;; =========================================================\n\n(struct invoice (id date client items tax) #:transparent)\n(struct item (desc hours rate) #:transparent)\n\n;; =========================================================\n;; SIMPLE INDENTATION PARSER\n;; Converts DSL text → nested S-expressions\n;; =========================================================\n\n(define (indent-level line)\n  (length (regexp-match* #px\"^ *\" line)))\n\n(define (tokenize line)\n  (map string->symbol\n       (regexp-split #px\"\\\\s+\" (string-trim line))))\n\n(define (parse-dsl text)\n  (define lines\n    (filter (λ (l) (not (regexp-match? #px\"^\\\\s*$\" l)))\n            (string-split text \"\\n\")))\n\n  (define stack '())   ;; (indent . node)\n  (define root '())\n\n  (for ([line lines])\n    (define ind (indent-level line))\n    (define node (tokenize line))\n\n    ;; pop until correct parent\n    (set! stack\n          (let loop ([st stack])\n            (cond\n              [(empty? st) st]\n              [(<= ind (caar st)) (loop (cdr st))]\n              [else st])))\n\n    (cond\n      [(empty? stack)\n       (set! root (cons node root))]\n\n      [else\n       (define parent (cdar stack))\n       (set-cdr! (car stack)\n                 (append parent (list node)))])\n\n    (set! stack (cons (cons ind node) stack)))\n\n  (reverse root))\n\n;; =========================================================\n;; DSL → AST\n;; =========================================================\n\n(define (parse-invoice expr)\n  (match expr\n    [(list 'invoice id clauses ...)\n     (define date #f)\n     (define client #f)\n     (define items '())\n     (define tax 0.0)\n\n     (for ([c clauses])\n       (match c\n         [(list 'date d)\n          (set! date d)]\n\n         [(list 'client cl)\n          (set! client cl)]\n\n         [(list 'item desc 'hours h 'rate r)\n          (set! items\n                (cons (item desc\n                             (string->number (symbol->string h))\n                             (string->number (symbol->string r)))\n                      items))]\n\n         [(list 'tax t)\n          (define s (symbol->string t))\n          (define num (string->number (regexp-replace #px\"%\" s \"\")))\n          (set! tax (/ num 100.0))]))\n\n     (invoice id date client (reverse items) tax)]))\n\n;; =========================================================\n;; EVALUATION\n;; =========================================================\n\n(define (subtotal inv)\n  (for/sum ([i (invoice-items inv)])\n    (* (item-hours i)\n       (item-rate i))))\n\n(define (total inv)\n  (define sub (subtotal inv))\n  (+ sub (* sub (invoice-tax inv))))\n\n;; =========================================================\n;; RUNNER\n;; =========================================================\n\n(define (run text)\n  (define parsed (parse-dsl text))\n  (define inv (parse-invoice (car parsed)))\n  (values inv (total inv)))\n\n;; =========================================================\n;; EXAMPLE DSL INPUT\n;; =========================================================\n\n(define input\n\"invoice INV-001\n  date 2026-04-29\n  client AcmeCorp\n  item WebsiteRedesign hours 10 rate 150\n  item HostingSetup hours 2 rate 100\n  tax 8%\")\n\n;; =========================================================\n;; EXECUTION\n;; =========================================================\n\n(define-values (inv total-cost)\n  (run input))\n\n(displayln inv)\n(displayln total-cost)","name":"","extension":"txt","url":"https://www.irccloud.com/pastebin/IY0Z8pcR","modified":1782465396,"id":"IY0Z8pcR","size":3459,"lines":131,"own_paste":false,"theme":"","date":1782465396}