行頭以外にヘッダー等があった場合にヘッダーとして認識されてしまう問題を修正

This commit is contained in:
usbharu 2024-11-14 12:23:31 +09:00
parent 985df8ed82
commit 6659720f2c
Signed by: usbharu
GPG Key ID: 95CBCF7046307B77
3 changed files with 129 additions and 65 deletions

View File

@ -7,91 +7,36 @@ class Lexer {
val tokens = mutableListOf<Token>()
val lines = PeekableStringIterator(input.lines())
var inQuote = false
var inCode = false
var inQuote = false //引用中の判断
var inCode = false //コードブロック内の判断
var inline = false //行頭の判断
val codeBuffer = StringBuilder()
line@ while (lines.hasNext()) {
inline = false //改行時にリセット
if (lines.peekOrNull() == "") {
blankLine(lines, tokens)
} else {
val line = lines.next()
val iterator = PeekableCharIterator(line.toCharArray())
char@ while (iterator.hasNext()) {
val next = iterator.next()
when {
next == '`' || next == '' -> {
//todo ````` のようなやつが来たときのことを考える
if (iterator.peekOrNull() == next) {
val codeBlockBuilder = StringBuilder()
codeBlockBuilder.append(next)
codeBlockBuilder.append(iterator.next())
if (iterator.peekOrNull() == next) {
codeBlockBuilder.append(iterator.next())
if (iterator.peekOrNull() == next) {
tokens.add(Text(codeBlockBuilder.toString()))
} else {
if (inCode) {
inCode = false
tokens.add(CodeBlock(codeBuffer.toString().trimStart('\n').trimEnd('\n')))
codeBuffer.clear()
} else {
inCode = true
var inFilename = false
val language = StringBuilder()
val filename = StringBuilder()
if (iterator.hasNext()) {
codeBlock@ while (iterator.hasNext()) {
val nextLanguage = iterator.next()
if ((nextLanguage == ':' || nextLanguage == '') && !inFilename) {
inFilename = true
continue@codeBlock
}
if (inFilename) {
filename.append(nextLanguage)
} else {
language.append(nextLanguage)
}
}
tokens.add(CodeBlockLanguage(language.toString(), filename.toString()))
}
}
}
} else if (iterator.peekOrNull() == null) {
tokens.add(Text(codeBlockBuilder.toString()))
}
} else {
val codeBuilder = StringBuilder()
while (iterator.hasNext() && iterator.peekOrNull() != next) {
codeBuilder.append(iterator.next())
}
if (iterator.hasNext() && iterator.next() == next) { //インラインコードブロックかと思ったら違った
tokens.add(InlineCodeBlock(codeBuilder.toString()))
} else {
tokens.add(Text(codeBuilder.insert(0, next).toString()))
}
}
inCode = codeblock(iterator, next, tokens, inCode, codeBuffer, inline)
}
inCode -> {
codeBuffer.append(next)
}
next == '#' || next == '' -> header(iterator, tokens)
(next == '>' || next == '') && !inQuote -> {
(next == '#' || next == '') && !inline -> header(iterator, tokens)
(next == '>' || next == '') && !inQuote && !inline -> {
inQuote = true
quote(iterator, tokens)
}
next == '-' || next == '=' || next == 'ー' || next == '' -> {
(next == '-' || next == '=' || next == 'ー' || next == '') && !inline -> {
if (iterator.peekOrNull()?.isWhitespace() == true) { //-の直後がスペースならリストの可能性
list(iterator, tokens)
} else {//それ以外ならセパレーターの可能性
@ -99,7 +44,7 @@ class Lexer {
}
}
next in '0'..'9' || next in ''..'' ->
(next in '0'..'9' || next in ''..'') && !inline ->
decimalList(iterator, tokens, next)
next == '[' || next == '「' -> tokens.add(SquareBracketStart)
@ -133,6 +78,9 @@ class Lexer {
}
}
}
if (!inline && tokens.lastOrNull() !is Whitespace) { //行頭が空白の場合は一旦無視する
inline = true
}
}
@ -157,6 +105,76 @@ class Lexer {
return tokens
}
private fun codeblock(
iterator: PeekableCharIterator,
next: Char,
tokens: MutableList<Token>,
inCode: Boolean,
codeBuffer: StringBuilder,
inline: Boolean
): Boolean {
var inCode1 = inCode
if (iterator.peekOrNull() == next && !inline) { //行頭かつ次の文字が`
val codeBlockBuilder = StringBuilder()
codeBlockBuilder.append(next)
codeBlockBuilder.append(iterator.next())
if (iterator.peekOrNull() == next) {
codeBlockBuilder.append(iterator.next())
if (iterator.peekOrNull() == next) {
tokens.add(Text(codeBlockBuilder.toString()))
} else {
if (inCode1) {
inCode1 = false
tokens.add(CodeBlock(codeBuffer.toString().trimStart('\n').trimEnd('\n')))
codeBuffer.clear()
} else {
inCode1 = true
var inFilename = false
val language = StringBuilder()
val filename = StringBuilder()
if (iterator.hasNext()) {
codeBlock@ while (iterator.hasNext()) {
val nextLanguage = iterator.next()
if ((nextLanguage == ':' || nextLanguage == '') && !inFilename) {
inFilename = true
continue@codeBlock
}
if (inFilename) {
filename.append(nextLanguage)
} else {
language.append(nextLanguage)
}
}
tokens.add(CodeBlockLanguage(language.toString(), filename.toString()))
}
}
}
} else if (iterator.peekOrNull() == null) {
tokens.add(Text(codeBlockBuilder.toString()))
}
} else {
val codeBuilder = StringBuilder()
while (iterator.hasNext() && iterator.peekOrNull() != next) {
codeBuilder.append(iterator.next())
}
if (iterator.hasNext() && iterator.next() == next) { //インラインコードブロックかと思ったら違った
if (codeBuilder.isEmpty()) {
tokens.add(Text("$next$next"))
} else {
tokens.add(InlineCodeBlock(codeBuilder.toString()))
}
} else {
tokens.add(Text(codeBuilder.insert(0, next).toString()))
}
}
return inCode1
}
private fun asterisk(
iterator: PeekableCharIterator,
next: Char,

View File

@ -29,3 +29,4 @@ data class UrlTitle(val title: String) : Token()
data class InlineCodeBlock(val text: String) : Token()
data class CodeBlock(val text: String) : Token()
data class CodeBlockLanguage(val language: String, val filename: String) : Token()
data object Tilde : Token()

View File

@ -760,4 +760,49 @@ class LexerTest {
), actual
)
}
@Test
fun 唐突のヘッダー() {
val lexer = Lexer()
val actual = lexer.lex("aiueo #a")
println(actual)
assertContentEquals(
listOf(
Text("aiueo"), Whitespace(1, ' '), Text("#a")
), actual
)
}
@Test
fun 唐突のリスト() {
val lexer = Lexer()
val actual = lexer.lex("aiueo - a")
println(actual)
assertContentEquals(
listOf(
Text("aiueo"), Whitespace(1, ' '), Text("-"), Whitespace(1, ' '), Text("a")
), actual
)
}
@Test
fun 唐突のコードブロック() {
val lexer = Lexer()
val actual = lexer.lex("aiueo ```abcd")
println(actual)
assertContentEquals(
listOf(
Text("aiueo"), Whitespace(1, ' '), Text("```abcd")
), actual
)
}
}