行頭以外にヘッダー等があった場合にヘッダーとして認識されてしまう問題を修正
This commit is contained in:
parent
985df8ed82
commit
6659720f2c
|
@ -7,91 +7,36 @@ class Lexer {
|
||||||
val tokens = mutableListOf<Token>()
|
val tokens = mutableListOf<Token>()
|
||||||
val lines = PeekableStringIterator(input.lines())
|
val lines = PeekableStringIterator(input.lines())
|
||||||
|
|
||||||
var inQuote = false
|
var inQuote = false //引用中の判断
|
||||||
var inCode = false
|
var inCode = false //コードブロック内の判断
|
||||||
|
var inline = false //行頭の判断
|
||||||
val codeBuffer = StringBuilder()
|
val codeBuffer = StringBuilder()
|
||||||
|
|
||||||
line@ while (lines.hasNext()) {
|
line@ while (lines.hasNext()) {
|
||||||
|
inline = false //改行時にリセット
|
||||||
if (lines.peekOrNull() == "") {
|
if (lines.peekOrNull() == "") {
|
||||||
blankLine(lines, tokens)
|
blankLine(lines, tokens)
|
||||||
} else {
|
} else {
|
||||||
val line = lines.next()
|
val line = lines.next()
|
||||||
|
|
||||||
val iterator = PeekableCharIterator(line.toCharArray())
|
val iterator = PeekableCharIterator(line.toCharArray())
|
||||||
char@ while (iterator.hasNext()) {
|
char@ while (iterator.hasNext()) {
|
||||||
val next = iterator.next()
|
val next = iterator.next()
|
||||||
when {
|
when {
|
||||||
next == '`' || next == '`' -> {
|
next == '`' || next == '`' -> {
|
||||||
//todo ````` のようなやつが来たときのことを考える
|
inCode = codeblock(iterator, next, tokens, inCode, codeBuffer, inline)
|
||||||
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 -> {
|
inCode -> {
|
||||||
codeBuffer.append(next)
|
codeBuffer.append(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
next == '#' || next == '#' -> header(iterator, tokens)
|
(next == '#' || next == '#') && !inline -> header(iterator, tokens)
|
||||||
(next == '>' || next == '>') && !inQuote -> {
|
(next == '>' || next == '>') && !inQuote && !inline -> {
|
||||||
inQuote = true
|
inQuote = true
|
||||||
quote(iterator, tokens)
|
quote(iterator, tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
next == '-' || next == '=' || next == 'ー' || next == '=' -> {
|
(next == '-' || next == '=' || next == 'ー' || next == '=') && !inline -> {
|
||||||
if (iterator.peekOrNull()?.isWhitespace() == true) { //-の直後がスペースならリストの可能性
|
if (iterator.peekOrNull()?.isWhitespace() == true) { //-の直後がスペースならリストの可能性
|
||||||
list(iterator, tokens)
|
list(iterator, tokens)
|
||||||
} else {//それ以外ならセパレーターの可能性
|
} else {//それ以外ならセパレーターの可能性
|
||||||
|
@ -99,7 +44,7 @@ class Lexer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next in '0'..'9' || next in '0'..'9' ->
|
(next in '0'..'9' || next in '0'..'9') && !inline ->
|
||||||
decimalList(iterator, tokens, next)
|
decimalList(iterator, tokens, next)
|
||||||
|
|
||||||
next == '[' || next == '「' -> tokens.add(SquareBracketStart)
|
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
|
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(
|
private fun asterisk(
|
||||||
iterator: PeekableCharIterator,
|
iterator: PeekableCharIterator,
|
||||||
next: Char,
|
next: Char,
|
||||||
|
|
|
@ -29,3 +29,4 @@ data class UrlTitle(val title: String) : Token()
|
||||||
data class InlineCodeBlock(val text: String) : Token()
|
data class InlineCodeBlock(val text: String) : Token()
|
||||||
data class CodeBlock(val text: String) : Token()
|
data class CodeBlock(val text: String) : Token()
|
||||||
data class CodeBlockLanguage(val language: String, val filename: String) : Token()
|
data class CodeBlockLanguage(val language: String, val filename: String) : Token()
|
||||||
|
data object Tilde : Token()
|
|
@ -760,4 +760,49 @@ class LexerTest {
|
||||||
), actual
|
), 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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue