-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #885 from candy-lang/csv
CSV Package
- Loading branch information
Showing
15 changed files
with
269 additions
and
6 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,11 @@ jobs: | |
- uses: technote-space/[email protected] | ||
|
||
automerge-pr: | ||
if: (github.event.action == 'opened' && !github.event.pull_request.draft) || github.event.action == 'ready_for_review' | ||
if: > | ||
github.head_ref == 'refs/heads/main' | ||
&& ( | ||
(github.event.action == 'opened' && !github.event.pull_request.draft) | ||
|| github.event.action == 'ready_for_review') | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
|
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[decode] := use ".decode" | ||
[encode] := use ".encode" |
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
[bool, checkEquals, equals, ifElse, int, list, recursive, text] = use "Core" | ||
[cursor, parser] = use "Parser" | ||
|
||
decode csv := | ||
needs (text.is csv) | ||
# TODO(JonasWanke): Error on lines with different field counts | ||
recursive [Lines: (,), Fields: (,), FieldStartOffset: 0, P: parser.new csv] { | ||
recurse [lines, fields, fieldStartOffset, p] -> | ||
Parser (Cursor [source, Offset: oldOffset]) = p | ||
p | parser.next % | ||
Ok [Parser: p, character] -> | ||
Parser c = p | ||
Cursor [Offset: newOffset] = c | ||
character % | ||
"," -> | ||
recurse [ | ||
lines, | ||
Fields: fields | list.append (source | text.getRange fieldStartOffset oldOffset), | ||
FieldStartOffset: newOffset, | ||
p, | ||
] | ||
" | ||
|
||
|
||
" -> | ||
fields = fields | list.append (source | text.getRange fieldStartOffset oldOffset) | ||
lines = lines | list.append fields | ||
# CSV files can have a trailing newline. | ||
ifElse | ||
(c | cursor.isAtEnd) | ||
{ | ||
ifElse (oldOffset | equals 0) { Ok (,) } { Ok lines } | ||
} | ||
{ | ||
recurse [lines, Fields: (,), FieldStartOffset: newOffset, p] | ||
} | ||
# TODO(JonasWanke): handle quoted field | ||
_ -> recurse [lines, fields, fieldStartOffset, p] | ||
Error Empty -> | ||
finalField = source | text.getRange fieldStartOffset oldOffset | ||
Ok | ||
ifElse | ||
finalField | text.isEmpty | bool.lazyAnd { fields | list.isEmpty } | ||
{ lines } | ||
{ lines | list.append (fields | list.append finalField) } | ||
} | ||
|
||
test = | ||
checkEquals (decode "") (Ok (,)) | ||
checkEquals (decode "{text.newline}") (Ok (,)) | ||
|
||
checkEquals (decode "aaa") (Ok (("aaa",),)) | ||
checkEquals (decode "aaa{text.newline}") (Ok (("aaa",),)) | ||
|
||
checkEquals (decode " aaa ") (Ok ((" aaa ",),)) | ||
checkEquals (decode " aaa {text.newline}") (Ok ((" aaa ",),)) | ||
|
||
checkEquals (decode "aaa,bbb") (Ok (("aaa", "bbb"),)) | ||
checkEquals (decode "aaa,bbb{text.newline}") (Ok (("aaa", "bbb"),)) | ||
checkEquals (decode "aaa,bbb{text.newline}ccc,ddd") (Ok (("aaa", "bbb"), ("ccc", "ddd"))) | ||
checkEquals | ||
decode "aaa,bbb{text.newline}ccc,ddd{text.newline}" | ||
Ok (("aaa", "bbb"), ("ccc", "ddd")) | ||
# Parser is broken, hence this verbose formatting: https://github.com/candy-lang/candy/issues/896 | ||
checkEquals | ||
decode " | ||
aaa , bbb {text.newline} ccc , ddd {text.newline} | ||
" | ||
Ok ( | ||
( | ||
" | ||
aaa | ||
", | ||
" | ||
bbb | ||
", | ||
), | ||
( | ||
" | ||
ccc | ||
", | ||
" | ||
ddd | ||
", | ||
), | ||
) | ||
|
||
testRfcExamples = | ||
# From https://datatracker.ietf.org/doc/html/rfc4180#section-2 | ||
checkEquals (decode "aaa,bbb,ccc") (Ok (("aaa", "bbb", "ccc"),)) | ||
|
||
checkEquals | ||
decode "aaa,bbb,ccc{text.newline}zzz,yyy,xxx{text.newline}" | ||
Ok (("aaa", "bbb", "ccc"), ("zzz", "yyy", "xxx")) | ||
checkEquals | ||
decode " | ||
field_name,field_name,field_name{text.newline}aaa,bbb,ccc{text.newline}zzz,yyy,xxx{text.newline} | ||
" | ||
Ok (("field_name", "field_name", "field_name"), ("aaa", "bbb", "ccc"), ("zzz", "yyy", "xxx")) | ||
# TODO(JonasWanke): handle quoted field | ||
# checkEquals | ||
# decode '"aaa,"b{{text.newline}}bb",ccc{{text.newline}}zzz,yyy,xxx{{text.newline}}"' | ||
# Ok (("aaa", "b{text.newline}bb", "ccc"), ("zzz", "yyy", "xxx")) | ||
# checkEquals (decode '"aaa,"b""bb",ccc{{text.newline}}"') (Ok (("aaa", '"b"bb"', "ccc"),)) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
[bool, checkEquals, equals, ifElse, iterator, list, result, text] = use "Core" | ||
|
||
encodeField field = | ||
needs (text.is field) | ||
ifElse | ||
field | text.contains '"""' | bool.lazyOr { field | text.contains text.newline } | ||
{ | ||
encoded = field | text.characters | iterator.fromList | ||
| iterator.map { char -> ifElse (char | equals '"""') { '""""' } { char } } | ||
| iterator.joinToText | ||
'""{{encoded}}""' | ||
} | ||
{ field } | ||
|
||
encodeLine line = | ||
needs (list.is line) | ||
needs (line | iterator.fromList | iterator.all text.is) | ||
line | iterator.fromList | iterator.map encodeField | iterator.joinToTextWithSeparator "," | ||
|
||
encode lines := | ||
needs (list.is lines) | ||
needs (lines | iterator.fromList | iterator.all list.is) | ||
fieldsPerLine = lines | list.first | result.map list.length | result.unwrapOr 0 | ||
lines | iterator.fromList | iterator.map { line -> | ||
needs (line | list.length | equals fieldsPerLine) | ||
"{line | encodeLine}{text.newline}" | ||
} | ||
| iterator.joinToText | ||
|
||
testEncodeLine = | ||
# TODO(JonaWanke): Add tests cases for leading/trailing whitespace when our parser is fixed, | ||
# https://github.com/candy-lang/candy/issues/896 | ||
checkEquals (encodeLine (,)) "" | ||
checkEquals (encodeLine ("aaa",)) "aaa" | ||
checkEquals (encodeLine ("aaa", "bbb")) "aaa,bbb" | ||
|
||
testEncodeLineWithSpecialCharacters = | ||
checkEquals (encodeLine ('"aa"a"',)) '""aa""a""' | ||
checkEquals (encodeLine ("aa{text.newline}a",)) '""aa{{text.newline}}a""' | ||
|
||
testEncode = | ||
checkEquals (encode (,)) "" | ||
checkEquals (encode (("aaa",),)) "aaa{text.newline}" | ||
checkEquals (encode (("aaa", "bbb"),)) "aaa,bbb{text.newline}" | ||
checkEquals (encode (("aaa",), ("bbb",))) "aaa{text.newline}bbb{text.newline}" | ||
|
||
testRfcExamples = | ||
# From https://datatracker.ietf.org/doc/html/rfc4180#section-2 | ||
checkEquals (encodeLine ("aaa", "bbb", "ccc")) "aaa,bbb,ccc" | ||
|
||
checkEquals | ||
encode (("aaa", "bbb", "ccc"), ("zzz", "yyy", "xxx")) | ||
"aaa,bbb,ccc{text.newline}zzz,yyy,xxx{text.newline}" | ||
checkEquals | ||
encode (("field_name", "field_name", "field_name"), ("aaa", "bbb", "ccc"), ("zzz", "yyy", "xxx")) | ||
" | ||
field_name,field_name,field_name{text.newline}aaa,bbb,ccc{text.newline}zzz,yyy,xxx{text.newline} | ||
" | ||
checkEquals | ||
encode (("aaa", "b{text.newline}bb", "ccc"), ("zzz", "yyy", "xxx")) | ||
'"aaa,"b{{text.newline}}bb",ccc{{text.newline}}zzz,yyy,xxx{{text.newline}}"' | ||
checkEquals (encode (("aaa", '"b"bb"', "ccc"),)) '"aaa,"b""bb",ccc{{text.newline}}"' |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cursor := use ".cursor" | ||
parser := use ".parser" |
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[bool, equals, int, text] = use "Core" | ||
|
||
is cursor := cursor % | ||
Cursor [source, offset] -> | ||
text.is source | bool.lazyAnd { int.is offset } | bool.lazyAnd { int.isNonNegative offset } | ||
| bool.lazyAnd { offset | int.isLessThanOrEqualTo (source | text.length) } | ||
_ -> False | ||
|
||
newAtStart source := | ||
needs (text.is source) | ||
Cursor [source, Offset: 0] | ||
|
||
isAtEnd cursor := | ||
needs (is cursor) | ||
Cursor [source, offset] = cursor | ||
offset | equals (source | text.length) | ||
|
||
add cursor length := | ||
needs (is cursor) | ||
needs (int.is length) | ||
needs (int.isNonNegative length) | ||
Cursor [source, offset] = cursor | ||
offset = offset | int.add length | ||
needs (offset | int.isLessThanOrEqualTo (source | text.length)) | ||
Cursor [source, offset] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
[bool, equals, ifElse, int, text] = use "Core" | ||
cursor = use "..cursor" | ||
|
||
is parser := parser % | ||
Parser c -> cursor.is c | ||
_ -> False | ||
|
||
new source := | ||
needs (text.is source) | ||
Parser (cursor.newAtStart source) | ||
|
||
peek parser := | ||
needs (is parser) | ||
Parser c = parser | ||
ifElse (c | cursor.isAtEnd) { Error Empty } { | ||
Cursor [source, offset] = c | ||
Ok (source | text.getRange offset (offset | int.add 1)) | ||
} | ||
next parser := | ||
needs (is parser) | ||
Parser c = parser | ||
ifElse (c | cursor.isAtEnd) { Error Empty } { | ||
Cursor [source, offset] = c | ||
c = c | cursor.add 1 | ||
Cursor [Offset: newOffset] = c | ||
Ok [Parser: Parser c, Character: source | text.getRange offset newOffset] | ||
} | ||
|
||
matches parser expectedText := | ||
needs (is parser) | ||
needs (text.is expectedText) | ||
Parser (Cursor [source, offset]) = parser | ||
endExclusive = offset | int.add (expectedText | text.length) | ||
endExclusive | int.isLessThanOrEqualTo (source | text.length) | ||
| bool.lazyAnd { source | text.getRange offset endExclusive | equals expectedText } | ||
require parser expectedText := | ||
needs (is parser) | ||
needs (text.is expectedText) | ||
ifElse | ||
parser | matches expectedText | ||
{ | ||
Parser c = parser | ||
Ok (Parser (c | cursor.add (expectedText | text.length))) | ||
} | ||
{ | ||
Parser (Cursor [offset]) = parser | ||
Error '"Expected "{{expectedText}}" at offset {offset}."' | ||
} |
This file contains 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
85bbecd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compiler
Time: Compiler/hello_world
37817577
ns/iter (± 401562
)37726636
ns/iter (± 581259
)1.00
Time: Compiler/fibonacci
192516036
ns/iter (± 555334
)191835875
ns/iter (± 698916
)1.00
Time: VM Runtime/hello_world
36117
ns/iter (± 2221
)43466
ns/iter (± 5701
)0.83
Time: VM Runtime/fibonacci/15
296468610
ns/iter (± 1728444
)296425323
ns/iter (± 1677685
)1.00
Time: VM Runtime/PLB/binarytrees/6
1593328858
ns/iter (± 7402098
)1613548766
ns/iter (± 21582512
)0.99
This comment was automatically generated by workflow using github-action-benchmark.