PasteRack.org
Paste # 44863
2025-02-13 23:15:13

Fork as a new paste.

Paste viewed 196 times.


Embed:

indent/dedent

  1. #lang racket/base
  2.  
  3. (require parser-tools/lex
  4.          (prefix-in : parser-tools/lex-sre))
  5.  
  6. (struct newline-indent
  7.   (count)
  8.   #:transparent)
  9. (struct newline-dedent
  10.   (count)
  11.   #:transparent)
  12.  
  13. ; set a port back one char:
  14. (define (unget port)
  15.   (file-position port (- (file-position port) 1)))
  16.  
  17. ; count indents:
  18. (define indent-count 0)
  19.  
  20. (define (inc-indent!)
  21.   (set! indent-count (+ indent-count  1)))
  22.  
  23. (define (reset-indents!)
  24.   (set! indent-count 0))
  25.  
  26. (define current-indent 0)
  27.  
  28. ; a lexer for measuring indentation:
  29. (define indent-lex
  30.   (lexer
  31.    ; end of file:
  32.    [(eof)      '(eof)]
  33.  
  34.    ; indent
  35.    ["    " (begin
  36.              (inc-indent!)
  37.              (indent-lex input-port))]
  38.  
  39.    [#\space 'bad-indent]
  40.  
  41.    ; return to normal lexing:
  42.    [any-char   (begin
  43.                  (unget input-port)
  44.                  (let ([count indent-count])
  45.                    (reset-indents!)
  46.                    (if (eq? count current-indent)
  47.                        (lex input-port)
  48.                        (begin
  49.                          (let ([dir (> count current-indent)])
  50.                            (set! current-indent count)
  51.                            (cons (if dir (newline-indent count) (newline-dedent count))
  52.                                  (lex input-port))
  53.                            )))
  54.                    ))]))
  55.  
  56.  
  57.  
  58. (define-lex-abbrevs
  59.   (lower-letter (:/ "a" "z"))
  60.   (upper-letter (:/ #\A #\Z))
  61.   (letter (:or lower-letter upper-letter ))
  62.  
  63.   (digit (:/ "0" "9"))
  64.   )
  65.  
  66. ; a lexer for printing characters:
  67. (define lex
  68.   (lexer
  69.    ; end:
  70.    [(eof)       '(eof)]
  71.  
  72.    ; skip newline:
  73.    [#\newline  (indent-lex input-port)]
  74.  
  75.    ; an actual character:
  76.    ;;[any-char    (cons lexeme (lex input-port))]
  77.  
  78.    [(:: letter
  79.         (:* letter digit #\_ #\-))
  80.     (cons (list  'identifier (string->symbol lexeme))
  81.           (lex input-port))]
  82.    [(:+ digit)
  83.     (cons (list 'number (string->number lexeme))
  84.           (lex input-port))]
  85.    ))
  86.  
  87.  
  88. (define in (open-input-string "foo
  89.  
  90.     bar
  91.     baz
  92.         asdf
  93.         zxcv
  94.     bat
  95. "))
  96.  
  97. (indent-lex in)
  98.  
  99. ;; prints out:
  100. ;;
  101. ;; (list
  102. ;;  '(identifier foo)
  103. ;;  (newline-indent 1)
  104. ;;  '(identifier bar)
  105. ;;  '(identifier baz)
  106. ;;  (newline-indent 2)
  107. ;;  '(identifier asdf)
  108. ;;  '(identifier zxcv)
  109. ;;  (newline-dedent 1)
  110. ;;  '(identifier bat)
  111. ;;  'eof)

=>

lexer: No match found in input starting with: