sexpr/src/sexpr.cr

76 lines
1.4 KiB
Crystal

# Based on fast-sexpr (https://www.npmjs.com/package/fast-sexpr)
module Sexpr
extend self
VERSION = {{ `shards version`.stringify.chomp }}
alias Node = String | Body
alias Body = Array(String) | Array(Node)
class Parser
private property i = 0
private property source : String
module Terms
STRING = /"/
SPACE = /\s/
LIST = /(#{SPACE})|[()]/
end
def initialize(@source : String)
end
private def get_char : Char?
if @i == @source.size
return nil
end
char = @source[@i]
@i += 1
char
end
private def read_value(is_terminator : Regex) : String
value = ""
while !is_terminator.matches?(str_char = get_char.to_s)
value += str_char
end
value
end
private def un_get_char : Nil
@i -= 1
end
def parse : Body
body = [] of Node
while char = get_char
if char == ')'
break
elsif char == '('
body << parse
elsif char == '"'
body << read_value(Terms::STRING)
elsif !Terms::SPACE.matches?(char.to_s)
un_get_char
body << read_value(Terms::LIST)
un_get_char
end
end
body
end
end
def remove_comments(source : String) : String
source.gsub(/;;?.+/, nil)
end
def parse(source : String) : Body
Parser.new(remove_comments(source)).parse
end
end