Skip to content

Commit 3bf1b7d

Browse files
committed
feat: implement indent handling in lexer and parser
1 parent 735a733 commit 3bf1b7d

File tree

10 files changed

+171
-41
lines changed

10 files changed

+171
-41
lines changed

playground/simple1.toon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
key: value

playground/simple2.toon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
key1: value1
2+
key2: value2

playground/simple3.toon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
key1:
2+
- value1
3+
- value2

playground/simple4.toon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
key1:
2+
- value1
3+
- value2
4+
5+
key2:
6+
- value1
7+
- value2

src/main/kotlin/com/github/xepozz/toon/language/ToonParserDefinition.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class ToonParserDefinition : ParserDefinition {
1616
override fun getWhitespaceTokens() = TokenSet.WHITE_SPACE
1717

1818
override fun createParser(project: com.intellij.openapi.project.Project?) = ToonParser()
19+
// override fun createParser(project: com.intellij.openapi.project.Project?) = YAMLParser()
1920

2021
override fun getFileNodeType() = FILE
2122

src/main/kotlin/com/github/xepozz/toon/language/ToonSyntaxHighlighter.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ class ToonSyntaxHighlighter : SyntaxHighlighterBase() {
1515
override fun getTokenHighlights(tokenType: IElementType) = when (tokenType) {
1616
ToonTypes.TEXT -> STRING_KEYS
1717
ToonTypes.NUMBER -> NUMBER_KEYS
18-
ToonTypes.TRUE -> CONSTANT_KEYS
19-
ToonTypes.FALSE -> CONSTANT_KEYS
18+
ToonTypes.BOOLEAN -> CONSTANT_KEYS
2019
ToonTypes.NULL -> CONSTANT_KEYS
2120
ToonTypes.COMMENT -> COMMENT_KEYS
2221
TokenType.BAD_CHARACTER -> BAD_CHAR_KEYS

src/main/kotlin/com/github/xepozz/toon/language/parser/Toon.bnf

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,101 @@
44
extends = "com.intellij.extapi.psi.ASTWrapperPsiElement"
55

66
psiClassPrefix = "Toon"
7-
psiImplClassSuffix="Impl"
8-
psiPackage="com.github.xepozz.toon.language.psi"
9-
psiImplPackage="com.github.xepozz.toon.language.psi.impl"
7+
psiImplClassSuffix = "Impl"
8+
psiPackage = "com.github.xepozz.toon.language.psi"
9+
psiImplPackage = "com.github.xepozz.toon.language.psi.impl"
1010

11-
elementTypeHolderClass="com.github.xepozz.toon.language.psi.ToonTypes"
11+
elementTypeHolderClass = "com.github.xepozz.toon.language.psi.ToonTypes"
1212
elementTypeClass = "com.github.xepozz.toon.language.psi.ToonElementType"
1313
tokenTypeClass = "com.github.xepozz.toon.language.psi.ToonTokenType"
1414

15-
psiImplUtilClass="com.github.xepozz.toon.language.psi.impl.ToonImplUtil"
16-
tokens=[
17-
LPAREN="LPAREN"
18-
RPAREN="RPAREN"
15+
psiImplUtilClass = "com.github.xepozz.toon.language.psi.impl.ToonImplUtil"
16+
17+
tokens = [
18+
LPAREN = "LPAREN"
19+
RPAREN = "RPAREN"
20+
LBRACE = "LBRACE"
21+
RBRACE = "RBRACE"
22+
LBRACKET = "LBRACKET"
23+
RBRACKET = "RBRACKET"
24+
DELIMITER = "DELIMITER"
25+
COMMA = "COMMA"
26+
DASH = "DASH"
27+
SHARP = "SHARP"
28+
EOL = "EOL"
29+
COMMENT = "COMMENT"
30+
TEXT = "TEXT"
31+
NUMBER = "NUMBER"
32+
NULL = "NULL"
33+
BOOLEAN = "BOOLEAN"
34+
INDENT = "INDENT"
35+
DEDENT = "DEDENT"
36+
BLOCK = "BLOCK"
1937
]
2038
}
2139

2240
file ::= entry*
2341

24-
private entry ::= table | keyValueMap | COMMENT | EOL
42+
private entry ::=
43+
object_field
44+
| table
45+
| list_item
46+
| COMMENT
47+
| EOL
48+
object_field ::= key DELIMITER field_value {
49+
pin = 2
50+
}
51+
private field_value ::=
52+
value_list
53+
| nested_object
54+
| array
2555

26-
table ::= tableDefinition DELIMITER (iterable | EOL array)
27-
{
28-
pin=2
56+
value_list ::= value (COMMA value)*
57+
58+
nested_object ::= EOL INDENT entry+ DEDENT {
59+
pin = 2
2960
}
3061

31-
tableDefinition ::= key LBRACKET SHARP? NUMBER RBRACKET (LBRACE iterable RBRACE)?
32-
{
33-
pin=2
62+
array ::= list_item (EOL list_item)+ {
63+
pin = 1
3464
}
3565

36-
array ::= keyValueArray+ | keyValueMap+ | valuesList
66+
list_item ::= DASH list_item_content {
67+
pin = 1
68+
}
3769

38-
valuesList ::= iterable (EOL iterable)*
70+
private list_item_content ::=
71+
value_list
72+
| key DELIMITER field_value
3973

40-
keyValueArray ::= keyValueArrayItem (EOL keyValueArrayItem)*
74+
table ::= table_header DELIMITER table_data EOL {
75+
pin = 2
76+
}
4177

42-
keyValueArrayItem ::= DASH (keyValueMap | iterable)
43-
{
44-
pin=1
78+
table_header ::= key LBRACKET table_size RBRACKET field_list? {
79+
pin=4
4580
}
4681

47-
keyValueMap ::= key DELIMITER (iterable | EOL array)
48-
{
49-
pin=2
82+
table_size ::= SHARP? NUMBER
83+
84+
field_list ::= LBRACE field_names RBRACE {
85+
pin = 1
5086
}
5187

52-
key ::= TEXT
88+
field_names ::= key (COMMA key)*
5389

54-
iterable ::= value (COMMA value)*
90+
private table_data ::=
91+
table_row | (EOL table_row)+
5592

56-
value ::= TEXT | NULL | FALSE | TRUE | NUMBER
57-
{
58-
implements=["com.intellij.psi.PsiLiteralValue"]
59-
methods=[getValue getReferences]
60-
}
93+
table_row ::= value (COMMA value)*
94+
95+
key ::= TEXT
96+
97+
value ::=
98+
TEXT
99+
| NUMBER
100+
| NULL
101+
| BOOLEAN {
102+
implements = ["com.intellij.psi.PsiLiteralValue"]
103+
methods = [getValue getReferences]
104+
}

src/main/kotlin/com/github/xepozz/toon/language/parser/Toon.flex

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ import com.github.xepozz.toon.language.psi.ToonTypes;
1414
%eof{ return;
1515
%eof}
1616

17+
%{
18+
private int prevIndent = 0;
19+
private boolean atLineStart = true;
20+
%}
1721

18-
NEWLINE=\r|\n|\r\n
22+
23+
NEWLINE=\r\n|\r|\n
1924
WHITESPACE=[ \t]+
2025
ALPHA=[A-Za-z]
2126
NUMBER=[\d]+(.[\d]+)?
2227
QUOTTED_STRING = "\""(\\\"|[^\"])*"\"" | "\'"(\\\'|[^\'])*"\'"
2328

24-
%state VALUE, TABLE_SIZE
29+
%state VALUE, TABLE_SIZE, MAIN
2530
%%
2631

2732
<YYINITIAL> {
@@ -34,17 +39,17 @@ QUOTTED_STRING = "\""(\\\"|[^\"])*"\"" | "\'"(\\\'|[^\'])*"\'"
3439
<TABLE_SIZE> {
3540
{NUMBER} { return ToonTypes.NUMBER; }
3641
"#" { return ToonTypes.SHARP; }
37-
"]" { yybegin(YYINITIAL); return ToonTypes.RBRACKET; }
42+
"]" { yybegin(MAIN); return ToonTypes.RBRACKET; }
3843
}
3944

4045
<VALUE> {
4146
"null" { return ToonTypes.NULL; }
42-
"false" { return ToonTypes.FALSE; }
43-
"true" { return ToonTypes.TRUE; }
47+
"false"|"true" { return ToonTypes.BOOLEAN; }
4448
"," { return ToonTypes.COMMA; }
4549
{NUMBER} { return ToonTypes.NUMBER; }
4650
{ALPHA}[^\n,]* { return ToonTypes.TEXT; }
4751
{QUOTTED_STRING} { return ToonTypes.TEXT; }
52+
{WHITESPACE} { return TokenType.WHITE_SPACE; }
4853
}
4954

5055

@@ -58,6 +63,6 @@ QUOTTED_STRING = "\""(\\\"|[^\"])*"\"" | "\'"(\\\'|[^\'])*"\'"
5863
"]" { return ToonTypes.RBRACKET; }
5964
"-" { return ToonTypes.DASH; }
6065
{WHITESPACE} { return TokenType.WHITE_SPACE; }
61-
{NEWLINE} { yybegin(YYINITIAL); return ToonTypes.EOL; }
66+
{NEWLINE} { atLineStart = true; yybegin(YYINITIAL); return ToonTypes.EOL; }
6267

6368
[^] { return TokenType.BAD_CHARACTER; }
Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,73 @@
11
package com.github.xepozz.toon.language.parser
22

3+
import com.github.xepozz.toon.language.psi.ToonTypes
34
import com.intellij.lexer.FlexAdapter
5+
import com.intellij.lexer.Lexer
6+
import com.intellij.lexer.LookAheadLexer
47

5-
class ToonLexerAdapter : FlexAdapter(ToonLexer(null))
8+
class ToonLexerAdapter : LookAheadLexer(FlexAdapter(ToonLexer(null))) {
9+
private val indentStack = mutableListOf(0)
10+
11+
override fun lookAhead(baseLexer: Lexer) {
12+
val tokenType = baseLexer.tokenType
13+
14+
if (tokenType == null) {
15+
addToken(ToonTypes.DEDENT)
16+
super.lookAhead(baseLexer)
17+
return
18+
}
19+
20+
if (tokenType != ToonTypes.EOL) {
21+
advanceAs(baseLexer, tokenType)
22+
return
23+
}
24+
25+
// Emit the newline token
26+
advanceAs(baseLexer, tokenType)
27+
28+
// Inspect the beginning of the next line
29+
val buffer = baseLexer.bufferSequence
30+
val bufferEnd = baseLexer.bufferEnd
31+
32+
var index = baseLexer.tokenStart
33+
if (index >= bufferEnd) return
34+
35+
var columns = 0
36+
37+
// Count leading spaces
38+
while (index < bufferEnd) {
39+
val c = buffer[index]
40+
when (c) {
41+
' ' -> {
42+
columns++
43+
index++
44+
}
45+
'\r', '\n' -> return
46+
else -> break
47+
}
48+
}
49+
50+
if (index >= bufferEnd) return
51+
52+
val newIndent = columns
53+
val currentIndent = indentStack.last()
54+
55+
val logicalLineStart = index // Actual content starts here
56+
57+
when {
58+
newIndent > currentIndent -> {
59+
indentStack.add(newIndent)
60+
addToken(ToonTypes.INDENT)
61+
}
62+
newIndent < currentIndent -> {
63+
while (indentStack.size > 1 && newIndent < indentStack.last()) {
64+
indentStack.removeAt(indentStack.size - 1)
65+
addToken(logicalLineStart, ToonTypes.DEDENT)
66+
}
67+
}
68+
else -> {
69+
// Same indent – nothing to emit
70+
}
71+
}
72+
}
73+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<extensions defaultExtensionNs="com.intellij">
1313
<fileType
14-
name="TOON file"
14+
name="TOON"
1515
implementationClass="com.github.xepozz.toon.language.ToonFileType"
1616
language="TOON"
1717
patterns="*.toon;*.toon.yaml"

0 commit comments

Comments
 (0)