M语言编程_所有编程语言大全

M语言编程_所有编程语言大全一直对技术有很强的兴趣,终于,决定要写自己的语言(m语言)。那就先从最简单的开始:解释执行器。一套完整的语言包含的肯定不止解释执行器了,还要有编译器和IDE,也就还要有语法高亮、智能提示等,不过还没

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一直对技术有很强的兴趣,终于,决定要写自己的语言(m语言)。那就先从最简单的开始:解释执行器。

一套完整的语言包含的肯定不止解释执行器了,还要有编译器和IDE,也就还要有语法高亮、智能提示等,不过还没学会那些,先搞个最基本的解释执行器。

思路如下:

  1. 定义好希望的语法(基本语句有:顺序执行、if语句、for语句、while语句、系统自有函数定义、用户函数定义、函数调用)
  2. 找一款词法语法解析器工具,让字符串流变成语法书(AST)
  3. 编写解释执行器
    1. 元数据收集
    2. 变量作用域定义、查找
    3. 解释执行

先设想我们的m语言语法要怎么牛b啊,比如下面这段demo语法代码:

go 计算标准体重(年龄)
{
    体重:年龄*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;
输出(计算标准体重(a));

 很显然,第一个语句块是用户函数的定义方式,以”go”字符串为函数定义的开始,接着是常规的函数名称、参数、函数方法块。

剩下的大致上就是顺序执行了,其中穿插着一些循环语句等,repeat循环自定义的比较厉害,好叼。。。感觉。。真的好叼。。。。

每个语句以封号后缀结束、赋值以冒号来标识。

接着来看看基于ANTLR的词法定义:

M语言编程_所有编程语言大全

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进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是入口规则,根规则

program program= NodeParser.parseProgram(tree);               //自己写的NodeParser类,需要一堆自定义的节点类型配合解析整棵AST树

mRuntime runtime=new mRuntime(program);

runtime.plainInterpreter();                           //解释器执行

System.out.println("");

AST节点的定义:

M语言编程_所有编程语言大全  

demo代码构建成AST树的效果图(antlr插件中能看):

M语言编程_所有编程语言大全

 

 转换成为AST树后,剩下的就是编写解释执行器,其实相当于前端编译器。

主要步骤是3步:

  1. 收集元数据
  2. 定义变量作用域
  3. 语句块的解释执行 
public void execute(program program) {
        //1. 先扫描函数定义,收集元数据
        collectMetaData(program);

        //2. 变量作用域
        walkAST4Variables(program);

        //3. 解释执行代码
        runCode(program);
}

 

 1. 收集元数据,其实就是对自定义函数的收集,统一放到一个Dictionary里,以便到时候引用到了执行语句块(和参数的传递)

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中众多节点类型中的一种,代表函数声明节点。

2. 定义变量作用域,由于存在函数(自定义函数、系统自带函数),因此需要有变量Scope的概念,存在局部变量覆盖全局变量现象

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("变量未定义");
    }
}  

 当局部变量中没有找到本地变量定义时,会根据parent关联向上找变量,直到为null。

3. 语句块的解释执行,这个可以说是最容易理解的地方了

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)会调用一系列子语句,如下图就一图就懂:

M语言编程_所有编程语言大全

如上图中,针对expression是调用calc的,一堆calc,expression中套expression。

system built-in函数的定义,是通过NativeMethodNode.setCode来标识的,比如当前实现的code为OUTPUT,功能如下:System.out.print/Console.Write()

M语言编程_所有编程语言大全

第一个红框是native node中判断code是哪个system built-in函数的编码代号

第二个红框是对应built-in函数的java语句执行。

demo m代码对应的解释执行输出:

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)

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/167994.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • C# 二代身份证验证 / VerifyIDCard[通俗易懂]

    C# 二代身份证验证 / VerifyIDCard[通俗易懂]由于现在社会技术的发展,需要验证身份证的合法性越发重要那么我们如何去验证一个身份证是否合法呢?我在以前的时候,特意写过一篇VB.NET查询身份证信息当然它是通过“百度身份证信息查询”的REST接口编写的一个类,当然那个时候并不考虑通过“身份证验证算法”识别身份证是否有效,注意VB.NET的主题是“查询身份证信息”而不是“验证身份证合法性”、我们以上面的“身份

  • RabbitMQ和Kafka对比以及场景使用说明

    RabbitMQ和Kafka对比以及场景使用说明  我目前的项目最后使用的是RabbitMQ,这里依然是结合网上大神们的优秀博客,对kafka和rabbitmq进行简单的比对。最后附上参考博客。  1.架构模型  rabbitmq    RabbitMQ遵循AMQP协议,RabbitMQ的broker由Exchange,Binding,queue组成,其中exchange和binding组成了消息的路由键;客户端Producer通过…

    2022年10月14日
  • Java中Boolean是什么?

    Java中Boolean是什么?Java中的boolean其实就是c中的bool型(逻辑型)数据类型。在java中,boolean值只能是true和false,而不能用0和1代替,并且一定要小写。要注意到的是,数值的0、-0、特殊值的null、NaN、undefined以及空字符(””)都会被解释为false,其他值则会被解释为true。…

  • android studio 皮肤,Android Studio 自定义皮肤主题和背景

    android studio 皮肤,Android Studio 自定义皮肤主题和背景AndroidStudio自定义皮肤主题-ColorThemes最近有人问我,“你代码这是用的什么字体?看着好舒服”,其实这是用了自定义皮肤主题包,主题包下载网站http://color-themes.com/?view=index这个皮肤主题不仅可以在AndroidStudio上使用,也可以在WebStorm,PyCharm,RubyMine,PhpStorm,AppCode等好多开发工…

    2022年10月29日
  • 详解 MNIST 数据集

    MNIST数据集已经是一个被”嚼烂”了的数据集,很多教程都会对它”下手”,几乎成为一个“典范”.不过有些人可能对它还不是很了解,下面来介绍一下.MNIST数据集可在http://yann.lecun.com/exdb/mnist/获取,它包含了四个部分:Trainingsetimages:train-images-idx3-ubyte.gz(9.9MB,解压后47

  • Lc5-最长回文字串

    Lc5-最长回文字串Lc5-最长回文子串js求数组求字符串的长度都是length方法js字符串截取,varlongestPalindrome=function(s){letres=”;for(leti=0;i<s.length;i++){letl=i-1,r=i+1;sub(l,r);l=i,r=i+1;sub(l,r);fu

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号