Apple:为孩子们创建的编程语言1

周海汉 2019-08-19
2019-08-19

Apple语言概述

Facebook的Libra项目,主体采用Rust语言,以安全高效著称。智能合约采用了自定义的Move语言,可以更方便安全处理金融业务。让一般的东摘西抄区块链项目相形见绌。其实定义一门语言,现在并不像几十年前那么困难了。缺的是思想。

Apple语言就是一门为孩子设计的语言。可以用于体验新设计语言的过程。

随着人类社会的发展,人类需要面临的计算越来越复杂。最开始的计算机,是几千年前的算盘类的机械装置。1694年德国发明家戈特弗里德-莱布尼茨发明了步进计算器,类似汽车的里程表,采用齿轮装置,手摇计算。每当一个一个齿轮转1圈10个齿,就会变成0,相邻的齿轮变成1.做减法时反向运动。做乘除法就是转为加减法。

1822年,Charles Babbage 提出一种机械装置,差分机,可以处理多项式计算,后面他又提出了更先进的“分析机”,有点类似现代的计算机,有存储,有指令。但都没有实现。英国数学家Ada lovelace,是诗人拜伦的女儿。她为分析机设想了一种语言,可以在分析机运行通用计算。Ada是公认的第一位程序员。

1890年美国人用打孔机电装置来统计人口,以分配预算和选票,大大缩减了原始统计方式,由原来的7年完成缩减到2年半。1924年IBM 成立,为何叫国际商业机器公司?因为那时的计算设备都是机械电子的。当时最大的哈佛马克一号用继电器来做开关计算,用于曼哈顿计划。继电器相比齿轮有了很大进步,因为直接用机电来控制开关,速度有了很大提高。但机械装置的问题是效率不高能耗高,还容易损坏,每秒能做50次翻转,3次加减运算。3秒完成一次乘法运算。机电装置还长虫。1947年9月,哈佛马克2号操作员从故障的继电器中拔出一只死虫。所以后面的程序员都需要在程序中找“Bug”。

1904年,英国物理学家John Fleming发明了热电子管(Thermionic Valve)——真空管。将两个电极装在一个气密的玻璃灯泡里,用电的方式来做开关。其中一个电极加热时会发射电子,,叫热电子发射。另一个电极会吸引电子。只有正电子可以穿过真空,负电子不能穿过,形成一个电子器件,电流只能单向流动,叫二极管(Diode)。1906年,美国发明家Lee de Forest改进成三级管,在中间加一个控制电极,通过加正电荷,允许电子流动,加负电荷,禁止电子流动,来控制电流的断开和闭合,形成一个开关。真空管相比继电器,没有机械运动,所以效率大大提高,每秒开闭数千次。但真空管很大,造价高,而且容易烧坏。真空管使用了半个世纪,广泛用于计算,通信,广播等领域。1943年底完成的“巨人1号”是第一个大规模使用真空管的计算机,由英国工程师Tommy Flowers设计,用于破解纳粹通信。这是第一台可编程计算机,采用把几百根电线插入插板的方式完成。1946年美国的Eniac则是第一台通用可编程计算机,每秒执行5000次加法运算,半天出一次故障。

直到1947年,贝尔实验室科学家John Bardeen等发明了晶体管,这种半导体电子器件才让电子计算往高效低耗稳定的方向持续发展。晶体管有两个电极,电极之间有一个隔开他们。这种材料有时导电,有时不导电。叫半导体。控制线连到一个门电极,通过改变门的电荷,来控制电极的导电性,从而形成一个开关,完成高性能逻辑数值运算。晶体管一下子提高到每秒10000次的开关,而且不容易坏,固体材料,体积小,节省能源。1957年,IBM的计算机508,就是采用晶体管设计,体积变小,造价变低,每秒执行4500次加法。而且逐渐引入个人电脑,得到广泛应用。现在一个晶体管小于50nm。而且正在向几纳米改进。一张纸的厚度是10万纳米。在纸的截面上,可以横着并排放数万个晶体管。每秒切换上百万次,用几十年不会坏。

在20世纪40年代,第一台计算机发明的时候,那时候人们只能用二进制编程,然后进化成用汇编。这些编程方式和计算机硬件关系紧密。输入输出的方式,都是打孔纸带,和阿兰图灵的设想非常像。图灵计算机系统有寄存器,ALG,指令集,内存等组成。那时程序员都是科学家兼任。

而现在的高级语言,使程序员只需关注逻辑算法需求,而无需关注底层硬件具体实现,为程序开发专业化工程化铺平了道路。

