当前位置:网站首页>Compiler analysis of clojure operation principle
Compiler analysis of clojure operation principle
2022-07-26 16:12:00 【Flying watermelon】
Clojure is a compiled language, yet remains completely dynamic — every feature supported by Clojure is supported at runtime. Rich Hickey https://clojure.org/
there runtime refer to JVM,JVM At first, it was for operation Java Designed for language , Now it has developed into a heavyweight platform , except Clojure outside , Many dynamic languages They also choose to be based on JVM To achieve . For a more specific description Clojure Operation principle , There will be two articles to introduce . This is the first article , The main contents involved are : Compiler workflow 、Lisp The macro mechanism of . The second part will mainly analyze Clojure Program compiled into bytecode How to ensure the characteristics of dynamic language and how to accelerate Clojure Program execution speed , This will involve JVM Class loading mechanism of 、 The reflex mechanism .
A compiled VS. interpreted
SO There is a problem Is Clojure compiled or interpreted, According to the official website quotation at the beginning of this article , explain Clojure It is a compiled language , It's like Java、Scala. however Clojure And Java What's different is ,Clojure You can compile at run time and then load , and Java Clearly distinguish between compile time and run time .
Compiler workflow
It is similar to the interpreter in interpretive language , Compiled languages pass through compilers (Compiler) To compile the source program into bytecode . Generally speaking , The compiler includes Two parts :
- front end : Lexical analysis → Syntax analysis → Semantic analysis
- Back end : analysis 、 Optimize → Target code generation
Clojure Our compiler also follows this pattern , It can be roughly divided into the following two modules :
- Read Clojure Source program → participle → structure S- expression , from LispReader.java Class implementation
- Macro extension → Semantic analysis → Generate JVM Bytecode , from Compiler.java Class implementation
The above figure shows the input and output of different stages , The specific implementation is explained one by one below .
LispReader.java
Generally speaking , Programming languages with complex syntax will separate lexical analysis and grammatical analysis into Lexer And Parser, But in Lisp In the family , The syntax of the source program is already AST 了 , So the Lexer And Parser Merge into one process Reader, Core code The implementation is as follows :
for (; ; ) {
if (pendingForms instanceof List && !((List) pendingForms).isEmpty())
return ((List) pendingForms).remove(0);
int ch = read1(r);
while (isWhitespace(ch))
ch = read1(r);
if (ch == -1) {
if (eofIsError)
throw Util.runtimeException("EOF while reading");
return eofValue;
}
if (returnOn != null && (returnOn.charValue() == ch)) {
return returnOnValue;
}
if (Character.isDigit(ch)) {
Object n = readNumber(r, (char) ch);
return n;
}
IFn macroFn = getMacro(ch);
if (macroFn != null) {
Object ret = macroFn.invoke(r, (char) ch, opts, pendingForms);
//no op macros return the reader
if (ret == r)
continue;
return ret;
}
if (ch == '+' || ch == '-') {
int ch2 = read1(r);
if (Character.isDigit(ch2)) {
unread(r, ch2);
Object n = readNumber(r, (char) ch);
return n;
}
unread(r, ch2);
}
String token = readToken(r, (char) ch);
return interpretToken(token);
}Reader The behavior of is by the built-in constructor ( There are currently figures 、 character 、Symbol These three categories ) With one called read table The extension mechanism of (getMacro) Driven ,read table Each record in it is provided with a characteristic symbol ( be called macro characters) To specific read behavior ( be called reader macros) Mapping .
And Common Lisp Different , Ordinary users cannot expand Clojure Inside read table. About extension read table The benefits of , You can refer to StackOverflow Upper What advantage does common lisp reader macros have that Clojure does not have?.Rich Hickey stay One Google Group There are explanations that are not open read table The reason of , Here is an excerpt from :
I am unconvinced that reader macros are needed in Clojure at this time. They greatly reduce the readability of code that uses them (by people who otherwise know Clojure), encourage incompatible custom mini- languages and dialects (vs namespace-partitioned macros), and complicate loading and evaluation. To the extent I’m willing to accommodate common needs different from my own (e.g. regexes), I think many things that would otherwise have forced people to reader macros may end up in Clojure, where everyone can benefit from a common approach. Clojure is arguably a very simple language, and in that simplicity lies a different kind of power. I’m going to pass on pursuing this for now,
By the end of Clojure 1.8 edition , There are nine in total macro characters:
Quote (')
Character (\)
Comment (;)
Deref (@)
Metadata (^)
Dispatch (#)
Syntax-quote (`)
Unquote (~)
Unquote-splicing ([email protected])Their specific meanings can be referred to Official documents reader#macrochars.
Compiler.java
Compiler Class mainly has three entry functions :
- compile, When calling
clojure.core/compileWhen using - load, When calling
clojure.core/require、clojure.core/useWhen using - eval, When calling
clojure.core/evalWhen using
These three entry functions will call macroexpand、analyze Method , Generate Expr object ,compile The function also calls emit Method generation bytecode.
macroexpand
Macro There is no doubt that Lisp The Dragon butcher's knife in the world , Can be in Compile time Automatic code generation :
static Object macroexpand(Object form) {
Object exf = macroexpand1(form);
if (exf != form)
return macroexpand(exf);
return form;
}macroexpand1 Function to do the main extension work , It will be called isMacro Judge the present Var Is it a macro , And this is through inspection var Is it a function , And meta information macro Is it true. Clojure Inside through defmacro Function to create a macro , It will be called var Of setMacro Function to set meta information macro by true.
analyze
interface Expr {
Object eval();
void emit(C context, ObjExpr objx, GeneratorAdapter gen);
boolean hasJavaClass();
Class getJavaClass();
}
private static Expr analyze(C context, Object form, String name)analyze Conduct major semantic analysis ,form Parameters are various data structures after macro expansion (String/ISeq/IPersistentList etc. ), The return value type is Expr, You can guess ,Expr Subclasses of are the body of the program , Follow the modular programming style , Every subclass knows how to evaluate itself (eval) Or output bytecode(emit).
emit
What needs to be clear here is ,Clojure The compiler didn't put Clojure The code changes to the corresponding Java Code , But by means of bytecode Operation Library ASM Direct generation can run in JVM Upper bytecode.
according to JVM bytecode The specification of , Every .class Files must consist of classes , and Clojure As a functional language , The body is a function , adopt namespace To encapsulate 、 Isolation function , You may take it for granted that everyone namespace Corresponding to a class ,namespace Each function in the class corresponds to the method in the class , In fact, this is not the case , according to Clojure Official documents , The correspondence is like this :
- Each file 、 function 、
gen-classWill generate a.classfile - Each file generates one
<filename>__initLoad class for gen-classGenerate classes with fixed names , Convenience and Java Interaction
Generated bytecode It will be introduced in detail in the second article in this series , Coming soon .
eval
Every Expr All subclasses of have eval Corresponding implementation of method . The following code snippet is LispExpr.eval The implementation of the , Other subclasses implement similar , I'm not going to repeat it here .
public Object eval() {
IPersistentVector ret = PersistentVector.EMPTY;
for (int i = 0; i < args.count(); i++)
// Here recursively find the value of each item in the list
ret = (IPersistentVector) ret.cons(((Expr) args.nth(i)).eval());
return ret.seq();
}summary
See before SICP After the implementation of several interpreters , But they are relatively simple , Through analysis Clojure Compiler implementation , Deepened eval-apply loop The understanding of the , Another point is to uncover the true face of macro , I always thought that macro was a magical thing , In fact, it's just Functions that run at compile time nothing more , The content of input and output is the data structure of the program , It is also inherent in the program AST.
Reference resources
边栏推荐
- 博途PLC顺序开关机功能块(SCL)
- 八叉树建立地图并实现路径规划导航
- 绘制漂亮的中学操场轮廓,生成带经纬度数据
- Google Earth Engine——MERRA-2 M2T1NXAER:1980-2022年气溶胶逐日数据集
- Kalibr calibration realsensed435i -- multi camera calibration
- 德国emg电动执行器EB800-60II
- 综合设计一个OPPE主页--明星机型的设计
- Mapwithstate of spark streaming state flow
- bucher齿轮泵QX81-400R301
- Vs2019debug mode too laggy can't enter the breakpoint
猜你喜欢

提问征集丨快来向NLLB作者提问啦!(智源Live第24期)
![[ten thousand words long text] Based on LSM tree thought Net 6.0 C # realize kV database (case version)](/img/84/640de0bf779cd45498204909be56d1.png)
[ten thousand words long text] Based on LSM tree thought Net 6.0 C # realize kV database (case version)

hawe螺旋插装式单向阀RK4

2022 test questions and answers for the latest national fire facility operator (senior fire facility operator)

可信隐私计算框架“隐语”开源专家观点集锦

小哥自创AI防拖延系统,一玩手机就被“闪瞎” | Reddit高热

Delta controller rmc200
![[RCTF2015]EasySQL](/img/68/328ee5cffc8b267b6b0f284eb8db2c.png)
[RCTF2015]EasySQL

SQL statement -- single line comment and multi line comment

Implementation of personalized healthy diet recommendation system based on SSM
随机推荐
[ten thousand words long text] Based on LSM tree thought Net 6.0 C # realize kV database (case version)
Test cases should never be used casually, recording the thinking caused by the exception of a test case
spark-streaming状态流之mapWithState
We were tossed all night by a Kong performance bug
TKE集群节点max-pod是如何配置的
.net get injection object manually
理解卷积神经网络中的权值共享
Bugku login1
Google Earth engine - merra-2 m2t1nxlv: 1980 present global pressure, temperature, wind and other data sets
Understand │ XSS attack, SQL injection, CSRF attack, DDoS attack, DNS hijacking
Operating system migration practice: deploying MySQL database on openeuler
[BJDCTF2020]Easy MD5
基于SSM开发实现校园疫情防控管理系统
Technology vane | interpretation of cloud native technology architecture maturity model
PAT甲级1048 Find Coins
Question collection come and ask nllb authors! (Zhiyuan live issue 24)
2022年全国最新消防设施操作员(高级消防设施操作员)考试试题及答案
2021年软件测试工具趋势
Build resume editor based on Nocode
机器人手眼标定Ax=xB(eye to hand和eye in hand)及平面九点法标定