tangled
alpha
login
or
join now
gearsco.de
/
pearl
2
fork
atom
An Erlang lexer and syntax highlighter in Gleam
2
fork
atom
overview
issues
pulls
pipelines
Add tests for errors
gearsco.de
7 months ago
e05b552a
a03f558e
+156
-20
2 changed files
expand all
collapse all
unified
split
src
pearl.gleam
test
pearl_test.gleam
+10
-14
src/pearl.gleam
···
32
32
InvalidRadix(radix: String)
33
33
NumericSeparatorNotAllowed
34
34
ExpectedExponent
35
35
-
NumbersCannotEndAfterRadix
35
35
+
NumberCannotEndAfterRadix
36
36
UnterminatedCharacter
37
37
UnterminatedEscapeSequence
38
38
ExpectedSigilDelimiter
···
73
73
74
74
pub fn tokenise(lexer: Lexer) -> #(List(Token), List(Error)) {
75
75
do_tokenise(lexer, [])
76
76
+
}
77
77
+
78
78
+
pub fn to_source(tokens: List(Token)) -> String {
79
79
+
list.fold(tokens, "", fn(code, token) { code <> token.to_source(token) })
76
80
}
77
81
78
82
fn do_tokenise(lexer: Lexer, tokens: List(Token)) -> #(List(Token), List(Error)) {
···
563
567
token.Integer(string.drop_end(lexed, 1)),
564
568
)
565
569
AfterExponent -> #(error(lexer, ExpectedExponent), token)
566
566
-
AfterRadix -> #(error(lexer, NumbersCannotEndAfterRadix), token)
570
570
+
AfterRadix -> #(error(lexer, NumberCannotEndAfterRadix), token)
567
571
AfterNumber -> #(lexer, token)
568
572
AfterSeparator -> #(error(lexer, NumericSeparatorNotAllowed), token)
569
573
}
···
684
688
token.UnterminatedString(contents <> before),
685
689
)
686
690
687
687
-
"\\" ->
688
688
-
case string.pop_grapheme(after) {
689
689
-
Error(_) -> #(
690
690
-
error(advance(lexer, after), UnterminatedString),
691
691
-
token.UnterminatedString(contents),
692
692
-
)
693
693
-
Ok(#(character, source)) ->
694
694
-
lex_string(
695
695
-
advance(lexer, source),
696
696
-
contents <> before <> "\\" <> character,
697
697
-
)
698
698
-
}
691
691
+
"\\" -> {
692
692
+
let #(lexer, escape) = lex_escape_sequence(advance(lexer, after))
693
693
+
lex_string(lexer, contents <> before <> "\\" <> escape)
694
694
+
}
699
695
700
696
_ -> #(advance(lexer, after), token.String(contents <> before))
701
697
}
+146
-6
test/pearl_test.gleam
···
1
1
+
import gleam/list
1
2
import gleeunit
2
2
-
import gleeunit/should
3
3
+
import pearl
4
4
+
import pearl/token
5
5
+
import simplifile
3
6
4
4
-
pub fn main() -> Nil {
7
7
+
pub fn main() {
5
8
gleeunit.main()
6
9
}
7
10
8
8
-
// gleeunit test functions end in `_test`
9
9
-
pub fn hello_world_test() {
10
10
-
1
11
11
-
|> should.equal(1)
11
11
+
fn assert_roundtrip(src: String, allow_errors: Bool) -> Nil {
12
12
+
let #(tokens, errors) = src |> pearl.new |> pearl.tokenise
13
13
+
case allow_errors {
14
14
+
True -> Nil
15
15
+
False -> {
16
16
+
assert errors == []
17
17
+
}
18
18
+
}
19
19
+
20
20
+
assert pearl.to_source(tokens) == src
21
21
+
}
22
22
+
23
23
+
fn assert_tokens(src: String, tokens: List(token.Token)) -> Nil {
24
24
+
let #(lexed, errors) =
25
25
+
src |> pearl.new |> pearl.ignore_whitespace |> pearl.tokenise
26
26
+
assert errors == []
27
27
+
let tokens = list.append(tokens, [token.EndOfFile])
28
28
+
assert lexed == tokens
29
29
+
}
30
30
+
31
31
+
fn assert_errors(src: String, errors: List(pearl.Error)) -> Nil {
32
32
+
let #(_, found_errors) = src |> pearl.new |> pearl.tokenise
33
33
+
assert found_errors == errors
34
34
+
}
35
35
+
36
36
+
pub fn unknown_character_test() {
37
37
+
let src = "a&b"
38
38
+
assert_errors(src, [pearl.UnknownCharacter("&")])
39
39
+
}
40
40
+
41
41
+
pub fn unterminated_string_eof_test() {
42
42
+
let src = "\"Some string that doesn't end"
43
43
+
assert_errors(src, [pearl.UnterminatedString])
44
44
+
}
45
45
+
46
46
+
pub fn unterminated_string_newline_test() {
47
47
+
let src =
48
48
+
"\"Some string that tries
49
49
+
to continue on the next line"
50
50
+
assert_errors(src, [pearl.UnterminatedString])
51
51
+
}
52
52
+
53
53
+
pub fn unterminated_atom_test() {
54
54
+
let src = "'This is an atom!"
55
55
+
assert_errors(src, [pearl.UnterminatedAtom])
56
56
+
}
57
57
+
58
58
+
pub fn invalid_radix_test() {
59
59
+
let src = "94#123"
60
60
+
assert_errors(src, [pearl.InvalidRadix("94")])
61
61
+
}
62
62
+
63
63
+
pub fn invalid_radix_low_test() {
64
64
+
let src = "1#123"
65
65
+
assert_errors(src, [pearl.InvalidRadix("1")])
66
66
+
}
67
67
+
68
68
+
pub fn number_cannot_end_after_radix_test() {
69
69
+
let src = "16#"
70
70
+
assert_errors(src, [pearl.NumberCannotEndAfterRadix])
71
71
+
}
72
72
+
73
73
+
pub fn unterminated_character_test() {
74
74
+
let src = "$"
75
75
+
assert_errors(src, [pearl.UnterminatedCharacter])
76
76
+
}
77
77
+
78
78
+
pub fn expected_sigil_delimiter_test() {
79
79
+
let src = "~abc"
80
80
+
assert_errors(src, [pearl.ExpectedSigilDelimiter])
81
81
+
}
82
82
+
83
83
+
pub fn unterminated_escape_sequence_test() {
84
84
+
let src = "\"\\x\""
85
85
+
assert_errors(src, [pearl.UnterminatedEscapeSequence])
86
86
+
}
87
87
+
88
88
+
pub fn unterminated_escape_sequence2_test() {
89
89
+
let src = "\"\\x1\""
90
90
+
assert_errors(src, [pearl.UnterminatedEscapeSequence])
91
91
+
}
92
92
+
93
93
+
pub fn unterminated_escape_sequence3_test() {
94
94
+
let src = "\"\\x{123\""
95
95
+
assert_errors(src, [
96
96
+
pearl.UnterminatedEscapeSequence,
97
97
+
pearl.UnterminatedString,
98
98
+
])
99
99
+
}
100
100
+
101
101
+
pub fn unterminated_escape_sequence4_test() {
102
102
+
let src = "\"\\"
103
103
+
assert_errors(src, [
104
104
+
pearl.UnterminatedEscapeSequence,
105
105
+
pearl.UnterminatedString,
106
106
+
])
107
107
+
}
108
108
+
109
109
+
pub fn double_numeric_separator_test() {
110
110
+
let src = "1__2"
111
111
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
112
112
+
}
113
113
+
114
114
+
pub fn trailing_numeric_separator_test() {
115
115
+
let src = "1_2_"
116
116
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
117
117
+
}
118
118
+
119
119
+
pub fn trailing_decimal_numeric_separator_test() {
120
120
+
let src = "1_2_.3"
121
121
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
122
122
+
}
123
123
+
124
124
+
pub fn leading_decimal_numeric_separator_test() {
125
125
+
let src = "1_2._3"
126
126
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
127
127
+
}
128
128
+
129
129
+
pub fn trailing_exponent_numeric_separator_test() {
130
130
+
let src = "1_2.3_e4"
131
131
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
132
132
+
}
133
133
+
134
134
+
pub fn leading_exponent_numeric_separator_test() {
135
135
+
let src = "1_2.3e_4"
136
136
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
137
137
+
}
138
138
+
139
139
+
pub fn leading_negative_exponent_numeric_separator_test() {
140
140
+
let src = "1_2.3e-_4"
141
141
+
assert_errors(src, [pearl.NumericSeparatorNotAllowed])
142
142
+
}
143
143
+
144
144
+
pub fn missing_exponent_test() {
145
145
+
let src = "1.2e"
146
146
+
assert_errors(src, [pearl.ExpectedExponent])
147
147
+
}
148
148
+
149
149
+
pub fn missing_negative_exponent_test() {
150
150
+
let src = "1.2e-"
151
151
+
assert_errors(src, [pearl.ExpectedExponent])
12
152
}