未来随着通用人工智能的实现,人类可以做的工作越来越少。人类的价值可能还是在创新创造文艺,对人类自身和宇宙的研究,创造更强大的机器智能,为人类未来千年亿年发展做规划,满足人类日益增长的物质文化需求。

编程作为一项综合性,基础性的学科,应该是每个人都需要掌握的,否则就没法适应时代的发展。

我想为孩子们创建一门简单的语言叫Apple,作为孩子们入门的语言。

Apple语言基于java jvm,这样方便不停的扩展,具有完善的功能和库,也利于快速实现。

这门语言要简单而强大,减少出错。

实现需求第一版

  • 实现整数加减乘除括号四则运算
  • 实现变量赋值
  • 实现单行和多行注释
  • 实现多行代码
  • 实现两行续为一行
  • 文件格式后缀为.ap

未来规划

实现图灵完备语言,内存自动管理,基本数据结构和算法,多线程,基本安全和tcp/ip, http web服务。

code

https://github.com/ablozhou/apple

ANTLR

网站:https://www.antlr.org/

为何要用ANTLR?

ANTLR (ANother Tool for Language Recognition) 是一个词法分析和语法分析的工具。作者Terence Parr ,从1989年开始做语言工具,现在圣地亚哥大学担任计算机教授。从元语言定义语法,最后可以生成相关词法分析,词,语法,生成分析树。是现在广泛使用的语言定义工具。

记得我上大学时,计算机课的老师就要求在Unix上,利用Lex/Yacc实现词法分析和语法分析,但那时做得并不精。Lex(词法分析生成器/A Lexical Analyzer Generator)和Yacc(Yet Another Compiler-Compiler)是原来计算机课程广泛使用的两个工具。 不过有了Antlr这样的工具,对开发语言又变容易了。

环境准备

  • MacOS:本项目测试主要是在Mac book完成,示例也是该系统。
  • Java:1.8+
  • antlr:4.7.2+

安装antlr4

cd /usr/local/lib
$ sudo curl -O https://www.antlr.org/download/antlr-4.7.2-complete.jar

$ vi ~/.zshrc
export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
alias antlr4='java -jar /usr/local/lib/antlr-4.7.2-complete.jar'
alias grun='java org.antlr.v4.gui.TestRig'
$ source ~/.zshrc

创建项目

完成后目录结构如下:

zhouhh@/Users/zhouhh/apple git:(master) $ tree
.
├── LICENSE
├── Readme.md
├── antlr
│   ├── pom.xml
│   └── src
│       └── main
│           └── antlr4
│               ├── Apple.g4
│               └── compile.sh
├── compiler
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── com
│           │       └── abloz
│           │           └── compile
│           │               ├── AppleVisitorImpl.java
│           │               └── Main.java
│           └── resources
├── hello.ap
└── pom.xml

这是maven组成的两个子模块的项目。

antlr项目只存放用于生产语法树的g4文件。

compiler项目则用于真正实现这门语言。

g4 文件

Apple.g4是语法定义文件。

grammar Apple;
// Author: Andy zhou <ablozhou@gmail.com>
// Date 2019.8.15

@header {package com.abloz.antlr;}
prog:   (stmt)*;
stmt:	expr ((NEWLINE|';'))*      # express
        | NEWLINE                  # newLine
        ;

expr:	expr op=(MUL|DIV) expr  # MulDiv
    |	expr op=(ADD|SUB) expr  # AddSub
    |	INT                  # int
    |	ID                   # id
    |	ID '=' expr          # assign
    |	'(' expr ')'         # parens
    ;
NEWLINE : [\r\n]+ ;
INT     : [0-9]+ ;

MUL : '*' ;
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;
ID : [_a-zA-Z0-9]+ ;             // match alpha,digital,_ identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

SPACES
: [ \t]+
;
COMMENT
 : '//' ~[\r\n\f]*
 ;
BLOCK_COMMENT
:  '/*' .*? '*/'
;

LINE_JOINING
 : '\\' SPACES? ( '\r'? '\n' | '\r' | '\f')
 ;
SKIPS: (WS|COMMENT|BLOCK_COMMENT|LINE_JOINING)+ -> skip;
$ antlr4 Apple.g4
$ javac Apple*.java
$ grun Apple prog -gui
100+2*34
^D

parse tree

pom文件

请参考github项目

Java 文件

  • AppleVisitorImpl.java
package com.abloz.compile;

import com.abloz.antlr.AppleBaseVisitor;
import com.abloz.antlr.AppleParser;

import java.util.HashMap;
import java.util.Map;

