/tmp/duangsuse.sock
23 subscribers
303 photos
3 videos
92 files
337 links
从 duangsuse::Echo (@dsuse) 跟进出来的分支,将在作者恢复原帐号访问的时候合并删除。
Download Telegram
那是不比较代码本身的,然后我们再对比一下两者的定义:

== 加减乘除运算符 ==
val ops = KeywordPattern<InfixOp<Number>>().apply {
listOf("*", "乘").forEach { register(it infixl 0 join fn(Int::times)) }
listOf("/", "除以").forEach { register(it infixl 0 join fn(Int::div)) }
register("除" infixl 0 join flip(fn(Int::div)))
register("分" infixl 0 join { a: Number, b: Number-> (a.toDouble() / b.toDouble()) as Number })
listOf("+", "加") .forEach { register(it infixl 1 join fn(Int::plus)) }
listOf("-", "减") .forEach { register(it infixl 1 join fn(Int::minus)) }
}

ParserKt 费了 7 行定义完了所有出现的运算符,并且包含它们的优先级信息。

const Plus = createToken({
name: "Plus",
pattern: /\+/,
categories: AdditionOperator
}) //...

SAP/chevrotain 单单定义加减乘除长啥样,就用掉 24 行代码
并且,它还把加减法都归类为 AdditionalOperator…… 显然对数学运算符,有没有对称性会比这种优先级上的工作有意义。
const AdditionOperator = createToken({
name: "AdditionOperator",
pattern: Lexer.NA
})

至于左递归 additionExpression, multiplicationExpression 就没啥可说了,毕竟属于解析器框架提供接口上的区别,这些定义用掉 18 行。

再说两个细节,数字读取和括号表达式。

$.RULE("parenthesisExpression", () => {
$.CONSUME(LParen)
$.SUBRULE($.expression)
$.CONSUME(RParen)
})

star 很多的框架写了 5 行

val atomParen = SurroundBy(parens.toCharPat(), Deferred{expr})
ParserKt 只需写一行就完了,加上错误处理也只用一行。

const NumberLiteral = createToken({
name: "NumberLiteral",
pattern: /[1-9]\d*/
})
$.RULE("atomicExpression", () => {
$.OR([/**/{ ALT: () => $.CONSUME(NumberLiteral) }, ...
单单定义 NumberLiteral 叫什么名字、长什么样子,就写了 4 行

val digit = digitFor('0'..'9')
val zeroNotation = Decide(
StickyEnd(item('0') or EOF, 0) { if (octal.test(peek)) error("no octal notations"); -1 }
)
val numPart = Contextual<Char, Int, Int>(digit) {
if (it == 0) zeroNotation
else Repeat(asInt(10, it), digit).Many()
}.discardFirst()
带上可以继续扩展/抽提的结构和直接数值读取,8 行
如果说完全等价的,Contextual(digitFor('1'..'9')) { Repeat(asInt(10, it), digitFor('0'..'9')) }.ignoreFirst(),1 行。
一支 爷爷 跳了 我 给 校长 孔雀 舞
Forwarded from Deleted Account
现在 ParserKt 可以在 100 行左右完成 JSON 的 read / show 了 🤔
Forwarded from Deleted Account
虽然我一直有在优化项目的接口易用性,但感觉还是有些“小乌云”……
顺序、直到、重复、选择 (SURD) 这些大结构没有变
单项、含于、符合 (IES) 的最后一个字母我给它加上了 StickyEnd,也算是解决了 Feed 模型的一个不被重视的问题
模式、正模式、可选模式、修饰模式 (PPOP) 我最后削掉了那个 PositivePattern
现在又变成了 POPCorn (都是助记的) 算是把 ConstantPattern 给合并到了基本模型里,方便 skip whitespaces 的 Pattern 的 rebuild。
Forwarded from Deleted Account
突然觉得我可以“汉化”一下,然后投稿给汉语编程,哈哈哈 😂
https://sap.github.io/chevrotain/docs/tutorial/step1_lexing.html#introduction
SELECT column1 FROM table2
SELECT name, age FROM persons WHERE age > 100

val letter = elementIn('a'..'z', 'A'..'Z') or item('_')
// [a-zA-Z]\w*
val name = Seq(::StringTuple, letter.toStringPat(), stringFor(letter or digit))
// 0|[1-9]\d*
val int = Contextual

sealed class SQL {
// SELECT {Name} FROM Name (WHERE Expr)?
class Select(val columns: List<String>, val table: String, val filter: Where?): SQL()
class Where(val expr: Expr)
}

val keyword = KeywordPattern<Pattern<Char, >


草写不出来了
https://tomassetti.me/parsing-in-javascript/#chevrotain

sealed class Token {
data class NamedConst(val name: String): Token()
}

fun KeywordPattern<Token>.mergeConsts(vararg names: String) = names.forEach { this[it] = Token.NamedConst(it) }
val namedConsts = KeywordPattern<Token>().apply {
mergeConsts("true", "false", "null")
}
val namedEscape = mapOf('b' to '\b', 't' to '\t', 'n' to '\n', 'r' to '\r', 'f' to '\u000C', 'v' to '\u000B')
val stringChar = Decide(!elementIn('"', '\n', '\\'), MapPattern(namedEscape) prefix item('\\')).mergeFirst {
if (it in namedEscape.values) 1 else 0 }
val string = SurroundBy(item('"') to item('"').clam {"unterminated string"}, stringFor(stringChar))
/tmp/duangsuse.sock
https://tomassetti.me/parsing-in-javascript/#chevrotain sealed class Token { data class NamedConst(val name: String): Token() } fun KeywordPattern<Token>.mergeConsts(vararg names: String) = names.forEach { this[it] = Token.NamedConst(it) } val namedConsts…
ParserKt 是 scannerless parsing 框架,目前对这样传统的 lexer-parser 结构 支持还不完善……

其实也可以用 iterator input,但着实是没好的 Pattern 去支持即时更新的 iterator
This media is not supported in your browser
VIEW IN TELEGRAM
我去专门独立于 Repeat pattern,写一个这种输入吧。
我把这个“封建余孽”加回来了…… 难道就是为了显示 ParserKt 很有灵活性么
class BoolScanner(feed: Feed<Char>): LexerFeed<Int>(feed) {
override fun tokenizer() = KeywordPattern<Int>().apply { mergeStrings("true" to 1, "false" to 0) }
override val eof = -1
}
这么说来,其实在这无聊的时候试着让 ParserKt 能在简洁性上吊打其他的框架,才是最有意思的事情吧……
This media is not supported in your browser
VIEW IN TELEGRAM
真的已经分不清框架的核心和外设了…… 都写在一个文件里 没有规矩