1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
package gololang
// The quoted literals of Golo: "strings", """multi-line strings""" and
// 'characters', and the one rule that governs all three — each runs to its
// closing quote, wherever that quote is.
import "rickub.com/turbo-editors/turbo-core/syntax"
// multilineStringQuote opens and closes a multi-line string. Inside one the
// lexer interprets nothing — no escape, no interpolation — so the only thing
// that can end it is these three runes.
const multilineStringQuote = `"""`
// takeQuoted colours a "…" or '…' literal from its opening quote to its
// closing one, and reports it as still open when the line ends first.
//
// Carrying it is the honest reading of the language. The interpreter reads a
// string to its closing quote and nothing stops it at a newline, so a line
// ending inside a literal is not broken source — it is a literal with a line
// break in it — and stopping the colour at the line would draw the next line
// as code the interpreter will never run as code.
//
// A character literal is read the same way, by the same loop in lexer.go, so
// it is carried the same way. That a 'character' spanning lines is nonsense to
// a reader is beside the point: the colour says what the interpreter will do.
func takeQuoted(s *syntax.LineScanner, quote rune, class syntax.Class, ifOpen carry) carry {
start := s.Pos()
s.Advance(1) // the opening quote
closed := consumeQuoted(s, quote)
s.Emit(start, s.Pos(), class)
return stillOpen(closed, ifOpen)
}
// finishQuoted colours the rest of a literal opened on an earlier line, and
// says whether it is still open at the end of this one.
func finishQuoted(s *syntax.LineScanner, quote rune, class syntax.Class, ifOpen carry) carry {
closed := consumeQuoted(s, quote)
s.Emit(0, s.Pos(), class)
return stillOpen(closed, ifOpen)
}
// consumeQuoted runs to the closing quote, or to the end of the line, and
// reports which it found.
//
// A backslash takes the rune after it out of consideration. That one rule
// covers every escape the lexer knows — \n, \t, \r, \\, \", \', \0 and \xHH —
// and the ones it does not, which it keeps as the character itself; all any of
// them need from this scanner is that the rune after the backslash cannot
// close the literal. A backslash that ends the line escapes the newline, which
// the lexer also reads as part of the string, and the carry covers that.
func consumeQuoted(s *syntax.LineScanner, quote rune) bool {
for !s.AtEnd() {
switch s.Peek(0) {
case '\\':
s.Advance(2)
case quote:
s.Advance(1)
return true
default:
s.Advance(1)
}
}
return false
}
// takeMultilineString colours a """…""" literal from its opening quotes to its
// closing ones, and reports it as still open when the line ends first.
//
// syntax.OpenBlockComment is named for comments, and what it does is exactly
// this: from an opener to the first closer, or to the end of the line, in one
// class, with no escapes considered. A multi-line string in Golo has no
// escapes either — the lexer appends every rune it meets until the three
// quotes — so the helper is the right tool under a misleading name, and the
// continuation on later lines goes through FinishBlockComment for the same
// reason.
func takeMultilineString(s *syntax.LineScanner) carry {
closed := syntax.OpenBlockComment(s, multilineStringQuote, multilineStringQuote, syntax.ClassString)
return stillOpen(closed, multilineStringOpen)
}
|