/**
 *
 *@author Andy Zhou <ablozhou@gmail.com>
 *@date 2019.08.15
 */
public class AppleVisitorImpl extends AppleBaseVisitor<Integer> {

    /** "memory" for our calculator; variable/value pairs go here */
    Map<String, Integer> memory = new HashMap<String, Integer>();


    @Override
    public Integer visitParens(AppleParser.ParensContext ctx) {

        return visit(ctx.expr());
    }

    @Override
    public Integer visitNewLine(AppleParser.NewLineContext ctx) {
        return 0;
    }

    /**
     *  expr op=('*'|'/') expr
     * @param ctx
     * @return
     */
    @Override
    public Integer visitMulDiv(AppleParser.MulDivContext ctx) {
        int left = visit(ctx.expr(0));  // get value of left subexpression
        int right = visit(ctx.expr(1)); // get value of right subexpression
        if ( ctx.op.getType() == AppleParser.MUL ) return left * right;
        return left / right; // must be DIV

    }

    @Override
    public Integer visitExpress(AppleParser.ExpressContext ctx) {
        Integer value = visit(ctx.expr());
        System.out.printf("%d\n",value);
        return value;
    }

    @Override
    public Integer visitProg(AppleParser.ProgContext ctx) {
        Integer c = ctx.getChildCount();
        Integer value = 0;
        for(Integer i = 0; i < c; i++){
            value = visit(ctx.getChild(i));

        }
        return value;
    }

    @Override
    public Integer visitAssign(AppleParser.AssignContext ctx) {
        String id= ctx.ID().getText();
        Integer value = visit(ctx.expr());
        memory.put(id,value);
        return value;
    }

    @Override
    public Integer visitAddSub(AppleParser.AddSubContext ctx) {
        Integer left = visit(ctx.expr(0));
        Integer right = visit(ctx.expr(1));
        if(ctx.op.getType() == AppleParser.ADD) {
            return left + right;
        }

        return left-right;
    }

    @Override
    public Integer visitId(AppleParser.IdContext ctx) {
        String id = ctx.ID().getText();
        if ( memory.containsKey(id) ) return memory.get(id);
        //System.out.println("id:"+id);

        return 0;
    }

    @Override
    public Integer visitInt(AppleParser.IntContext ctx) {
        return Integer.valueOf(ctx.INT().getText());
    }
}
  • Main.java
package com.abloz.compile;
import com.abloz.antlr.AppleLexer;
import com.abloz.antlr.AppleParser;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.IOException;
/**
 *
 *@author Andy Zhou <ablozhou@gmail.com>
 *@date 2019.08.15
 */
public class Main {

    public static void main(String[] args) {

        if(args.length < 1) {
            System.out.println("run:\n java -jar compiler-1.0-SNAPSHOT-jar-with-dependencies.jar xxx.ap");
            return;
        }
        String inputFile = args[0];
        CharStream input =null;

        try {
            input = CharStreams.fromFileName(inputFile);
        } catch (IOException e) {
            e.printStackTrace();
        }

//        String appleExpr = "3+2*5";
//        CharStream input = CharStreams.fromString(appleExpr);
        AppleLexer lexer=new AppleLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        AppleParser parser = new AppleParser(tokens);
        ParseTree tree = parser.prog(); // parse
        AppleVisitorImpl vt=new AppleVisitorImpl();
        Integer i = vt.visit(tree);
        System.out.println("out:"+i);

    
    }
}

编译

$ java -jar /usr/local/lib/antlr-4.7.2-complete.jar -visitor -no-listener -encoding UTF-8 Apple.g4 

也可以直接用mvn package来联编整个项目。

Apple语言测试文件

// test data
 // Computer v1.0
/* 这是一个多行的apple语言测试
    第一版主要实现复杂表达式,变量和注释

*/ //UTF-8

4+3*2
7 - 10 / 2  //with spaces.
 a=3;
3+a
3*(2+3)
b=3*2
c=b/ \
2 //another line
 e = 2+ (3*(3+2)-5)
 x = 3;
x
/* test other */
f=x+2

测试

$ java -jar compiler-1.0-SNAPSHOT-jar-with-dependencies.jar hello.ap

输出结果:

10
2
3
6
15
6
3
12
3
3
5
out:5

参考

  • ANTLR 官方网址 http://www.antlr.org/
  • ANTLR 官方 Github https://github.com/antlr/antlr4
  • 大量语法文件例子 https://github.com/antlr/grammars-v4

如非注明转载, 均为原创. 本站遵循知识共享CC协议,转载请注明来源