M语言编程_所有编程语言大全(Meaning)
一直對技術(shù)有很強的興趣,終于,決定要寫自己的語言(m語言)。那就先從最簡單的開始:解釋執(zhí)行器。
一套完整的語言包含的肯定不止解釋執(zhí)行器了,還要有編譯器和IDE,也就還要有語法高亮、智能提示等,不過還沒學(xué)會那些,先搞個最基本的解釋執(zhí)行器。
思路如下:
- 定義好希望的語法(基本語句有:順序執(zhí)行、if語句、for語句、while語句、系統(tǒng)自有函數(shù)定義、用戶函數(shù)定義、函數(shù)調(diào)用)
- 找一款詞法語法解析器工具,讓字符串流變成語法書(AST)
- 編寫解釋執(zhí)行器
- 元數(shù)據(jù)收集
- 變量作用域定義、查找
- 解釋執(zhí)行
先設(shè)想我們的m語言語法要怎么牛b啊,比如下面這段demo語法代碼:
go 計算標(biāo)準(zhǔn)體重(年齡)
{
體重:年齡*3;
體重;
}
體重:10;
a:10;
a:輸出(體重);
b:25;
a:100+10+b;
輸出(a);
(a==135)->
{
a:a+a+a;
輸出(a);
}
else
{
輸出(b);
};
a:1;
while a<10 ->{
a:a+2;
輸出(a);
};
輸出("WHILE OK");
repeat i from 0 to 100 step 10->{
輸出(i);
}
init->{
輸出("FOR INIT");
}
onerror->{
輸出("FOR ERROR");
}
finally->{
輸出("FOR FINALLY");
};
輸出('FOR OK');
a:10;
輸出(計算標(biāo)準(zhǔn)體重(a));
很顯然,第一個語句塊是用戶函數(shù)的定義方式,以”go”字符串為函數(shù)定義的開始,接著是常規(guī)的函數(shù)名稱、參數(shù)、函數(shù)方法塊。
剩下的大致上就是順序執(zhí)行了,其中穿插著一些循環(huán)語句等,repeat循環(huán)自定義的比較厲害,好叼。。。感覺。。真的好叼。。。。
每個語句以封號后綴結(jié)束、賦值以冒號來標(biāo)識。
接著來看看基于ANTLR的詞法定義:
m.g4:
grammar m;
import basic,function,assignStmt,ifStmt,forStmt,whileStmt;
nomalStmt
:assignStmt
|ifStmt
|forStmt
|whileStmt
;
declarationStmt
:functionDeclare
;
stmt
:nomalStmt LS
|declarationStmt
;
program
: stmt+
;
由于詞法語法定義較多,不貼代碼了,可以下載代碼看全部的(基于ideas/需要安裝antlr4插件)
接下來是時候讓我們load進(jìn)demo代碼解析成AST樹啦:
String code=Utils.readTxtFile("F:\\BaiduYunDownload\\mLanguage(4)\\m_code2.m");//這個是放demo代碼的文件
code=code.substring(1);//去掉第一個特殊字符
CharStream is = CharStreams.fromString(code); //antlr對象,讀入字符串
mLexer lexer = new mLexer(is); //mLexer是antlr自動生成的一個詞法類
CommonTokenStream tokens = new CommonTokenStream(lexer); //antlr對象
mParser parser = new mParser(tokens); //mParser是antlr自動生成的一個此法解析類
mParser.ProgramContext tree=parser.program(); //program是入口規(guī)則,根規(guī)則
program program= NodeParser.parseProgram(tree); //自己寫的NodeParser類,需要一堆自定義的節(jié)點類型配合解析整棵AST樹
mRuntime runtime=new mRuntime(program);
runtime.plainInterpreter(); //解釋器執(zhí)行
System.out.println("");
AST節(jié)點的定義:
demo代碼構(gòu)建成AST樹的效果圖(antlr插件中能看):
轉(zhuǎn)換成為AST樹后,剩下的就是編寫解釋執(zhí)行器,其實相當(dāng)于前端編譯器。
主要步驟是3步:
- 收集元數(shù)據(jù)
- 定義變量作用域
- 語句塊的解釋執(zhí)行
public void execute(program program) {
//1. 先掃描函數(shù)定義,收集元數(shù)據(jù)
collectMetaData(program);
//2. 變量作用域
walkAST4Variables(program);
//3. 解釋執(zhí)行代碼
runCode(program);
}
1. 收集元數(shù)據(jù),其實就是對自定義函數(shù)的收集,統(tǒng)一放到一個Dictionary里,以便到時候引用到了執(zhí)行語句塊(和參數(shù)的傳遞)
private void collectMetaData(program program) {
for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
if(stmt.declarationStmt!=null)
this.userDefinedFunctionSymbols.defineMethod(stmt.declarationStmt.functionDeclare.functionIdentifier.getIdentifier(), stmt.declarationStmt.functionDeclare);
}
public class UserDefinedFunctionSymbols {
private Dictionary<String, functionDeclare> methods=new Hashtable<>();
public functionDeclare getMethod(String identifier) {
return methods.get(identifier);
}
public void defineMethod(String identifier, functionDeclare ast) {
methods.put(identifier, ast);
}
}
functionDeclare是具體的node,屬于AST中眾多節(jié)點類型中的一種,代表函數(shù)聲明節(jié)點。
2. 定義變量作用域,由于存在函數(shù)(自定義函數(shù)、系統(tǒng)自帶函數(shù)),因此需要有變量Scope的概念,存在局部變量覆蓋全局變量現(xiàn)象
private void walkAST4Variables(program program)
{
program.VariableSymbols=globalVariableSymbol;
for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
{
stmt.VariableSymbols=program.VariableSymbols;
if(stmt.declarationStmt!=null)
{
stmt.declarationStmt.VariableSymbols=stmt.VariableSymbols;
VarWalker.walk(stmt.declarationStmt);
}
if(stmt.nomalStmt!=null)
{
stmt.nomalStmt.VariableSymbols=stmt.VariableSymbols;
VarWalker.walk(stmt.nomalStmt);
}
}
}
public class VariableSymbol {
private Dictionary<String, Variable> variables=new Hashtable<>();
private VariableSymbol parentVariableSymbol;
public void setParentVariableSymbol(VariableSymbol parentVariableSymbol)
{
this.parentVariableSymbol=parentVariableSymbol;
}
public void defineVariable(String name, Variable variable) {
variables.put(name, variable);
}
public void setValue(String name, Object value)
{
Variable variable=getVariable(name);
variable.Value=value;
}
public Object getValue(String name)
{
Variable variable=getVariable(name);
return variable.Value;
}
private Variable getVariable(String name) {
List<String> keys=Collections.list(variables.keys());
if(keys.contains(name))
return this.variables.get(name);
if(this.parentVariableSymbol!=null)
return this.parentVariableSymbol.getVariable(name);
throw new RuntimeException("變量未定義");
}
}
當(dāng)局部變量中沒有找到本地變量定義時,會根據(jù)parent關(guān)聯(lián)向上找變量,直到為null。
3. 語句塊的解釋執(zhí)行,這個可以說是最容易理解的地方了
private void runCode(program program) {
StmtExecutor executor=new StmtExecutor(this);
for(com.mckay.language.m.core.nodes.m.stmt stmt:program.stmts)
if(stmt.nomalStmt!=null)
executor.execute(stmt.nomalStmt);
}
StmtExecutor.execute(nomalStmt)會調(diào)用一系列子語句,如下圖就一圖就懂:
如上圖中,針對expression是調(diào)用calc的,一堆calc,expression中套expression。
system built-in函數(shù)的定義,是通過NativeMethodNode.setCode來標(biāo)識的,比如當(dāng)前實現(xiàn)的code為OUTPUT,功能如下:System.out.print/Console.Write()
第一個紅框是native node中判斷code是哪個system built-in函數(shù)的編碼代號
第二個紅框是對應(yīng)built-in函數(shù)的java語句執(zhí)行。
demo m代碼對應(yīng)的解釋執(zhí)行輸出:
10 135 405 3 5 7 9 11 WHILE OK FOR INIT 0 10 20 30 40 50 60 70 80 90 100 FOR FINALLY FOR OK 30 ok
代碼下載(基于java)
總結(jié)
以上是生活随笔為你收集整理的M语言编程_所有编程语言大全(Meaning)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Safari中选择电脑版页面浏览
- 下一篇: 【爱思助手】教你简单搞定插件和Cydia