1 -- Copyright 2006-2016 Mitchell mitchell.att.foicica.com. See LICENSE.
3 -- It does not keep track of indentation perfectly.
5 local l
= require('lexer')
6 local token
, word_match
= l
.token
, l
.word_match
7 local P
, R
, S
= lpeg
.P
, lpeg
.R
, lpeg
.S
9 local M
= {_NAME
= 'yaml'}
12 local indent
= #l
.starts_line(S(' \t')) *
13 (token(l
.WHITESPACE
, ' ') + token('indent_error', '\t'))^
1
14 local ws
= token(l
.WHITESPACE
, S(' \t')^
1 + l
.newline^
1)
17 local comment
= token(l
.COMMENT
, '#' * l
.nonnewline^
0)
20 local string = token(l
.STRING
, l
.delimited_range("'") + l
.delimited_range('"'))
23 local integer
= l
.dec_num
+ l
.hex_num
+ '0' * S('oO') * R('07')^
1
24 local special_num
= '.' * word_match({'inf', 'nan'}, nil, true)
25 local number = token(l
.NUMBER
, special_num
+ l
.float
+ integer
)
28 local ts
= token('timestamp', l
.digit
* l
.digit
* l
.digit
* l
.digit
* -- year
29 '-' * l
.digit
* l
.digit^
-1 * -- month
30 '-' * l
.digit
* l
.digit^
-1 * -- day
31 ((S(' \t')^
1 + S('tT'))^
-1 * -- separator
32 l
.digit
* l
.digit^
-1 * -- hour
33 ':' * l
.digit
* l
.digit
* -- minute
34 ':' * l
.digit
* l
.digit
* -- second
35 ('.' * l
.digit^
0)^
-1 * -- fraction
37 S(' \t')^
0 * S('-+') * l
.digit
* l
.digit^
-1 *
38 (':' * l
.digit
* l
.digit
)^
-1)^
-1)^
-1)
41 local constant
= token(l
.CONSTANT
,
42 word_match({'null', 'true', 'false'}, nil, true))
45 local type = token(l
.TYPE
, '!!' * word_match({
47 'map', 'omap', 'pairs', 'set', 'seq',
49 'binary', 'bool', 'float', 'int', 'merge', 'null', 'str', 'timestamp',
51 }, nil, true) + '!' * l
.delimited_range('<>'))
53 -- Document boundaries.
54 local doc_bounds
= token('document', l
.starts_line(P('---') + '...'))
57 local directive
= token('directive', l
.starts_line('%') * l
.nonnewline^
1)
59 local word
= (l
.alpha
+ '-' * -l
.space
) * (l
.alnum
+ '-')^
0
62 local colon
= S(' \t')^
0 * ':' * (l
.space
+ -1)
63 local key
= token(l
.KEYWORD
,
64 #word
* (l
.nonnewline
- colon
)^
1 * #colon
*
65 P(function(input
, index
)
66 local line
= input
:sub(1, index
- 1):match('[^\r\n]+$')
67 return not line
:find('[%w-]+:') and index
69 local value
= #word
* (l
.nonnewline
- l
.space^
0 * S(',]}'))^
1
70 local block
= S('|>') * S('+-')^
-1 * (l
.newline
+ -1) * function(input
, index
)
71 local rest
= input
:sub(index
)
72 local level
= #rest
:match('^( *)')
73 for pos
, indent
, line
in rest
:gmatch('() *()([^\r\n]+)') do
74 if indent
- pos
< level
and line
~= ' ' or level
== 0 and pos
> 1 then
75 return index
+ pos
- 1
80 local literal
= token('literal', value
+ block
)
83 local anchor
= token(l
.LABEL
, '&' * word
)
84 local alias
= token(l
.VARIABLE
, '*' * word
)
85 local tag = token('tag', '!' * word
* P('!')^
-1)
86 local reserved
= token(l
.ERROR
, S('@`') * word
)
87 local indicator_chars
= token(l
.OPERATOR
, S('-?:,[]{}!'))
93 {'doc_bounds', doc_bounds
},
98 {'constant', constant
},
100 {'indicator', tag + indicator_chars
+ alias
+ anchor
+ reserved
},
101 {'directive', directive
},
105 indent_error
= 'back:%(color.red)',
106 document
= l
.STYLE_CONSTANT
,
107 literal
= l
.STYLE_DEFAULT
,
108 timestamp
= l
.STYLE_NUMBER
,
110 directive
= l
.STYLE_PREPROCESSOR
,
113 M
._FOLDBYINDENTATION
= true