Last active
March 17, 2025 00:46
-
-
Save moea/cce2b742d3b2920cda4874fbe9b6934c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns assistant.reader | |
(:import [java.io PushbackReader StringReader])) | |
(defprotocol Reader | |
(read-char [this]) | |
(peek-char [this]) | |
(unread-char [this ch]) | |
(eof? [this])) | |
(extend-type PushbackReader | |
Reader | |
(read-char [rdr] | |
(let [ch (.read rdr)] | |
(when-not (neg? ch) | |
(char ch)))) | |
(peek-char [rdr] | |
(let [ch (.read rdr)] | |
(when-not (neg? ch) | |
(.unread rdr ch) | |
(char ch)))) | |
(unread-char [rdr ch] (.unread rdr (int ch))) | |
(eof? [rdr] (nil? (peek-char rdr)))) | |
;; Skip whitespace | |
(defn skip-whitespace [reader] | |
(loop [] | |
(let [ch (peek-char reader)] | |
(when (and ch (Character/isWhitespace ch)) | |
(read-char reader) | |
(recur))))) | |
;; Parsing functions | |
(declare parse) | |
(defn parse-int [reader first-digit] | |
(let [sb (StringBuilder.)] | |
(.append sb first-digit) | |
(loop [] | |
(let [ch (peek-char reader)] | |
(if (and ch (Character/isDigit ch)) | |
(do | |
(.append sb (read-char reader)) | |
(recur)) | |
(Integer/parseInt (.toString sb))))))) | |
(defn parse-string [reader] | |
(let [sb (StringBuilder.)] | |
(loop [] | |
(let [ch (read-char reader)] | |
(cond | |
(nil? ch) (throw (ex-info "Unexpected EOF in string" {})) | |
(= ch \\) (do (.append sb (read-char reader)) (recur)) | |
(= ch \" ) (.toString sb) | |
:else (do (.append sb ch) | |
(recur))))))) | |
(defn parse-symbol [reader first-char] | |
(let [sb (StringBuilder.)] | |
(.append sb first-char) | |
(loop [] | |
(let [ch (peek-char reader)] | |
(if (and ch (not (Character/isWhitespace ch)) (not (#{\( \) \[ \] \"} ch))) | |
(do (.append sb (read-char reader)) | |
(recur)) | |
(symbol (.toString sb))))))) | |
(defn parse-vector [reader] | |
(loop [elems []] | |
(skip-whitespace reader) | |
(let [ch (peek-char reader)] | |
(cond | |
(nil? ch) (throw (ex-info "Unexpected EOF in vector" {})) | |
(= ch \]) (do (read-char reader) (vec elems)) | |
:else (recur (conj elems (parse reader))))))) | |
(defn parse-list [reader] | |
(loop [elems []] | |
(skip-whitespace reader) | |
(let [ch (peek-char reader)] | |
(cond | |
(nil? ch) (throw (ex-info "Unexpected EOF in list" {})) | |
(= ch \)) (do (read-char reader) (apply list elems)) | |
:else (recur (conj elems (parse reader))))))) | |
(defn parse [reader] | |
(skip-whitespace reader) | |
(let [ch (read-char reader)] | |
(cond | |
(nil? ch) nil | |
(Character/isDigit ch) (parse-int reader ch) | |
(= ch \" ) (parse-string reader) | |
(= ch \[) (parse-vector reader) | |
(= ch \() (parse-list reader) | |
:else (parse-symbol reader ch)))) | |
(defn read-sexp [^PushbackReader rdr] | |
(parse rdr)) | |
(comment | |
(with-open [rdr (PushbackReader. (StringReader. "(hello [\"hello\" 123 [456]])"))] | |
(read-sexp rdr))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment