const filter = browser.webRequest.filterResponseData(e.requestId);
let buffers = null; //result
// onNext, onDone, onError
filter.ondata = (event) => {
const data = event.data;
if (buffers === null) {
buffers = new Uint8Array(data); return;
}
const buffer = new Uint8Array(buffers.byteLength + data.byteLength);
//v 将响应分段数据收集拼接起来,在完成加载后整体替换。这可能会改变浏览器接收数据分段渲染的行为。
buffer.set(buffers); buffer.set(new Uint8Array(data), buffers.buffer.byteLength);
buffers = buffer;
}
filter.onstop = () => {
if (buffers === null) { filter.close(); return; }
for (let item of rule) { //< 缓存实例,减少开销
const encoding = item.encoding || "UTF-8";
try {
const _text = this._textDecode(encoding, buffers.buffer);
const text = item._func(_text, detail);
if (typeof text === 'string' && text !== _text) { buffers = this._textEncode(encoding, text); }
} catch (e) { console.error(e); }
}
filter.write(buffers.buffer);
buffers = null;
filter.close();
}
filter.onerror = () => { buffers = null; }
duangsuse::Echo
const filter = browser.webRequest.filterResponseData(e.requestId); let buffers = null; //result // onNext, onDone, onError filter.ondata = (event) => { const data = event.data; if (buffers === null) { buffers = new Uint8Array(data); return; } …
首先我们来学习第一个模式,拼接
注意
(Uint8Array 是
然后是 filter 的 Observer,Observer 是一种利用副作用的模式,它有 onNext, onFinish, onError 三个 slot (message handler)。
顺带提及一下
我们在
consume/filter.write/close
然后,
无论怎么看,滥用 try 都是不对的。首先一条是 better flat than deep,不重视代码嵌套深度的人是写不出容易维护的代码的(因为深的嵌套层次往往意味着存在模板化的代码或者过度设计)
如果已经在
使用 ES6 还有一种骚一点的写法:
当然,其实
是一个通用模式——线性查找(民科私货知识注意!)
我们也可以用
Uint8Array fun concat(buf0, buf1) {
let buf01 = new Uint8Array(buf0.byteLength+buf1.byteLength);
buf01.set(buf0, 0); buf01.set(buf1, buf0.byteLength);
return buf01;
} 注意
buffers = buffer; 那一段的类型正确性: buffer 没有 it.buffer.byteLength? 其实用 it.buffer.byteLength 或 it.byteLength 在这里是一样的。(Uint8Array 是
TypedArray, it.buffer 是 ArrayBuffer)然后是 filter 的 Observer,Observer 是一种利用副作用的模式,它有 onNext, onFinish, onError 三个 slot (message handler)。
const filter = browser.webRequest.filterResponseData(e.requestId);
/* filter.ondata = (event) => {}; filter.onstop = () => {}; filter.onerror = () => {}; */ 顺带提及一下
let buffers = null; 作为数据依赖的合理性,是合理的,毕竟我们要在 ondata 里拼接这个 buffer。 这就好像整个 filterReponseData 的程序是某种意义上的 Array,它必须有一个私有属性以存储状态一样。我们在
filter.onstop 里添加了后继逻辑,当然这整个也可以被抽提出来(作为一个类,暴露抽象的 onRequestData(mergedBuffer) 即可)filter.onstop = () => {
if (buffers === null) { filter.close(); return; }
onRequestData(buffers.buffer);
filter.write(buffers.buffer);
buffers = null; //< 这个 dispose 必须要做,可没 GC 帮你削对象了
filter.close();
}
大体流程就是处理(consume),尾操作(index+=1 那一类的,这里是 filter.write(buffers.buffer)),和 close()。consume/filter.write/close
然后,
onRequsetData(buffer) {
for (let item of rule) { //< 缓存实例,减少开销
try { //< should be removed
const text = this._textDecode(item.encoding, buffer);
const replaced = item._func(text, detail);
if (typeof replaced === "string" && replaced !== text) { buffers = this._textEncode(encoding, text); }
} catch (e) { console.error(e); }
}
} 无论怎么看,滥用 try 都是不对的。首先一条是 better flat than deep,不重视代码嵌套深度的人是写不出容易维护的代码的(因为深的嵌套层次往往意味着存在模板化的代码或者过度设计)
如果已经在
_textDecode 和 _textEncode 里做好了错误处理,一般而言无论对变量还是对控制流都要采取最小化作用域原则,能精确的就不要把整个子程序都套到 try 里去。使用 ES6 还有一种骚一点的写法:
processRequestData(buffer) {
for (let { encoding, func } of rules) {
let text = this.decodeText(buffer)
let replaced = func(text);
if (typeof replaced == "string" && replaced !== text) return this.encodeText(replaced);
}
return buffer;
} 当然,其实
for (let x in xs) {
if (p(x)) return f(x);
}
return f1(); 是一个通用模式——线性查找(民科私货知识注意!)
我们也可以用
kotlin.sequences 重写一遍:return rules.mapNotNull { rule ->
val text = decode(buffer, rule.encoding)
rule.func(text).takeIf { it != text }
}.firstOrNull() ?: buffer
duangsuse::Echo
首先我们来学习第一个模式,拼接 Uint8Array fun concat(buf0, buf1) { let buf01 = new Uint8Array(buf0.byteLength+buf1.byteLength); buf01.set(buf0, 0); buf01.set(buf1, buf0.byteLength); return buf01; } 注意 buffers = buffer; 那一段的类型正确性: buffer 没有 it.buffer.byteLength? 其实用…
better flat than deep 之所以是一个原则,是因为若不作为原则,它也不是不可替代的选择,例如
较浅的块嵌套层次对好的代码很重要,比如 Promise——把 callback 和 error handling 结合在一起,如果我们用
对于代码来说,一致性也是相当重要的——所以当你开始降低嵌套层次,就会更容易和其它浅嵌套的部分契合一些,也更容易发现可以重构的一些细节。
我们来看看一个更广义的 if:(下文 f() f1() 是对立的两条分支, cont() 是 continued block 的意思)
如果我们用线性指令流来表达:
这种 if/else 的结构没法用 early-return 改写,举个例子:
不过,如果有可能还是得做进一步封装,比如 C++ 里如果 cont() 仅包含对资源的 close(),你就可以用 RAII(resource acquire is actual initialization) 技术,它能够在离开局部作用域 (return, ...) 之前自动回收资源。
if (p) {
f();
}
cont();
当然也可以写成if (!p) return; // 或者 break 之类能够跳过下面的 else branch 的东西但是,语义上条件执行并不能证明
f();
cont();
if (p) f(); 或者 if (!p) { return; } f(); 是唯一的正确选项。较浅的块嵌套层次对好的代码很重要,比如 Promise——把 callback 和 error handling 结合在一起,如果我们用
onOver/onDone 加 if (res.error) else …… 那代码还能看么?对于代码来说,一致性也是相当重要的——所以当你开始降低嵌套层次,就会更容易和其它浅嵌套的部分契合一些,也更容易发现可以重构的一些细节。
我们来看看一个更广义的 if:(下文 f() f1() 是对立的两条分支, cont() 是 continued block 的意思)
if (p) {
f();
} else {
f1();
}
cont(); 如果我们用线性指令流来表达:
(br.not b) a (jmp c) b c或者
(br.if a) b (jmp c) a c其实控制流图上,c 一般被称为 merge block…… 这里就用
cont() 代表了:P这种 if/else 的结构没法用 early-return 改写,举个例子:
if (!p) return; f(); cont();但是 if (!p) return 的 false branch 不包括 cont 就是了,如果 dirty 一点可以随意 jump (goto),但不建议。
(test p) (br.not return) (call f) (call cont) return
if (p) f(); else f1();
(test p) (br.not f1) (call f) (jmp cont) f1 (call f1) cont
不过,如果有可能还是得做进一步封装,比如 C++ 里如果 cont() 仅包含对资源的 close(),你就可以用 RAII(resource acquire is actual initialization) 技术,它能够在离开局部作用域 (return, ...) 之前自动回收资源。
#phonetic https://zh.wikipedia.org/wiki/%E5%85%83%E9%9F%B3 vowel 是『元音』的意思 🤔
Wikipedia
元音
母音(vowel)又称母音,是音位的一种,与子音(又稱子音)相对。元音是在发音过程中由气流通过口腔而不受阻碍发出的音。发元音时,气流从肺部通过声门冲击声带,使声带发出均匀震动,然后震音气流不受阻碍地通过口腔、鼻腔,通过舌、唇的调节而发出不同的声音。发元音时声带必然震动,这叫做浊音。也有的语言发元音时声带不振动,发出清元音(voiceless vowel)。元音并非在所有的语言中都与元音字母一一对应,一些元音字母的组合也可以表示特殊的元音,比如汉语中的某些複韵母;某些语言中一些元音字母在某些情况下不发音,如法语中元音字母“e”在词末。
https://github.com/dhakehurst/net.akehurst.language/blob/efafa3e57f89ddea2f81ca3080121f2e70cce9e0/net.akehurst.language/agl-processor/src/commonMain/kotlin/processor/Agl.kt#L57
草,学到了,原来 Kotlin/Multiplatform 重载要这么用
草,学到了,原来 Kotlin/Multiplatform 重载要这么用
GitHub
dhakehurst/net.akehurst.language
Generic Language (DSL) support for kotlin multiplatform (parser, syntax-analyser, formatter, processor, etc) - dhakehurst/net.akehurst.language
https://github.com/dhakehurst/net.akehurst.language/blob/efafa3e57f89ddea2f81ca3080121f2e70cce9e0/net.akehurst.language/agl-processor/src/commonMain/kotlin/processor/LanguageProcessorDefault.kt#L94
草,觉得可以学习一下,自带补齐方法
草,觉得可以学习一下,自带补齐方法
override fun expectedAt(goalRuleName: String, inputText: CharSequence, position: Int, desiredDepth: Int): List<CompletionItem>
override fun expectedAt(inputText: CharSequence, position: Int, desiredDepth: Int): List<CompletionItem>GitHub
dhakehurst/net.akehurst.language
Generic Language (DSL) support for kotlin multiplatform (parser, syntax-analyser, formatter, processor, etc) - dhakehurst/net.akehurst.language
https://github.com/dhakehurst/net.akehurst.language/blob/efafa3e57f89ddea2f81ca3080121f2e70cce9e0/net.akehurst.language/agl-processor/src/commonMain/kotlin/parser/scannerless/ScannerlessParser.kt#L130
看到这里我被宏大的一个
看到这里我被宏大的一个
fun 给震惊到了,忽然想到为什么 ParserKt 不能 lazy 处理 SourceLocation 的问题,才记起 ParserKt 的输入是纯 Feed<T> 流,不可能回溯……GitHub
dhakehurst/net.akehurst.language
Generic Language (DSL) support for kotlin multiplatform (parser, syntax-analyser, formatter, processor, etc) - dhakehurst/net.akehurst.language
https://github.com/dhakehurst/net.akehurst.language/blob/efafa3e57f89ddea2f81ca3080121f2e70cce9e0/net.akehurst.language/agl-processor/src/commonMain/kotlin/parser/scannerless/InputFromCharSequence.kt
看到这个“新”Parser lib 我就知道 ParserKt 的设计是有多不容易了……
遇到 parser 既没有 lexer/parser 又没有 TERMINAL/NON-TERMINAL 还不强调scannerless因为它本身概念上就不存在scanner这…… 虽然和 Haskell 系的 Parserc 差不多,但更 Kotlin 一些
如今又得重构了……
看到这个“新”Parser lib 我就知道 ParserKt 的设计是有多不容易了……
遇到 parser 既没有 lexer/parser 又没有 TERMINAL/NON-TERMINAL 还不强调scannerless因为它本身概念上就不存在scanner这…… 虽然和 Haskell 系的 Parserc 差不多,但更 Kotlin 一些
如今又得重构了……
GitHub
dhakehurst/net.akehurst.language
Generic Language (DSL) support for kotlin multiplatform (parser, syntax-analyser, formatter, processor, etc) - dhakehurst/net.akehurst.language
https://github.com/dhakehurst/net.akehurst.kotlin.kt2ts #Kotlin #TypeScript #dev #tools 🤔 第一次知道可以这么干
看来 ParserKt 的 Kotlin/JS document 要靠这个人的实践经验了
看来 ParserKt 的 Kotlin/JS document 要靠这个人的实践经验了
GitHub
GitHub - dhakehurst/net.akehurst.kotlin.kt2ts: Typescript definition file generator for kotlin (js) classes
Typescript definition file generator for kotlin (js) classes - dhakehurst/net.akehurst.kotlin.kt2ts
https://github.com/dhakehurst/net.akehurst.language/blob/efafa3e57f89ddea2f81ca3080121f2e70cce9e0/net.akehurst.language/agl-processor/src/commonMain/kotlin/parser/sppt/SharedPackedParseTreeDefault.kt
这一系列操作…… 很算法啊
一般类似 ANTLR 一样的工具也会有 rewrite/action 的选择吧,可这个几乎只有 SPPT(shared packed parse tree) 一样……
ParserKt 是纯递归下降解析组合子,所以没有 YaCC 系的 Parser-Tree 说法,只有内部递归的调用树和临时的 Fold 对象,没有这种……
这一系列操作…… 很算法啊
一般类似 ANTLR 一样的工具也会有 rewrite/action 的选择吧,可这个几乎只有 SPPT(shared packed parse tree) 一样……
ParserKt 是纯递归下降解析组合子,所以没有 YaCC 系的 Parser-Tree 说法,只有内部递归的调用树和临时的 Fold 对象,没有这种……
GitHub
dhakehurst/net.akehurst.language
Generic Language (DSL) support for kotlin multiplatform (parser, syntax-analyser, formatter, processor, etc) - dhakehurst/net.akehurst.language