设计一个全新的程序语言"Simple"

在学习之前我们先了解一点基础知识

语法规则

  • 语法规则的目的是把 例如 y = x + 1 这样的有效程序和1dkfa,d这种无效的字符串区分开
  • 语法规则解决具有二义性程序问题,如 1 + 2 * 3的运算优先级
  • 语法规则程序表面是否合乎规则,不关心程序的含义
  • 语法解析器总是把源代码转换成抽象语法🌲(AST)

语义

程序作了什么?

1.小步语义

小步语义的含义有点类似于数学求值的方式,如,(1 * 2) + (3 * 4)的计算步骤如下:

  • 执行 1 * 2 的乘法得到 2
  • 执行 3 * 4 的乘法得到 12
  • 执行 2 + 12 的加法得到 14
2.表达式

1 + 2 * 3 这就是表达式😊

如果我们用Java写一个 1 + 2 * 3 的程序,并将结果打印出来,该怎么做?

...
int a = 1 + 2 * 3;
System.out.println("a:" + a);
...

看起来很简单,那是因为Java在后台默默的帮我们做了很多工作,前面讲过,首先将 1 + 2 * 3抽象成语法树,然后在使用分析语义,算出结果,当然,具体实现逻辑会更复杂。

那我们怎么让Simple实现这个计算逻辑呢?

到现在为止,我们的Simple还是一张白纸,像一个刚出世的小孩,它什么都不认识,那我们首先要教它认识数字,所以我们定义一个特殊的名称Number,告诉它只要遇到这个字符串,就认为是数字。

注意,Simple中的数字和数学中的数字不是一个概念,这里Simple语言的底层实现我们用的是Java,就像Java的底层用到的是C或C++一样。

//在我们Simple的语法中,万物都基于Simple,在以后的写法中
//默认所有类型都继承Simple
class Number extends Simple{
    int value;
    public Number(int value){
        this.value = value;
    }
}

OK,那现在在Simple中,我们就可以用Number(1)表示 1了。但是想要实现上面的表达式,我们还需要让Simple这个小孩认识加法和乘法。

class Add extends Simple{
    Simple left;
    Simple right;
    
    public Add(Simple left, Simple right){
        this.left = left;
        this.right = right;
    }
}

class Multipy extends Simple{
    Simple left;
    Simple right;
    
    public Multipy(Simple left, Simple right){
        this.left = left;
        this.right = right;
    }
}

到目前为止呢,我们已经定义了数字、加法和乘法,所以上面的表达式我们已经可以用Simple来表示了:

Add(Number(1), Multipy(Number(2),Number(3)));

请注意,到目前为止,我们只是定义了Simple语法规则,离让它真正跑起来还有几步之遥🙂...

我们前面语义部分介绍到小步语义,没错,在这里我们就用到了它,按照小步语义的规则,这个计算应该是被拆分为3步:

  • 1 + 2 * 3
  • 1 + 6
  • 7

用我们人的思维逻辑,一眼就能看出应该先计算 2 * 3 再和一相加,但是对于程序来说,它如何能知道呢?在这里,我们引入一套规则,我们定义每个表达式(数字也认为是一个表达式)都有一个是否可以继续规约(reducible,类似于数学中的求值),如表达式 2 * 3 可以继续规约为 6 ,但是 6 不可以规约。

我们前面讲到在Simple语言中,万物基于Simple,所以,我们给Simple加两个方法。

abstract class Simple{
    //是否可规约
    public boolean abstract reducible();
    //规约
    public Simple abstract reduce();
}

那么,我们所有的子类都要实现这两个方法。

class Number extends Simple{
    ...
    //因为Number不可继续规约,故返回false
    public boolean reducible(){
        return false;
    }
    
    public Simple reduce(){
        //do nothing
        return this;
    }
    ...
}

class Add extends Simple{
    ...
    //加法可以规约
    public boolean reducible(){
        return true;
    }
    
