(cl:defpackage #:quiz2
(:use #:cl))
(cl:in-package #:quiz2)
(defvar *stack* nil
"Stack for parsed objects")
(defun tab-ws (pos)
#-dont-blame-tabspace(warn "Tabular is found at pos ~S" pos)
(* 8 (floor (+ pos 8) 8)))
(defun count-ws (input-stream)
(loop :for cnt := 0 :then (case char
((#\Space)
(1+ cnt))
((#\Tab)
(tab-ws cnt))
((#\Return #\Linefeed)
(return-from count-ws
(count-ws input-stream)))
(otherwise
(unread-char char input-stream)
(return-from count-ws
cnt)))
:for char := (read-char input-stream)))
(defun combine-lines (first rest)
(cons first (nreverse rest)))
(defun reduce-forms (offset)
(let ((tail (cdr (pop *stack*))))
(if (> offset (car (first *stack*)))
(push (list offset (nreverse tail))
*stack*)
(let ((first (pop (cdr (first *stack*)))))
(progn (push (combine-lines first tail)
(cdr (first *stack*)))
*stack*)))))
(defun read-the-line (input-stream)
(let ((space-offset (count-ws input-stream))
(data (read-preserving-whitespace input-stream)))
(loop :while (and (rest *stack*)
(< space-offset (car (first *stack*))))
:do (reduce-forms space-offset))
(cond
((and (symbolp data)
(string= (symbol-name data) "!#"))
(throw 'thats-all-folks
(cons 'progn (loop :while (rest *stack*)
:do (reduce-forms 0)
:finally
(return (nreverse (cdr (first *stack*))))))))
((and *stack*
(= space-offset (car (first *stack*))))
(push data (cdr (first *stack*)))
*stack*)
(t
(push (cons space-offset (list data))
*stack*)))))
(defun read-off-side (stream subchar parameter)
(declare (ignore subchar parameter))
(let ((*stack* nil))
(catch 'thats-all-folks
(loop
(read-the-line stream)))))