#daily 见到了一些非常简单的逻辑,但是居然算法就无法立刻想出来...
比如三个并排 LED 的『进度』提示
然后这个循环可以包装一下,成为 Coroutine 或者 Thread 模式的...
和 MDWiki 的 未读提示
比如三个并排 LED 的『进度』提示
while true
all_lights on
sleep k
light_r off
sleep k
light_c off
sleep k
light_l off
sleep k
continue
然后这个循环可以包装一下,成为 Coroutine 或者 Thread 模式的...
和 MDWiki 的 未读提示
routine update
if view_top < element_pos.low_y
make_gray view
duangsues.is_a? SaltedFish
#daily 见到了一些非常简单的逻辑,但是居然算法就无法立刻想出来... 比如三个并排 LED 的『进度』提示 while true all_lights on sleep k light_r off sleep k light_c off sleep k light_l off sleep k continue 然后这个循环可以包装一下,成为 Coroutine 或者 Thread 模式的... 和 MDWiki 的 未读提示 routine update…
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from dnaugsuz
LLVM 做得也挺多的...
JIT 编译是很耗时的,虽然词法分析和语法分析之前都做了,bitcode 到机器代码要经过大得吓人的工序,数十个 Pass... 分析 Pass 和优化 Pass 都比较花时间
循环优化和启发式余赘指令合并、各种图操作、寄存器选择大概都是比较花时间的吧
AOT 就基本不用再多花时间做这些了
PassManager 要加载一大堆不是完全无关的 Pass,这些 Pass 还要初始化...JIT 编译是很耗时的,虽然词法分析和语法分析之前都做了,bitcode 到机器代码要经过大得吓人的工序,数十个 Pass... 分析 Pass 和优化 Pass 都比较花时间
循环优化和启发式余赘指令合并、各种图操作、寄存器选择大概都是比较花时间的吧
AOT 就基本不用再多花时间做这些了
LineTerminator = \r|\n|\r\n
InputCharacter = [^\r\n]
TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "*/"
EndOfLineComment = "//" {InputCharacter}* {LineTerminator}?
Comment = {TraditionalComment} | {EndOfLineComment}
%x YYINITIAL
%s STRING CHAR
%%
<YYINITIAL> {
{Comment} { }
\" { yybegin(STRING); }
\' { yybegin(CHAR); }
}
<STRING>("\"") { yybegin(YYINITIAL); printf("\""); }
<CHAR>("\'") { yybegin(YYINITIAL); printf("'"); }
<<EOF>> {}
%%
int main(int argc, const char *argv[]) {
if (argc > 1)
yyin = fopen(argv[1], "r");
else
yyin = stdin;
yylex();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
static int lex(const char *YYCURSOR, const char *YYLIMIT) {
const char *YYMARKER;
/*!re2c
re2c:define:YYCTYPE = char;
re2c:define:YYFILL:naked = 1;
re2c:define:YYFILL = "return 0;";
end = "\x00";
bin = '0b' [01]+;
oct = "0" [0-7]*;
dec = [1-9][0-9]*;
hex = '0x' [0-9a-fA-F]+;
* { printf("a"); }
bin end { printf("a"); }
oct end { printf("a"); }
dec end { printf("a"); }
hex end { printf("a"); }
*/
return -1;
}
int main(int argc, const char *argv[]) {
if (argc > 1) {
FILE *lexf = fopen(argv[1], "rb");
if (lexf == NULL) { perror("Cannot open file"); exit(-1); };
fseek(lexf, 0, SEEK_END);
const size_t fsize = (size_t) ftell(lexf);
fseek(lexf, 0, SEEK_SET);
char *buffer = (char*) malloc(fsize);
fread(buffer, 1, fsize, lexf);
int result = lex(buffer, buffer + fsize);
free(buffer);
fclose(lexf);
return result;
} else {
fprintf(stderr, "Streaming is not supported\n");
}
}
如果可能的话,我愿意尝试 Kotlin/Multiplatform 来解决这两个,不过对 JS 来说其实 Kt 里必须写的东西其实不必要,比如数值类型自动提升、Number 对象操作等
ApkBundler 是我打算彻底独立于 GeekApk 和 GitApk 的作品,设计上会恢复之前 GeekApk 早期的大部分设计、包括依赖系统
我不知道我打不打算真正把它当一个 “商业项目” 运作,当然即使商业也只是商业标准,个人兴趣还是主打(
(当然,如果是商业项目的话,首先必须等我高中毕业了,其次,它得符合天朝变态的审查制度,但它明显会成为最良心的国产软件,而且像极了以前的酷安
(当然,我不会“借鉴”现在的酷安,它只会面向发烧友,至少是我运行的时候
(当然,我保存了之前所有真正有价值的设计文档,所以基本不会丢失好的设计,包括 PM(私信)也会加上
(当然,之前我貌似在 @dsuse 上发过一些要加的东西
ApkBundler Scheme 可能是,大家都知道很多极具扩展性的、著名的、Geek 的软件都拥有自己的脚本语言来扩展自己的功能(比如,Emacs/Emacs Lisp、Vim/VimScript)
ApkBundler 的 Android 客户端和 Web 客户端当然会支持 Java Class 文件插件和 JavaScript 插件,但是主打依然是 ABS(ApkBundler Scheme) 和 Kotlin/ES5 混合开发
ApkBundler Scheme
UpValue 由于 JVM 已有 GC 且闭包不需如此实现不需要使用
tailrec heredoc constarg-folding string-templating
exception square brackets call-by-need
BreakLexer
lex-features comment break binop(right left all) keyword
current(frame)
stack
.gotofast1
.gotofast2
.stop
.yield
.wait
.goto
.value
tailrec
.
varargs apend eval pure noreturn noarg onearg twoarg
org.apkbundler.scheme
text/
Lexer
BreakLexer
(SubSequence)Parser
HeapParser
Token
TokenType
TokenStream
SexpStream
Combinator
type/
Symbol
Reference
Chunk
Closure
Sexp
SexpList
SexpAtom
AbsStack
AbsFrame
port/
ListImpl
HashImpl
StringImpl
StringBuilderImpl
ReflectImpl
error/
Error
LexerError
ParserError
ReferenceError
TypeError
ArgumentError
Errno
ReflectError
ControlFlowTransfer
front/
Main
SchemeDebugger
SchemeServer
SchemeRepl
utils/
Coroutine
SchemeThread
Future
AbsClassLoader
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/BshClassLoader.java)AbsClassPath
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/BshClassPath.java)AbsClassManager
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/ClassManagerImpl.java)AutoCloseable
SchemeJava
NumericOperations
AbsTyper
Stdlib
Scheme
基本数据类型
nil
list
chunk / closure
ref
array
pair
vector
map
enum
promise
fiber
date
行注释
p.s. 冰封波博客上那个字符串 escape 失败不报错作为标识符的真是奇妙
展开规则:
对任何宏:检查参数列表长度,对所有参数 sexp 列表递归展开并对标识符解引用,然后填充宏参数递归展开,如果是 varargs 宏就直接将求值后的列表填充到 varargs 参数里
对
对
插件:
1. 取值 hook
2. 找不到变量 proc
3. SEND handler
4. 错误 hook
作用域:
全局变量
全局变量
不过它是惰性求值的
Lime 使用嵌套作用域
全局变量
"Lime 的「六个展开」"
1.
否则,尝试全局的
这所谓「六个展开」其实可以归纳为三类
1. 宏展开
Lime 基于宏和自省功能来实现类似 Scheme 的语法,宏最终被展开为原生调用执行
Apply(施用宏)过程中,S 表达式首项必须是一个能被解析为宏的对象
而其后皆为宏的参数,如果参数长度不对,抛出 Arity Mismatch,除非是 vararg 宏
2. 原生调用
Lime 使用原生调用赋予拥有反射能力的 JVM 方便执行动态动作的能力
handlerid 其后皆为宏的参数,如果参数长度不对,抛出 Arity Mismatch,除非是 vararg 接收器
3. 无展开
不进行展开,直接返回原对象
ApkBundler 是我打算彻底独立于 GeekApk 和 GitApk 的作品,设计上会恢复之前 GeekApk 早期的大部分设计、包括依赖系统
我不知道我打不打算真正把它当一个 “商业项目” 运作,当然即使商业也只是商业标准,个人兴趣还是主打(
(当然,如果是商业项目的话,首先必须等我高中毕业了,其次,它得符合天朝变态的审查制度,但它明显会成为最良心的国产软件,而且像极了以前的酷安
(当然,我不会“借鉴”现在的酷安,它只会面向发烧友,至少是我运行的时候
(当然,我保存了之前所有真正有价值的设计文档,所以基本不会丢失好的设计,包括 PM(私信)也会加上
(当然,之前我貌似在 @dsuse 上发过一些要加的东西
ApkBundler Scheme 可能是,大家都知道很多极具扩展性的、著名的、Geek 的软件都拥有自己的脚本语言来扩展自己的功能(比如,Emacs/Emacs Lisp、Vim/VimScript)
ApkBundler 的 Android 客户端和 Web 客户端当然会支持 Java Class 文件插件和 JavaScript 插件,但是主打依然是 ABS(ApkBundler Scheme) 和 Kotlin/ES5 混合开发
ApkBundler Scheme
UpValue 由于 JVM 已有 GC 且闭包不需如此实现不需要使用
tailrec heredoc constarg-folding string-templating
exception square brackets call-by-need
BreakLexer
lex-features comment break binop(right left all) keyword
current(frame)
stack
.gotofast1
.gotofast2
.stop
.yield
.wait
.goto
.value
tailrec
.
varargs apend eval pure noreturn noarg onearg twoarg
string $templating
string $(.value 'templating)
'(string 's)org.apkbundler.scheme
text/
Lexer
BreakLexer
(SubSequence)Parser
HeapParser
Token
TokenType
TokenStream
SexpStream
Combinator
type/
Symbol
Reference
Chunk
Closure
Sexp
SexpList
SexpAtom
AbsStack
AbsFrame
port/
ListImpl
HashImpl
StringImpl
StringBuilderImpl
ReflectImpl
error/
Error
LexerError
ParserError
ReferenceError
TypeError
ArgumentError
Errno
ReflectError
ControlFlowTransfer
front/
Main
SchemeDebugger
SchemeServer
SchemeRepl
utils/
Coroutine
SchemeThread
Future
AbsClassLoader
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/BshClassLoader.java)AbsClassPath
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/BshClassPath.java)AbsClassManager
(https://gitapk.popf.rip/OSS-Mirrors/BeanShell/src/branch/merge-fork-beanshell2/src/main/java/bsh/classpath/ClassManagerImpl.java)AutoCloseable
SchemeJava
NumericOperations
AbsTyper
Stdlib
Scheme
基本数据类型
nil
()
bool #t #f
number 01 0x1 0b1001 0o122 2323N 32323D 2233.223F 66666L 6B
支持 Java 的 Byte Short Int Long Double BigInteger BigDecimal 类型list
(a b c)
string '' "" "\"" "\n" 'string
symbol dsdsd abc identifier
char 'c'
高等数据类型chunk / closure
ref
array
pair
vector
map
enum
promise
fiber
date
行注释
; comment
任何不可识别的都作为标识符p.s. 冰封波博客上那个字符串 escape 失败不报错作为标识符的真是奇妙
展开规则:
对任何宏:检查参数列表长度,对所有参数 sexp 列表递归展开并对标识符解引用,然后填充宏参数递归展开,如果是 varargs 宏就直接将求值后的列表填充到 varargs 参数里
对
^* 宏:检查参数列表长度,然后填充宏参数递归展开,如果是 varargs 宏就直接将求值后的列表填充到 varargs 参数里对
. 宏:检查对应 send handler 参数列表长度,将所有参数 sexp 列表递归展开并对标识符解引用,发送调用到处理 handler插件:
1. 取值 hook
2. 找不到变量 proc
3. SEND handler
4. 错误 hook
作用域:
全局变量
args 在每次递归展开时有效,它是惰性求值的全局变量
__stack 储存了所有作用域表不过它是惰性求值的
Lime 使用嵌套作用域
全局变量
__env 表示整个 Lime 全局表__lime 表示解释器对象"Lime 的「六个展开」"
1.
(symbol arglist)
2. (. handlerid arglist)
3. ((macro sexp) arglist)
4. ()
5. (macro-value arglist)
6. object
额外展开:^# 定义特殊的宏对象,它接受后面所有 S 表达式(1 args) 如果对象拥有 Macro toMacro() 方法,尝试使用 toMacro() 返回的宏否则,尝试全局的
(->macro 1) 返回的宏,如果没有定义直接失败,如果返回的不是宏也失败这所谓「六个展开」其实可以归纳为三类
1. 宏展开
Lime 基于宏和自省功能来实现类似 Scheme 的语法,宏最终被展开为原生调用执行
(symbol arglist)
((macro sexp) arglist)
(macro-value arglist)
(object arglist)「宏解析」允许解析「符号」、S 表达式列表、宏对象、可以转换为宏的对象
Apply(施用宏)过程中,S 表达式首项必须是一个能被解析为宏的对象
而其后皆为宏的参数,如果参数长度不对,抛出 Arity Mismatch,除非是 vararg 宏
2. 原生调用
Lime 使用原生调用赋予拥有反射能力的 JVM 方便执行动态动作的能力
(. handlerid arglist)
Lime 必须有一个手段把宏变为实际行动,这就是 Lime 如此做的途径,“原生”调用handlerid 其后皆为宏的参数,如果参数长度不对,抛出 Arity Mismatch,除非是 vararg 接收器
3. 无展开
不进行展开,直接返回原对象
()
object
Lime 不知道该如何展开这些对象,一般它认为这些对象已经被展开,所以直接返回他们GitApk: Android apps with a cup of tea
BeanShell
Beanshell scripting language
(显然地,我不会刻意抄 Lice 的模式... 比如将 S 表达式作为 AST 处理并且给 Token 加上过多的调试信息(元数据)
不过我要理解它,并刻意把优秀的实践添加上,当然我现在还是很菜,所以连手写计算器可能都不会,接下来怎么办看学习了
不过我要理解它,并刻意把优秀的实践添加上,当然我现在还是很菜,所以连手写计算器可能都不会,接下来怎么办看学习了
Lice 模式实现检查:
Strict syntax
Lisp-style comments
Variables
Basic functions
Compare
Literals
Casts
Functional Programming
Functions are first-class ✅
Call by value ✅
Call by name 可以使用前置宏实现
Call-by-name 宏打算实现
Call by need 可以使用宏前置和本地引用型参数实现
Call by need 宏打算支持
Strict syntax
Lisp-style comments
Variables
Basic functions
Compare
Literals
Casts
Functional Programming
Functions are first-class ✅
(# (a b) (+ a b))Call by value ✅
((# (a b) (+ a b)) 1 2) ; 3Call by name 可以使用前置宏实现
Call-by-name 宏打算实现
Call by need 可以使用宏前置和本地引用型参数实现
Call by need 宏打算支持
新语言就被称为 sexp
打算支持手动尾递归优化、Heredoc 文本、纯函数的折叠、字符串内联代码、异常系统(和解释器内部堆栈)、方括号、动态自定义语法(类似 R6RS 的特定字符序列自动结合,当然用 Trie 树实现比较)
支持 call-by-value(称为 direct)、call-by-name(称为 evaluated)、call-by-need(称为 lazy)
类似 R6RS 的 ' 语法
允许控制流异常
允许
允许
允许使用
函数叫做 Closure,支持 vararg(不定参) append(前置展开) eval(求值时自动展开) lazy(求值时自动展开一次,以后都使用第一次的值) pure(纯函数) noreturn(不会返回)
noarg(无参数) onearg(一个参数) twoarg(两个参数) threearg(三个参数) inline(强制内联) internal(代表内部原生展开器) void(无需返回) noexcept(不会抛出异常) nobind(不是闭包)
optimized(识别方法体所代表的操作,编译时虚拟机江创建优化指令,例如 add)
当然大部分都是没卵用的 flag(
Closure 还可以保存执行状态,可以作为 Fiber 随时挂起和恢复执行
sexp/
text/
Lexer
Parser
Token
Metadata
TokenType
ICharStream
ITokenStream
ISexpStream
type/
Symbol
Reference
Chunk
Binding
Closure
Pair
model/
Sexp
SexpList
SexpAtom
virtual/
SexpVM
Instruction
Compiler
Stack
Frame
port/
ListImpl
HashImpl
StringImpl
StringBufferImpl
ReflectImpl
ArrayImpl
VectorImpl
RealImpl
ComplexImpl
except/
LexError
ParseError
ReferenceError
TypeError
ArgumentError
ReflectError
ControlFlowException
front/
Main
SexpRepl
SexpServer
utils/
Promise
ClassHelper
JavaFFI
NumberOp
Stdlib
api/scripting/
SexpContext
ScriptEngine
EngineFactory
Sexp
执行模式和以前无异,不过这次更不 Functional 了一些(迫真),也不使用作用域优化,只是默认一般没人使用作用域,都是使用 apply
作用域还是函数调用作用域
还是支持 Locals、Globals 取值赋值 Hook、找不到变量 Hook、Send 操作 Hook、异常 Hook、公开作用域列表(也即调用帧)
1.
1. nil
4. list
打算支持手动尾递归优化、Heredoc 文本、纯函数的折叠、字符串内联代码、异常系统(和解释器内部堆栈)、方括号、动态自定义语法(类似 R6RS 的特定字符序列自动结合,当然用 Trie 树实现比较)
支持 call-by-value(称为 direct)、call-by-name(称为 evaluated)、call-by-need(称为 lazy)
类似 R6RS 的 ' 语法
允许控制流异常
.goback 和 .gonext 步进到语句允许
.stop 终止解释和 .yield 立刻执行权交回上层栈帧允许
.value 直接返回值和 tailrec 直接替换调用堆栈的函数调用允许使用
. 调用原生 Handler函数叫做 Closure,支持 vararg(不定参) append(前置展开) eval(求值时自动展开) lazy(求值时自动展开一次,以后都使用第一次的值) pure(纯函数) noreturn(不会返回)
noarg(无参数) onearg(一个参数) twoarg(两个参数) threearg(三个参数) inline(强制内联) internal(代表内部原生展开器) void(无需返回) noexcept(不会抛出异常) nobind(不是闭包)
optimized(识别方法体所代表的操作,编译时虚拟机江创建优化指令,例如 add)
当然大部分都是没卵用的 flag(
Closure 还可以保存执行状态,可以作为 Fiber 随时挂起和恢复执行
sexp/
text/
Lexer
Parser
Token
Metadata
TokenType
ICharStream
ITokenStream
ISexpStream
type/
Symbol
Reference
Chunk
Binding
Closure
Pair
model/
Sexp
SexpList
SexpAtom
virtual/
SexpVM
Instruction
Compiler
Stack
Frame
port/
ListImpl
HashImpl
StringImpl
StringBufferImpl
ReflectImpl
ArrayImpl
VectorImpl
RealImpl
ComplexImpl
except/
LexError
ParseError
ReferenceError
TypeError
ArgumentError
ReflectError
ControlFlowException
front/
Main
SexpRepl
SexpServer
utils/
Promise
ClassHelper
JavaFFI
NumberOp
Stdlib
api/scripting/
SexpContext
ScriptEngine
EngineFactory
Sexp
执行模式和以前无异,不过这次更不 Functional 了一些(迫真),也不使用作用域优化,只是默认一般没人使用作用域,都是使用 apply
作用域还是函数调用作用域
还是支持 Locals、Globals 取值赋值 Hook、找不到变量 Hook、Send 操作 Hook、异常 Hook、公开作用域列表(也即调用帧)
1.
(. handlerid arglist)
2. (symbol arglist)
3. (macro-value arglist)
4. ((macro sexp) arglist)
5. ()
6. object
7. .stop .yield
8. (.value obj)
9. (.goback) (.gonext)
10. (tailrec macro-repr args)
原生字面还是支持1. nil
()
2. bool #t #f
3. number 01 0x1 0b1001 0o122 2323N 32323D 2233.223F 66666L 6B
支持 Java 的 Byte Short Int Long Double BigInteger BigDecimal 类型4. list
(a b c)
5. string '' "" "\"" "\n" 'string
6. symbol dsdsd abc identifier
7. char 'c'
duangsues.is_a? SaltedFish
新语言就被称为 sexp 打算支持手动尾递归优化、Heredoc 文本、纯函数的折叠、字符串内联代码、异常系统(和解释器内部堆栈)、方括号、动态自定义语法(类似 R6RS 的特定字符序列自动结合,当然用 Trie 树实现比较) 支持 call-by-value(称为 direct)、call-by-name(称为 evaluated)、call-by-need(称为 lazy) 类似 R6RS 的 ' 语法 允许控制流异常 .goback 和 .gonext 步进到语句 允许 .stop 终止解释和…
接下来是分开的额外定义文档,包括文本语法、异常处理、执行流程、流程控制、内部函数、虚拟机流程、自动类型转换提升、闭包实现
R6RS 实现文档上有写的第三种实现方式比较有趣,但是友协无法参考,比如作用域优化中使用 box... 这里是没有必要的,因为它支持本地调用
准备定义一套新的字节码和执行方式
准备定义一套新的字节码和执行方式
因为有些优化是应该理解的,所以在实现前我要先翻译好 https://www.biwascheme.org/doc/internal/opecodes.html 这个...
补一个不想让人看的,毕竟现实骨感,打算拿 PHP 写一个类似 ApkBundler 的,设计类似 ApkBundler,不过小一些,包含 web 前端
暂时叫做 ApkBin,经典的 PHP Laravel + RDBMS + RESTFul 架构
而那个暂时代替 ABS 的解释器叫做 Sexp
并且这次是以抄酷安为主收集 GeekApk 的扩展特性为辅(就是说新概念不实现,简单的添加 Sexp 插件系统),而且提供安装包托管服务
补一个不想让人看的,毕竟现实骨感,打算拿 PHP 写一个类似 ApkBundler 的,设计类似 ApkBundler,不过小一些,包含 web 前端
暂时叫做 ApkBin,经典的 PHP Laravel + RDBMS + RESTFul 架构
而那个暂时代替 ABS 的解释器叫做 Sexp
并且这次是以抄酷安为主收集 GeekApk 的扩展特性为辅(就是说新概念不实现,简单的添加 Sexp 插件系统),而且提供安装包托管服务
解释一下什么是 HeapParser:
之前 Lime 的 Parser 是一个递归下降法解析器,伪代码看来就是(而且,那时候 Parsing 和 Lexer 不是同时进行的):
(所以,当时的非流模式还要给递归 parser 取子序列,麻烦很多,现在不需要了)
假设你写了一个分词器,它只会产生数字词条序列,而你也正确的定义了
优点是效果好、理论上能解析嵌套更深的表达式,缺点是因为堆分配太多可能增加 GC 压力
这个看不懂就算了,太长也懒得看..
之前 Lime 的 Parser 是一个递归下降法解析器,伪代码看来就是(而且,那时候 Parsing 和 Lexer 不是同时进行的):
(所以,当时的非流模式还要给递归 parser 取子序列,麻烦很多,现在不需要了)
def parse()乍一看的确不容易理解,因为递归一般比循环更加困难,我们来举例分析
sexp = []
while (tok = token()) != EOS
if tok.type == LPAREN
sexp << parse()
elseif tok.type == RPAREN
return sexp
sexp << objectize(tok)
return sexp
假设你写了一个分词器,它只会产生数字词条序列,而你也正确的定义了
objectize() 将数值字面解析为数字对象(1 323 3444 (23333 (1) () 666.666) (((1))))一个复杂的 S 表达式。
result = parse()最后的结果是
sexp = []
while (tok = token()) != EOS # Token is LPAREN (
sexp << parse() # recursive call self
sexp = []
while (tok = token()) != EOS
# tok = 1
sexp << objectize(tok) # [1]
sexp << objectize(tok) # [1 323]
sexp << objectize(tok) # [1 323 3444]
sexp << parse() # [1 323 3444 [23333 [1] [] 666.666]]
sexp << parse() # [1 323 3444 [23333 [1] [] 666.666] [[[1]]]]
... frame ommited ..
[[1 323 3444 [23333 [1] [] 666.666] [[[1]]]]]
而 HeapParser 则使用堆上的链表模拟这个解析栈,它完全不需要递归,所以被称为 HeapParser优点是效果好、理论上能解析嵌套更深的表达式,缺点是因为堆分配太多可能增加 GC 压力
这个看不懂就算了,太长也懒得看..
def heapParse()
emultedStack = []
while (tok = token()) != EOS
if tok.type == LPAREN
emulatedStack << []
elseif tok.type == RPAREN
emulatedStack[emulatedStack.lastIndex - 1] << emulatedStack.pop()
emulatedStack[emulatedStack.lastIndex] << objectize(tok)
return emultedStack
然后写完那篇 IL 文章后我打算结合 ABS 的设计再手写
+ Ruby 版本的解释器
+ Java 版本的解释器
+ Rust no_std 版本的解释器
+ Ruby 版本的解释器
+ Java 版本的解释器
+ Rust no_std 版本的解释器
duangsues.is_a? SaltedFish
解释一下什么是 HeapParser: 之前 Lime 的 Parser 是一个递归下降法解析器,伪代码看来就是(而且,那时候 Parsing 和 Lexer 不是同时进行的): (所以,当时的非流模式还要给递归 parser 取子序列,麻烦很多,现在不需要了) def parse() sexp = [] while (tok = token()) != EOS if tok.type == LPAREN sexp << parse() elseif tok.type…
当然不是没有区别的
普通的递归下降法解析器 把
HeapParser 则解析成
这样的话,就没法直接写
普通的递归下降法解析器 把
(() ()) 解析为 [[[] []]]HeapParser 则解析成
[[] []]这样的话,就没法直接写
1 2 3 这种,顶层必须是 S 表达式,但也符合真实还是支持 Locals、Globals 取值赋值 Hook、找不到变量 Hook、Send 操作 Hook、异常 Hook、公开作用域列表(也即调用帧)