Property definitions

csv $ CsvReader :: defaultinit
# Reads records from a CSV file.
#
# By default, the format recognizes EOLs as `\n`
#
# ~~~nit
# var example = """
# foo,bar
# "Hello, word!",1234.5 + 42
# "Something
# ""else""\", baz
# """
# var reader = new CsvReader.from_string(example)
# var table = reader.read_all
#
# assert table.header  == ["foo","bar"]
# assert table.records == [["Hello, word!","1234.5 + 42"],
# 			["Something\n\"else\""," baz"]]
# ~~~
class CsvReader
	super CsvStream

	# The input stream.
	var istream: Reader

	# Do we skip the empty lines?
	#
	# Note: Even if this attribute is `false`, the presence of an line ending at
	# end of the last record does not change the number of returned record.
	# This is because the line endings are processed as terminators, not as
	# separators. Therefore, when there is more than one line ending at the end
	# of the file, the additional lines are interpreted as empty records that
	# are skipped only if `skip_empty` is set to `true`.
	#
	# `false` by default.
	var skip_empty: Bool = false is writable

	# Creates a new CSVReader from a `string` data
	init from_string(s: String) do init(new StringReader(s))

	# Reads the content of the Stream and interprets it as a CSV Document
	#
	# Optional parameter `has_header` determines whether the first line
	# of the CSV Document is header data.
	# Defaults to true
	fun read_all(has_header: nullable Bool): CsvDocument do
		var header: nullable Array[String] = null
		if has_header == null then has_header = true
		var iss = istream
		var res_data = new Array[Array[String]]
		var eol_st = eol.first
		var line = new Array[String]
		var esc = delimiter
		var sep = separator
		var eol = eol
		var is_eol = false
		var eol_buf = new Buffer.with_cap(eol.length)
		var c = iss.read_char
		var el = new Buffer
		while not iss.eof do
			if c == null then continue
			loop
				if c == esc then
					c = iss.read_char
					loop
						if c == esc then
							c = iss.read_char
							if c != esc then break
						end
						if c == null then break
						el.add c
						c = iss.read_char
					end
				end
				if c == sep then break
				if c == eol_st then
					eol_buf.add c.as(not null)
					is_eol = true
					for i in [1 .. eol.length[ do
						c = iss.read_char
						if c == null or c != eol[i] then
							is_eol = false
							el.append(eol_buf)
							eol_buf.clear
							break
						end
						eol_buf.add c
					end
					if not is_eol then continue
					eol_buf.clear
					break
				end
				if c == sep then break
				el.add c.as(not null)
				c = iss.read_char
				if c == null then break
			end
			line.add el.to_s
			el.clear
			if is_eol or iss.eof then
				c = iss.read_char
				is_eol = false
				if skip_empty and line.is_empty then
					continue
				end
				if has_header and header == null then
					header = line
				else res_data.add line
				line = new Array[String]
			end
			if c == sep then c = iss.read_char
		end
		if header == null then header = new Array[String]
		var doc = new CsvDocument
		doc.header = header
		doc.records = res_data
		return doc
	end
end
lib/csv/csv.nit:225,1--342,3