    public Simple reduce(){
        if(left.reducible()){
        //如果这个表达式左边可以规约,则优先规约
            return new Add(left.reduce(),right);
        }else if(right.reducible()){
        //如果右边可以规约,则先规约右边
            return new Add(left,right.reduce());
        }else{
        //如果两边都不可以规约,则两边都是数字,直接加起来
            return new Number(left.value + right.value);
        }
    }
    ...
}

class Multipy extends Simple{
    ...
    //乘法可以规约
    public boolean reducible(){
        return true;
    }
    
    public Simple reduce(){
        if(left.reducible()){
        //如果这个表达式左边可以规约,则优先规约
            return new Multipy(left.reduce(),right);
        }else if(right.reducible()){
        //如果右边可以规约,则先规约右边
            return new Multipy(left,right.reduce());
        }else{
        //如果两边都不可以规约,则两边都是数字,直接相乘
            return new Number(left.value * right.value);
        }
    }
}

好了,现在Simple已经能处理加法和乘法的混合运算了,让我给它输入一个表达式试试效果吧。

Simple simple = new Add(new Multipy(new Number(1),new Number(2)),new Multipy(new Number(3),new Number(4)));
System.out.println(simple);
while (simple.reducible()){
     simple = simple.reduce();
     System.out.println(simple);
}

/* 以下是输出日志:
 1  *  2  +  3  *  4 
 2  +  3  *  4 
 2  +  12 
 14 
*/

以下是完整Demo

/**
 * 完整运行Demo,如有错误,欢迎指正
 * Created by gaoyufei on 2017/1/25.
 */
public class MySimpleLanguage {

    interface Simple {
        public boolean reducible();

        public Simple reduce();
    }

    static class Number implements Simple {
        public int value;

        public Number(int value) {
            this.value = value;
        }

        public boolean reducible() {
            return false;
        }

        public Simple reduce() {
            return this;
        }

        @Override
        public String toString() {
            return " " + value + " ";
        }
    }

    static class Add implements Simple {
        public Simple left;
        public Simple right;
        public Add(Simple left,Simple right){
            this.left = left;
            this.right = right;
        }
        public boolean reducible() {
            return true;
        }

        public Simple reduce() {
            if(left.reducible()){
                return new Add(left.reduce(),right);
            }else if(right.reducible()){
                return new Add(left,right.reduce());
            }else {
                return new Number(((Number)left).value + ((Number)right).value);
            }
        }

        @Override
        public String toString() {
            return left.toString() + " + " + right.toString();
        }
    }

    static class Multipy implements Simple {
        public Simple left;
        public Simple right;
        public Multipy(Simple left,Simple right){
            this.left = left;
            this.right = right;
        }
        public boolean reducible() {
            return true;
        }

        public Simple reduce() {
            if(left.reducible()){
                return new Multipy(left.reduce(),right);
            }else if(right.reducible()){
                return new Multipy(left,right.reduce());
            }else {
                return new Number(((Number)left).value * ((Number)right).value);
            }
        }

        @Override
        public String toString() {
            return left.toString() + " * " + right.toString();
        }
    }

    public static void main(String args[]) {
        System.out.println("---------Start simple---------");
        Simple simple = new Add(new Multipy(new Number(1),new Number(2)),new Multipy(new Number(3),new Number(4)));
        System.out.println(simple);
        while (simple.reducible()){
            simple = simple.reduce();
            System.out.println(simple);
        }
        System.out.println("---------End simple-----------");
    }
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,818评论 25 707
  • 我又翘课了,回家。十一月的武汉还是夏天的样子,我穿着过膝的白裙子,踩着不高的凉鞋,嗯,我准备回家。 中秋国庆我都在...
    萌萌哒的林菇凉阅读 201评论 0 1
  • 否定自己,看到这样的标题,不要觉得我就是在展露悲观的情绪,不要觉得我是在放弃自己,相反,我是在积极地对自己负责。 ...
    小斌PPT阅读 490评论 0 1