编程语言 订阅
编程语言(programming language)可以简单的理解为一种计算机和人都能识别的语言。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。 [1]  编程语言处在不断的发展和变化中,从最初的机器语言发展到如今的2500种以上的高级语言,每种语言都有其特定的用途和不同的发展轨迹。编程语言并不像人类自然语言发展变化一样的缓慢而又持久,其发展是相当快速的,这主要是计算机硬件、互联网和IT业的发展促进了编程语言的发展。 [1] 展开全文
编程语言(programming language)可以简单的理解为一种计算机和人都能识别的语言。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。 [1]  编程语言处在不断的发展和变化中,从最初的机器语言发展到如今的2500种以上的高级语言,每种语言都有其特定的用途和不同的发展轨迹。编程语言并不像人类自然语言发展变化一样的缓慢而又持久,其发展是相当快速的,这主要是计算机硬件、互联网和IT业的发展促进了编程语言的发展。 [1]
信息
外文名
programming language
别    称
计算机语言
种    类
机器语言、汇编语言和高级语言
中文名
编程语言
编程语言简介
计算机编程语言能够实现人与机器之间的交流和沟通,而计算机编程语言主要包括汇编语言、机器语言以及高级语言,具体内容如下: [2]  汇编语言该语言主要是以缩写英文作为标符进行编写的,运用汇编语言进行编写的一般都是较为简练的小程序,其在执行方面较为便利,但汇编语言在程序方面较为冗长,所以具有较高的出错率。 [2]  机器语言这种语言主要是利用二进制编码进行指令的发送,能够被计算机快速地识别,其灵活性相对较高,且执行速度较为可观,机器语言与汇编语言之间的相似性较高,但由于具有局限性,所以在使用上存在一定的约束性。 [2]  高级语言所谓的高级语言,其实是由多种编程语言结合之后的总称,其可以对多条指令进行整合,将其变为单条指令完成输送,其在操作细节指令以及中间过程等方面都得到了适当的简化,所以,整个程序更为简便,具有较强的操作性,而这种编码方式的简化,使得计算机编程对于相关工作人员的专业水平要求不断放宽。 [2] 
收起全文
精华内容
参与话题
问答
  • 编程语言

    千次阅读 2019-11-10 21:45:39
    编程语言学习 首先要思考学习编程语言的目的,目的是为了让计算机知道自己要干什么。只要能实现目的就行,当然花的时间成本越少越好。 机器语言太难,汇编有点难(不同的芯片还不一样),所以选择高级语言。 ...

    编程语言学习

     

    首先要思考学习编程语言的目的,目的是为了让计算机知道自己要干什么。只要能实现目的就行,当然花的时间成本越少越好。

    机器语言太难,汇编有点难(不同的芯片还不一样),所以选择高级语言。

     

    学第一门编程语言时,跟着老师或者教材认真学,例子都要敲一遍,语法也要仔细看一遍,常见的错误也要注意,最后最好能刷一遍该语言的面试编程题然后做一个项目。

     

    当掌握几门编程语言后,学其他的编程语言时,不要从头到尾的看书听课。浏览一遍基本的类型,语法后看该语言实现业务的经典框架,如果是纯学习,弄清原理,思想和流程就够了,因为再细如果不用的话也是马上忘记。如果是项目工作中要使用,那么,用该语言写一下经典的编程题,做一下笔试题,然后把用到的框架的基本功能复现一遍基本上就够了。工作使用中遇到问题再查,更深的框架应用用到再学。

     

    那么如何量化代表学习了该编程语言呢?

     

    1 数据类型(int/integer float double char string dict list... )

    2 程序结构(if for while break continue...)

    3 IO及文件操作(print,read,write)

    4 类和对象(class)

    5 数据库操作(连接,执行sql,查询结果保存)

     

    掌握上面的五项,就代表对该语言入门了。掌握一门编程语言的本质是用它解决相应的问题,不同的语言因为一些原因,其应用往往被局限在某一个领域,因此,只要能够使用该语言解决其应用领域的问题就可以了。这也是学习框架的原因,因为通过框架可以了解语言应用的领域,领域内业务的流程和配套的其他的一些技术。所以说在上面的基础上能够用现有框架解决问题就代表你掌握了这么语言了。

     

    针对不同的语言总结了一下各种语言的特性以及学习路线

    C(嵌入式)

    1 数据类型(int float... )

    2 程序结构(if for while break continue...)

    3 IO及文件操作(printf,read,write)

    4 指针修改内存

     

    C++(PC软件)

    1 数据类型(int float double)

    2 程序结构(if for while break continue...)

    3 IO及文件操作(cin,cout,read,write)

    4 指针

    5 类

    6 模板及STL

    7 Qt

     

    C#(网站,unity3d)

    1 数据类型($)

    2 程序结构(if for while break continue...)

    3 IO及文件操作

    4 .net

     

    java(网站和android)

    1 数据类型(int float double...)

    2 程序结构(if for while break continue...)

    3 IO及文件操作(println,read,write)

    4 类与反射

    5 SSH

    6 android

     

    python(数据分析,深度学习,爬虫)

    1 数据类型(list,dict...)

    2 程序结构(if for while break continue...)

    3 IO及文件操作(input,print,read,write)

    4 django,flask,scapy,mysql,mongodb,redis/memcache

    5 numpy,skilearn,pandas,matplotlib,

    6 tensorflow,pytorch,keras

     

    go(网站,区块链)

    1 数据类型(int,string...)

    2 程序结构(if for while break continue...)

    3 IO及文件操作(input,print,read,write)

    4 beego docker

    5 BTC ETC EOS

     

    php(网站)

    1 数据类型($)

    2 程序结构(if for while break continue...)

    3 IO及文件操作(echo)

    4 lamp

    5 thinkphp

     

    其中,C语言最早,由于软件危机,出现面向对象编程,然后出现包含面向对象的C++,然后出现全部都对象化的面向对象的java,然后在互联网兴起的时候被用在了做网站上,后来随着android的兴起,进一步得到发展,其半编译半解释的特性具有跨平台,灵活和效率的特点。为了针对java,微软开发了C#,C#的设计者之前设计了Delphi,所以C#的语法与delphi有许多相似的地方。php则是一个快速做网站的语言,一度被认为是最好的编程语言。python是纯解释性的语言,语言非常灵活,使用简单功能强大,开发效率很高,运行效率很低,ubuntu自带python2.7,适合用来做算法实现,有时可以当作乞丐版的matlab。随着机器学习和深度学习等重要算法的发展而发展起来。go可以看作C语言在互联网时代的2.0版本,大牛背书,多线程高并发的特性,兼具效率和灵活性,随着区块链的发展崛起。另外,js在前端的发展也很迅速,也被用作unity3d的脚本同时和深度学习的convnetjs,lua则作为游戏脚本语言随着游戏的发展而发展。其他没有讨论到的还有matlab,ruby,object-C,delphi,pearl,swift,R,VB,pascal,Fortran使用范围较为受限且暂时没有进入前三的可能。

    编程语言就像人话一样,本身的优劣之分并没有想象的那么大,决定其优劣的根本原因是使用的人数的多少,一个新的领域的出现往往会推动一门编程语言的发展或者造就一门新的编程语言。但是,对于程序员来说,只需要知道编译原理,只需要知道只不过都是转化为01罢了,只需要知道水平低的时候选择哪门言能够最快最好的解决业务上的问题,水平高的时候随便那个都能用来做好用的框架给别人用就够了。所以说要选择的话,用python做实现,C/C++做工程应该是最好的,可惜的是,要达到能靠这两门语言快速解决所有问题,对一个程序员来说,有很长的一段路要走,但是如果要问有没有必要,就像问全世界的人是否应该说一种语言一样,可能只是一种不可能的必要性,因为世界上大多数人不是程序员。

     

    最后列举一下一些编程题和项目,这些项目包含了一门语言中的很多语法,可以很好的帮助快速熟悉一门编程语言

     

    1 数据结构与算法的实现。链表队列栈树查找排序基本上是笔试必须的。

    2 根据输入,读取指定文件中的内容,查找出其中包含的某个关键词后面的数值,做为程序流程的控制值,控制程序的执行,最后将执行结果显示出来,然后保存到指定文件中。包含了文件读写,判断循环,输入输出,强化版的配置文件读取。

    3 多线程生产消费模型的实现以及出现异常的异常捕获。(异常,多线程)

    4 图书馆的业务的类的实现(继承)

    5 小的图书馆网站的实现(mysql/mongodb/redis,html/Css,tomcat/apache/nginx)

    6 dll的调用以及生产exe还有混合编程的内嵌语法

    展开全文
  • 2020年5月份编程语言排行榜

    万次阅读 多人点赞 2020-05-10 15:51:08
    这段时间一直在忙,都忘记更新这个排行榜了,今天重操旧业,给大家看一下5月份的编程语言排行榜 TIOBE排行榜5月份数据 2020年5月TIOBE指数 以下是官方说明 五月标题:编程语言C又回到了第一位 Java和C在4月份已经...

    前言

    本文章中语言排名数据来自TIOBE排行榜和PYPL排行榜。

    这段时间一直在忙,都忘记更新这个排行榜了,今天重操旧业,给大家看一下5月份的编程语言排行榜

    TIOBE排行榜5月份数据

    在这里插入图片描述

    2020年5月TIOBE指数

    以下是官方说明

    五月标题:编程语言C又回到了第一位

    Java和C在4月份已经非常接近了,但是这个月C再次超越了Java。上一次C排名第一是在2015年。我们只能猜测为什么C又是第一名。其中一个原因可能是冠状病毒。这听起来可能很傻,但有些编程语言确实从这种情况中受益。数据科学领域的例子有Python和R,因为每个人都在寻找病毒的解毒剂。但是,嵌入式软件软件如C和C++也越来越受欢迎,因为它们被用在医疗设备的软件中。另一方面,值得一提的是,Rust现在已经接近前20名(一个月内从27名升至21名)。-保罗詹森首席执行官蒂奥贝软件

    TIOBE编程社区索引是编程语言流行程度的一个指标。索引每月更新一次。评级是基于全球熟练工程师、课程和第三方供应商的数量。流行的搜索引擎,如谷歌,必应,雅虎!,维基百科,亚马逊,YouTube和百度被用来计算收视率。需要注意的是,TIOBE索引并不是关于最好的编程语言,也不是大多数代码都是用哪种语言编写的。

    该索引可用于检查您的编程技能是否仍然是最新的,或者在开始构建新的软件系统时,对应采用何种编程语言作出战略决策。TIOBE指数的定义可以在 这里 找到。

    TIOBE编程语言变化图

    Y3GQFs.png

    其他编程语言

    下面列出了完整的前50种编程语言。此概述是非正式发布的,因为可能是我们错过了某种语言的情况。如果您觉得缺少一种编程语言,请通过tpci@tiobe.com通知我们。另请检查我们监视的所有编程语言的概述。
    在这里插入图片描述

    未来50种编程语言

    以下语言列表表示#51至#100。由于差异相对较小,因此仅列出了编程语言(按字母顺序)。

    • (Visual) FoxPro, ABC, Awk, Bash, BBC BASIC, bc, Bourne shell, C shell, cg, Clean, Clojure, CoffeeScript, Common Lisp, Crystal, cT, Elixir, Euphoria, Falcon, Forth, Hack, Icon, Inform, Io, Korn shell, Ladder Logic, LiveCode, Maple, Mercury, ML, MQL4, NATURAL, Object Pascal, OCaml, OpenCL, Oz, PL/I, PostScript, Programming Without Coding Technology, Q, Raku, Red, Ring, S, SPARK, Standard ML, Stata, Tcl, Vala/Genie, Verilog, VHDL

    本月指数变化

    本月对索引的定义进行了以下更改:

    • Warejo注意到Wikipedia将DWScript重定向到Object Pascal。因此,截至本月,DWScript搜索词已从Delphi转移到Object Pascal。
    • 由于C ++ 11和C ++ 14在Wikipedia上拥有自己的页面,因此这些搜索字词不再用于C ++。这是阿兰·德克(Alain Dekker)提出的建议。
    • 还有很多邮件仍需要处理。只要有更多时间可用,您的邮件就会得到答复。请耐心等待。

    长期历史

    要查看大图,请在下面找到许多年前十大编程语言的位置。请注意,这些是12个月内的平均职位。
    Y3NggI.png

    编程语言名人堂

    列出所有“年度最佳编程语言”获奖者的名人堂如下所示。该奖项授予一年中评分最高的编程语言。
    Y3UKGd.png

    TIOBE结尾

    以上数据仅TIOBE提供,真实性仅供参考。
    TIOBE编程语言排行榜是编程语言流行趋势的一个指标,每月更新,这份排行榜排名基于互联网有经验的程序员、课程和第三方厂商的数量。排名使用著名的搜索引擎(诸如Google、MSN、Yahoo!、Wikipedia、YouTube以及Baidu等)进行计算。请注意这个排行榜只是反映某个编程语言的热门程度,并不能说明一门编程语言好不好,或者一门语言所编写的代码数量多少。


    官方地址:https://www.tiobe.com/tiobe-index/

    PYPL排行榜5月份数据

    2020年5月在全球范围内,与一年前相比:
    Y3a0te.png

    PYPL结尾

    PYPL排行榜也是一个关于编程语言流行度的参考指标,其榜单数据的排名均是根据榜单对象在 Google 上相关的搜索频率进行统计排名,原始数据来自 Google Trends,也就是说某项语言或者某款 IDE 在 Google 上搜索频率越高,表示它越受欢迎。如果你相信这些来自众多开发者以及用户的选择,我们可以将 PYPL 作为一个参考,以帮助决定学习何种语言或 IDE,或者在新的软件项目中使用何种语言或数据库。


    官方地址:http://pypl.github.io/PYPL.html

    最后

    如果有什么想法或者别的可以打开本网站链接或者在小程序评论哦!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 脚本语言和编程语言的区别

    万次阅读 2017-04-15 22:05:31
    脚本语言是一个不需要显示编译的编程语言 例如,在日常例子中,你必须要编译一个C语言程序,在你运行他们之前.但在通常情况下,你不用编译一个JavaScript程序,在你使用他们之前.因此呢,JavaScript经常被称为"脚本语言"....

     

    脚本语言不需要显式的进行编译的一门语言。

    例如,在编写C语言程序时候,在运行之前需要经过编译和链接的过程。但对于一些脚本语言,例如javascript,你不用编译一个JavaScript程序,比如shell脚本,也不要编译,直接通过解析器进行解析执行即可。

    在现代硬件和编译技术下,编译的效率更高,花费时间更短。从而导致脚本语言和传统的编程语言之间的界限不再那么明显

    需要注意的是,一个语言是不是"脚本"不应该只从语言角度去看待,应该从运行所在的环境去区别。你完全可以写一个C语言的解释器,把C语言作为输入让解释器来帮你完成执行。同样也可以将JavaScript编译成机器代码并且存放在可执行文件中.Ruby就是一个很好的例子,它以一个解释器(脚本语言)出现,但现在已经有多个编译器可以编译它了.

    "脚本"语言例子(传统的角度)

    • Lua
    • JavaScript
    • VBScript and VBA
    • Perl

    传统需要编译的例子:

    • C
    • C++
    • D
    • Java(需要注意,Java被编译成字节码(bytecode),这些字节码会在运行的时候解释)

    Python有两种情况:Python在广泛的使用中不需要编译,但是主要的实现(CPython)需要编译成字节码并且运行字节码在虚拟机中,它可以把字节码写入到文件(.pyc.pyo)中,使用时,不需要重新编译

    当然这里只是冰山一角,更多的内容有待探索.

    展开全文
  • 本文是《打破国外垄断,开发中国人自己的编程语言》系列文章的第1篇。本系列文章的主要目的是教大家学会如何从零开始设计一种编程语言(marvel语言),并使用marvel语言开发一些真实的项目,如移动App、Web应用等。

    下一篇:使用监听器实现计算器

    阅读本系列文章将是“最残酷的头脑风暴,大家做好准备了吗

    本文是《打破国外垄断,开发中国人自己的编程语言》系列文章的第1篇。本系列文章的主要目的是教大家学会如何从零开始设计一种编程语言(marvel语言),并使用marvel语言开发一些真实的项目,如移动App、Web应用等。marvel语言可以通过下面3种方式运行:

    1. 解释执行

    2. 编译成Java Bytecode,利用JVM执行

    3. 编译成二进制文件,本地执行(基于LLVM)

    本系列文章实现的marvel语言并不像很多《自己动手》系列一样,做一个玩具。marvel语言是一个工业级的编程语言,与kotlin、Java等语言是同一个级别,设计之初是为了试验编程语言的新特性。我们团队开发的超平台开发系统UnityMarvel内嵌的Ori语言的部分特性也是来源于Marvel。关于UnityMarvel的细节后面会专门写文章介绍。这里先讨论编译器的问题。

    1.  如果系统软件受到制约,有没有可能突出重围呢?

    我们知道,现在中美贸易战如火如荼,可能以后使用国外很多软件,尤其是系统软件,都会有一些问题。这就需要我们在一些关键领域有自己可以控制的技术和软件,例如,操作系统、编程语言、数据库、科学计算软件等。其实这些种类的软件中,大多都属于基础软件,只有操作系统和编程语言(以及相关的IDE)可以称为是系统软件。

    这里先说说基础软件和系统软件的区别。基础软件是指很多软件都依赖的软件,例如,流行的程序库(如tensorflow、pytorch等)、数据库(如MySQL、Oracle等)。但大多数基础软件的一个共同特点是只服务于特定领域,例如,你不可能用MySQL开发一款游戏,也不可能用tensorflow开发移动App。而在基础软件中有一小类,它们是通用的,几乎适合于各个领域,我们将这类软件称为系统软件。它们是整个IT领域的基础架构。没有它们,整个IT领域将不复存在。例如,目前,只有操作系统和编译器符合这两个特征。大家可以想想,没有了关系型数据库,还有其他类型的数据库可以使用,没有了tensorflow,IT领域也不会停止运转。但没有了Windows、macOS、Linux、C语言、Java语言这些技术,世界将会怎样,将会重新退回到工业文明时代。所以系统软件是基础软件的一个子集,而且必不可少。如果将基础软件和其他软件比作星球,那么系统软件就是星核。

    在系统软件中,编译器是最容易突破的。因为编译器(编程语言)的生态相比操作系统来说,更容易建立。这是因为目前有很多虚拟机可以选择,例如,最常用的是JVM,当然,还有微软的.net core等技术。如果我们的编程语言可以基于JVM,那么就意味着可以利用Java语言的所有生态,如果我们的编程语言可以用更容易的方式调用其他语言(如C++、Go等),在某种程度上,也就可以直接使用这些编程语言的生态。当然,还有更先进的超生态技术(UnityMarvel的Ori语言正是基于超生态技术的),总之,作为一种新的编程语言,利用其他的生态是最廉价的方式,当然,在语言发展的过程中,也可以逐渐建立自己的生态(相当于骑驴找马),这也是一种策略。所以如果想突破,编译器(编程语言)是最容易的一个。当然,如果拥有自己可以控制的编程语言,可以为后期的操作系统提供支援,例如,利用超生态技术,在建立新操作系统之前,就为该操作系统提前建立生态(这一点以后专门撰文阐述)。

    2. 开发编程语言需要哪些知识

    现在进入到最关键的部分了,开发一种编程语言到底需要哪些知识呢?其实需要的知识还是蛮多的。最基础的要求是必须至少会一种编程语言。如C、C++、Java、C#、Go、Python等。当然,推荐会3种以上的编程语言,因为我们是在设计编程语言,不是在设计普通的软件。在设计编程语言时,需要进行横向比较,也就是需要参考其他的编程语言,因为任何新技术都不可能100%完全凭空产生,这些新技术都会或多或少地留下其他同类技术的影子,编程语言也不例外。例如,UnityMarvel内嵌的Ori语言就是参考了数十种编程语言,以及加入了自己的新技术而最终形成的。

    除了要了解大量的编程语言外,还有很多与业务有关的知识需要掌握。主要的知识结构(不仅仅这些,后面用到了再详细讲)如下:

    (1)了解大量的编程语言(推荐3种以上)

    (2)编译原理的基础知识

    (3)算法能力

    (4)编译器前端生成器

    (5)学习能力

    (6)想象力

    尽管开发编程语言并不会像大学学的编译原理一样从0开始构造一个编译器,但编译原理的基础知识还是要掌握的,不了解编译原理的同学,赶紧上B站、西瓜视频、油管去补课,后期我也会结合marvel语言做相关的视频课程,大家可以关注哦!

    算法就不必说了,编译器里面充斥着各种算法,编译器的算法密度几乎超过了绝大多数应用。任何形式的算法都可能涉及到,最基础的数据结构必须掌握,其他的算法,能学多少就学多少,多多益善。这个没有固定的教程,也是需要不断在实践中学习。

    开发编译器的基本步骤如下图所示。

    首先说明一点,并不是所有的编译器都严格按照这些步骤进行,有可能会将多个步骤合成一个步骤(例如,语法分析和语义分析合成一步,最后输出AST),也有可能将一步分成多个步骤,或者再增加一些与业务相关的步骤。

    对于工业级编译器来说,并不会从0开始实现词法和语法分析器,并不是这东西有多难,而是如果完全手工编写代码,要添加或修改一个新语法,那简直就是一场噩梦,因为要修改非常多的地方,而且一旦出错,非常不好找原因(因为代码过于复杂)。由于词法分析和语法分析有规律可循,所以出现了很多通过文法生成词法分析器和语法分析器的工具,由于词法分析与语法分析是编译器前端的重要组成部分,所以这类工具通常称为“编译器前端生成器”。比较著名的包括lex、yacc、javacc、antlr等。其中lex是专门用来生成词法分析器的,yacc用来生成语法分析器的,javacc可以同时生成词法和语法分析器、antlr也同样可以生成词法分析器和语法分析器。不过lex和yacc只支持C语言,javacc只支持Java语言。而antlr支持多种编程语言,例如Java、C++、JavaScript、Go、C#、Swift等。本系列文章也使用了antlr的最新版本antlr4来实现编译器的前端(词法分析器和语法分析器)。

    这几种工具都是依赖于文法生成词法分析器和语法分析器的,例如,在antlr4中,如果要识别加减乘除四则运算,只需要编写下面的文法即可。

    expr:   expr op=('*'|'/') expr     
        |   expr op=('+'|'-') expr
    

    文法是不是很简单呢?但如果要编写完善的代码,可能需要上百行才能实现(我们团队实现的Ori语言,利用antlr4生成的词法和语法分析器,总共6万行Go语言代码,我们自己编写了大概4万行Go代码,整个编译器有超过10万行代码,3/5是自动生成的,2/5是自己编写的)。而且文法还标识了优先级,antlr4规定,写在前面的文法的优先级高于写在后面的文法的优先级。我们知道,对于四则运算来说,是先乘除,后加减,所以expr op=('*'|'/') expr 应该在expr op=('+'|'-') expr 前面,倒过来是不行了。如果要加更复杂的运算,例如,平方、开方、幂等,只需要修改这个文法即可,是不是很简单呢?

    前面说的前4点是硬知识,也有很多教程可以学习,但最后两点:学习能力和想象力,就要完全靠自己的天赋了。因为前面4点能让你做出一个看着还不错的编译器,但最后两点能决定你做的编译器有多强大。

    实现一个编程语言,所涉及到的知识要比实现编译器难度更大。因为如果实现编译器,并且是已经存在的编程语言,由于语法已经确定,所以只需要实现出来即可。但编程语言不同,一切需要重新设计,尤其是在涉及到新语法时,非常困难,需要了解的知识相当多,所以需要拥有快速学习能力,可以在短时间内学会并掌握任何知识和技术。另外,想象力更重要,因为设计一款新的编程语言,有些东西可能不仅仅局限于IT领域,也不仅仅局限于自己所从事的技术领域,例如。在Ori语言中,拥有一些创新的语法,需要同时适应类似JavaScript的单线程模式和Java的多线程模式。因此,拥有多维度的想象力才是最终取得胜利的关键。

    3. 自己设计的编程语言会流行吗

    我经常在网上看到很多同学在问,为什么中国没有自己流行的编程语言(尽管有易语言,但由于是中文编程,所以注定不会全球流行,国内也并不算流行)呢?BAT等大厂为何不开发一个呢?然后有人回答,开发编程语言容易,关键是生态,还有人回答,BAT是因为没有必要,因为编程语言没有和KPI挂钩,也有些人回答,开发一款编程语言,火起来很难。其实这些都可能是原因,但主要原因其实就是需求没有与行动挂钩,或者说,现在的编程语言已经足够满足需求了,没有必要再开发一款新的编程语言,而且这些大厂的盈利压力都很大,当然,还有技术积累的问题。

    其实编程语言有很多种,有一种就是像Java、C#、C++一样的通用编程语言,这类语言什么都能做,是一种图灵完备的编程语言。还有另外一种编程语言,如SQL、VBA、ABAP(SAP的内嵌语言),这类属于领域编程语言,他们也可能是图灵完备的,也可能不是图灵完备的。通常使用这类编程语言完成某些特定的工作,如SQL操作数据库,VBA操作Office、ABAP操作SAP数据等。其实在国内有很多公司内部已经提供了类似的领域语言,只是非常专业,功能单一,绝大多数人不清楚而已。

    至于自己开发出来的编程语言是否会流行,其实你们想太多了。编程语言是为了解决实际问题而存在的,不是为了流行而存在的。就像衣服,最初的用途是为了保暖,而不是时尚,当大多数人都使用自己生产的衣服保暖,那他就是流行款了!所以让编程语言解决实际问题才是优先要考虑,至于以后是否会流行,自己说了不算!

    像我们团队开发的UM系统,其实原来压根就没打算自己开发编程语言,想直接使用JavaScript,不过后来发现,JavaScript太动态了,使用JavaScript根本没有办法做一款完美的IDE,而且功能有限,并且混乱。还有就是JS是动态语言,如果将其转换为静态语言,会以牺牲性能为代价,而且无法有效融合单线程和多线程的特性,并且还无法与UM IDE融为一体,所以没办法,才开发一款自己的编程语言Ori,并且融合了数十种编程语言的优秀特性,而且加入了更先进的特性(如内嵌SQL、虚拟组件、虚拟数据库、支持跨平台的语法、客户端服务端一体化、柔性热更新等),当然,这些特性需要与UM IDE配合才能使用。

    4.  开发编程语言,从这里起航:配置Antlr4环境

    如果一上来就开发编程语言,估计大家就开始晕了,所以我们先从最简单的开始,就是先来编写一个可以解析加减乘除表达式的编译器。我们使用了antlr4来生成词法分析器和语法分析器,所以先要配置一下antlr4的开发环境。

    由于antlr4使用Java开发,所以不管用什么编程语言设计编译器,JDK必须安装,并且还需要一款强大的Java IDE,这里推荐Intellij IDEA。我们只使用Intellij IDEA的最基础功能,所以CE(社区版)版足够了,这个版本是免费的。

    在安装完Intellij IDEA CE后,到下面的页面下载antlr4工具相关的库。

    https://www.antlr.org/download.html

    进入页面,找到下面的部分,点击第1个链接下载即可。

    下载完antlr4的工具包后,找到其中的Java运行时库,并用Intellij IDEA CE创建一个Java工程,然后直接将Antlr4 Java运行时库复制到工程的lib目录中(没有lib目录可以建立一个),如下图所示。

    然后在lib目录的右键菜单中点击“Mark Directory as”>“Sources Root”菜单项,将lib编程源代码目录,这样Intellij IDEA CE就会搜索lib目录中的所有库。当然,可以直接在模块中引用antlr4的库,不过将antlr4 运行时库与工程放到一起,这样如果将工程复制到其他机器上,就不会由于antlr4的运行库没有复制而导致无法运行了。

    然后需要安装Intellij IDEA CE的Antlr插件。进入插件安装页面,如果没有安装antlr插件,选择Marketplace标签页,输入antlr搜索插件,通常第一个就是。点击右侧的install按钮即可安装。如果已经安装,Antlr插件会出现在Installed页面中,如下图所示。

           安装完Antlr插件后,新创建一个文件,将文件扩展名设置为g4,就会看到文件前面的图标变成了红色,里面有一个A字母,这就是Antlr4的标识,如下图所示。

    5. Antlr4的Hello World

    现在我们开始进入激动人心的时刻了,用Antlr4亲手做我们的第一个编译器:解析四则运算表达式的计算器。不过在完成这个编译器之前,一定要了解一下Antlr4。

    下面先给出一个可以识别以hello开头的词组的识别程序的文法。首先创建一个名为Hello.g4的文件,并输入下面的代码:

    grammar Hello;
    r  : 'hello' ID ;
    ID : [a-z]+ ;
    WS : [ \t\r\n]+ -> skip ;
    

    大家先不需要管这些代码是什么意思,只需要照猫画虎输入即可。

    然后在Hello.g4右键菜单点击“Configure ANTLR”菜单项,会弹出如下图的对话框,设置第一个文本输入框,指定生成目录,这里指定与Hello.g4相同的目录。Hello.g4生成的文件都会放在这个目录中。

    然后点击Hello.g4右键菜单的“Generate ANTLR Recognizer”菜单项,会自动生成一堆文件,如下图所示。注意:Java文件都隐藏了扩展名。

    Hello.java和MyHelloVisitor.java是后来创建的,其他文件都是自动生成的。其中HelloLexer.java是词法分析器、HelloParser.java是语法分析器,其他文件后面再说。

    大家可以打开这两个文件,看到每一个文件的内容都有上百行,这要是人工编写,会累死人,而使用Antlr4,只需要4行文法就搞定。如果要添加或修改原来的语法,只需要修改Hello.g4文件,然后再重新生成一遍即可。

    现在有一个问题,怎么用Hello.g4生成的一堆文件呢?或者换种问法,生成的这些文件有什么用呢?

    Hello.g4生成的这些文件的主要目的就是进行词法分析和语法分析,那么如何用呢?使用有如下两种方式:

    1.  用grun工具测试

    2. 用Java代码调用词法分析器和语法分析器,编写完整的编译器

    现在先来说说grun工具。其实并没有grun这个东西,grun是一个别名,真实的工具在是antlr-4.8-complete.jar中的 org.antlr.v4.gui.TestRig类,在macOS或Linux下,可以使用alias命令起一个别名,官方叫grun,所以这里就沿用了官方的叫法。如果在windows下,可以创建一个grun.cmd文件。

    起别名的完整命令如下:

    alias grun='java   -classpath  .:/System/Volumes/Data/sdk/compilers/antlr4-4.8/antlr-4.8-complete.jar org.antlr.v4.gui.TestRig'
    

    现在就可以使用grun测试我们的程序了。

    首先要说明一点,grun测试的是.class文件,不是.java文件,所以在测试之前,要在终端中切换到.class文件所在的目录。Intellij IDEA CE默认的.class目录是out/production目录,如下图所示。在一开始,前面生成的.java文件并没有编译,读者可以随便找个Java程序运行下,这时Intellij IDEA CE会编译所有还没有编译的.java文件,我们会发现,刚才生成的所有.java文件都生成了同名的.class文件。

    读者可以直接在操作系统的终端进入.class所在的目录,或者通过Intellij IDEA CE下方的Terminal也可以输入命令行,如下图所示。

    现在来做我们的第一个测试:

    首先输入下面的命令(先不需要管命令是什么意思):

    grun Hello r -tokens
    

    然后输入下面的内容:

    hello world
    

    如果读者在macOS或Linux下,按Ctrl+D,如果在Windows下,按Ctrl+Z输入结束符号,会输出如下图的内容:

    现在来解释一下grun Hello r -tokens是什么意思。Hello表示Hello.g4中grammar后面的部分,也就是Hello。r是文法产生式等号左侧的符号(非终结符),也就是r  : 'hello' ID ;中的r。-tokens表示列出所有的tokens。

    那么什么是token呢?其实token是词法分析器的输出,同时,token将作为语法分析器的输入,而AST(抽象语法树)则是语法分析器的输出。

    token就是编程语言中不可再分的单元,相当于编程语言的原子。看下面的程序:

    if(i == 10) {
    }
    

    这是一个非常简单的条件语句,那么在这两行代码中,有多少个token呢?根据token不可分割的原则,包含如下的token:

    if,(,i,==,10,),{,}
    

    上面用逗号(,)分隔的符号都是token,例如,if是关键字,将作为一个整体对待,在解析代码时,肯定不会将if拆开,10是一个整数,也将作为一个整体对待,肯定不会将其拆成1和0。

    那么Hello的输出结果意味着什么呢?我们输入了hello world,根据语法规则。任何字符串都需要以hello开头,所以hello将作为一个token(相当于前面条件语句的if关键字,这里hello是一个关键字)。而后面可以是任意字符串,但与hello之间至少要有一个空格。所以hello world符合Hello的语法规则,hello  abc也同样符合,而helloabc就不符合了,因为hello和abc之间没有任何分隔符,根据最长匹配原则,Antlr4会选择最长的字符串进行匹配,所以匹配的是helloabc,而不是hello。

    现在我们的实验也做完了,可能很多读者还是一头雾水,不过不要紧,我们再详细讲一下Antlr4到底是怎么分析的。

    Antlr4采用了自顶向下递归的分析方式。自顶向下就是先将整个编程语言源文件看成一个整体,这就是入口点,也就是Hello.g4中的r。这个入口点起任何名字都可以,只要不和其他的文法标识重名即可。然后从这个入口点开始,就可以用递归的方式写文法了。文法用于从上到下推导,左侧是文法标识,右侧是文法的产生式。例如,要识别下面一组字符串:

    hello world hello abc hello Bill hello 李宁

    很明显,这4行文本都是以hello开头,后面跟着任意的字符串,中间用空格分隔。所以我们的文法应该是以hello开头,后面跟一个标识,用ID表示。文法如下:

    r : 'hello' ID;
    

    在Antlr4中,每一个文法都要用分号(;)结尾,如果是固定的字符串,如关键字,用单引号括起来。如'hello'。

    ID表示任意的标识符,也是终结符。所谓终结符,是指不能再继续往下推导的符号(相当于树的叶子节点)。在Antlr4中,终结符标识用由首字母大写的字符串表示,如ID。而非终结符(可以继续往下推导)用首字母小写的字符串表示,如r。

    现在是自顶向下分析的第1步,第2步是处理ID。文法如下:

    ID : [a-z]+ ;
    

    ID的产生式不包含任何的非终结符,也就是再也无法继续推导了。[a-z]是一种简写,也就是a到z共26个小写字母中的任何一个,后面的加号(+)表示至少要有一个小写字母。

    到现在为止,自顶向下分析的过程已经完成了,分为两步,第一步将整个字符串看做一个整体,并且将其分解为hello和后面的任意字符串。第二部来处理这个任意字符串。这里规定,这个任意字符串只能由小写字母组成。

    不过现在还有一个问题,Antlr4怎么知道hello和world之间需要有空格或其他空白符分隔呢?其实这就涉及到Hello.g4的最后一行代码了:WS : [ \t\r\n]+ -> skip ;   这行代码设置了一个skip通道(通道会在后面的文章中详细讲解),用于忽略指定的字符,这些被忽略的字符,将作为token的分隔符,这里面指定了4个分隔符:空格、制表符(\t)、回车符(\r)、换行符(\n)。也就是说,下面的形式也是可以的:

    hello
    world
    

    ok,现在Hello.g4的语法规则已经讲的差不多了,里面涉及到了一些概念,在后面的文章中会详细讲解。现在来总结一下:

    Antlr4的文法文件是以g4作为扩展名,第一行代码必须以grammar开头,后面跟着语法名,如Hello,该名字必须与g4文件名一致。每一行代码都必须用分号(;)分隔。然后就是若干文法产生式了。例如,Ori语言的最顶端文法是这样的。

    grammar Ori;
    program : sourceElements? EOF
    sourceElement   : statement
    statement
    :
        importStatement
    | sqlStatement
        | dollarMemberStatement        
    |  classDeclarationStatement
        |  interfaceDeclarationStatement
    | functionDeclarationStatement
        | variableStatement     
    | ifStatement                
        | iterationStatement         
    | continueStatement           
        | breakStatement       
    | returnStatement             
        | withStatement             
    | switchStatement           
        | throwStatement           
    | tryStatement                 
        | blockStatement  
    | expressionStatement
        | commentStatement
        ;
    

    program是Ori语言的入口点,然后Ori语言将整个语言分成若干源代码元素(sourceElements?),后面的问号表示可选,也就是说,Ori语言的源代码文件可以是空文件。EOF是文件结束符。这里讲每一个源代码元素对应一条statement(语句),这里之所以不直接使用statement,而是使用sourceElement,是因为以后可能会进行扩展,这时只需要修改sourceElement即可(目前sourceElement等于statement),而一条语句包括多种,如ImportStatement、sqlStatement(内嵌SQL)、classDeclarationStatement(类声明)等。然后就继续往下分,如sqlStatement还会包含sqlInsert、sqlUpdate等。以此类推,直到不可再分为止。这就是自顶向下分析的基本方法,其实这就是分治法的一种表现,尽管编程语言看着很复杂,一个大型系统可能会有上百万甚至更多行代码,但如果将编程语言从顶向下分析,涉及到的语句种类也不过几十种而已。Ori语言的文法文件也就1000多行,包括词法文件部分,也就2000行出头。用2000行代码,就可以完全描述一种图灵完备的编程语言,真是perfect。而这2000行代码,生成的Go语言代码超过了60000行。

    现在再回到grun工具上来。其实grun的功能很强大,除了可以作为测试工具外,还可以显示Antlr4生成的AST,看一下自顶向下分析的流程。

    首先准备一个hello.txt文件,并输入hello world。然后在终端输入下面的命令(读者要将hello.txt文件的路径改成自己机器上的路径):

    grun Hello r -gui < /MyStudio/java/java_knowledge/antlr/test/hello.txt
    

    然后就会弹出如下图的窗口,右侧显示了AST的树状结构。Antlr4制作编译器的过程就是先根据源代码生成AST,然后对AST进行遍历(根据语言的特性,会遍历1到n遍),遍历完后,就会生成中间代码、以及最终的二进制文件。所以AST起到了承前启后的作用。

    6. 如何用程序进行词法和语法分析

    尽管已经了解了Antlr4的基本使用方法,但到现在为止,还没有用Java编写过一行代码呢?现在我就来演示如何用Java调用上一节生成的词法分析器和语法分析器。

    下面先给出实现代码:

    首先创建一个MyHelloVisitor.java文件,并输入下面的代码:

    import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
    
    
    public class MyHelloVisitor extends AbstractParseTreeVisitor<String> implements HelloVisitor<String> {
    
    
    @Override public String visitR(HelloParser.RContext ctx) {
        System.out.println(ctx.getText());
        System.out.println(ctx.ID().getText());
    return visitChildren(ctx);
      }
    }
    然后再创建一个Hello.java文件,并输入下面的代码:
    import org.antlr.v4.runtime.CharStream;
    import org.antlr.v4.runtime.CharStreams;
    import org.antlr.v4.runtime.CommonTokenStream;
    import org.antlr.v4.runtime.tree.ParseTree;
    
    
    public class Hello {
    public static void main(String[] args) throws Exception  {
    // 读取源代码文件,这里选择直接从字符串读取
             CharStream input = CharStreams.fromString("hello world");
    // 创建词法分析器对象 
            HelloLexer lexer = new HelloLexer(input);
    // 获取词法分析器输出的tokens
            CommonTokenStream tokens = new CommonTokenStream(lexer);
    // 创建语法分析器对象,并将词法分析器输出的tokens作为语法分析器的输入
            HelloParser parser = new HelloParser(tokens);
    // 开始分析程序,这也是生成AST的过程
            ParseTree tree = parser.r();    // 文法的入口点r会转换为一个方法,调用该方法,就会自顶向下递归分析源代码
    // 创建Visitor对象
            MyHelloVisitor hello = new MyHelloVisitor();
    // 开始遍历AST
            hello.visit(tree);
        }
    }
    

    现在运行Hello.java,如果在Run窗口输出如下图的内容,说明运行成功了。

    现在来解释一下前面的代码。这里先要知道Antlr4是如何遍历AST的。Antlr4有如下两种方式遍历AST:

    (1)listener

    (2)visitor

    第一种方式更灵活,但不容易使用。visitor不灵活,但容易使用。本例使用了第2种方式来遍历AST,但本系列文章的大多数代码主要使用listener来遍历AST。listener方式会在后面的文章中详细介绍,这里主要介绍visitor。其实这两种遍历AST的方式的原理类似,都是遇到了一个节点,就会调用相应的回调方法,然后将必要的信息作为参数传入回调方法,用户可以在回调方法中完成代码生成、数据处理、中间代码优化等工作。那么这些回调方法放在哪里呢?这就要说到前面创建的MyHelloVisitor类。该类实现了HelloVisitor接口,该接口是根据Hello.g4文件自动生成的,代码如下:

    import org.antlr.v4.runtime.tree.ParseTreeVisitor;
    public interface HelloVisitor<T> extends ParseTreeVisitor<T> {  
    T visitR(HelloParser.RContext ctx);
    }
    

    我们可以看到,该接口中只有一个方法,就是visitR,该方法是遍历到r节点调用的回调方法。

    如果文法文件很大时,会生成相当多的回调方法,例如,Ori语言的文法就生成了数百个回调方法,这些回调方法并不一定都用到,在这种情况下,并不需要实现所有的回调方法,所以Antlr4在生成回调接口文件的同时,还生成了一个默认实现类,如本例的HelloBaseVisitor,默认实现类已经默认实现了所有的回调方法,我们的Visitor类只需要从该类继承,就只需要实现必要的回调方法即可。

    import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
    
    
    public class HelloBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements HelloVisitor<T> {
    
    
    @Override public T visitR(HelloParser.RContext ctx) { return visitChildren(ctx); }
    }
    

    本例的MyHelloVisitor类继承了HelloBaseVisitor类,并覆盖了visitR方法,输出了r节点的文本和ID的文本。

    对于Hello类来说,就是最终的调用代码了。通常一个用Antlr4实现的编译器,需要经过如下几步:

    (1)读取源代码文件(或直接从字符串获取源代码)

    (2)创建词法分析器(输入是单个字符、输出是tokens)

    (3)创建语法分析器(输入是tokens、输出是AST)

    (4)开始遍历AST

    这4步已经在Hello类中做了详细的注释,大家可以自行查看。

    7.  弄一个可以解析表达式的计算器

    前面已经给出了一个完整的Antlr4案例,不过这个案例太简单了,没什么实际的用途,本节会利用Antlr4实现一个有实际价值的计算器程序。该程序可以解析过个表达式,表达式包含加减乘除运算,每一个表达式占一行,用分号(;)结尾。

    先给出文法:Calc.g4

    grammar Calc;
    // 下面是语法
    prog:   stat+ ;
    
    
    stat:   expr ';'                # printExpr
        |   ID '=' expr ';'         # assign
        |   NEWLINE                 # blank
        ;
    
    
    expr:   expr op=('*'|'/') expr      # MulDiv
        |   expr op=('+'|'-') expr      # AddSub
        |   INT                         # int
        |   ID                          # id
        |   '(' expr ')'                # parens
        ;
    // 下面是词法
    MUL :   '*' ;
    DIV :   '/' ;
    ADD :   '+' ;
    SUB :   '-' ;
    ID  :   [a-zA-Z]+ ;      // 匹配标识符
    INT :   [0-9]+ ;         // 匹配整数
    WS  :   [ \t]+ -> skip ; // 忽略空白符
    NEWLINE:'\r'? '\n' ;     // 空行
    

    现在生成Calc.g4 的相关文件。先看一下生成的CalcVisitor.java文件,代码如下:

    import org.antlr.v4.runtime.tree.ParseTreeVisitor;
    public interface CalcVisitor<T> extends ParseTreeVisitor<T> {
      T visitProg(CalcParser.ProgContext ctx);  
      T visitPrintExpr(CalcParser.PrintExprContext ctx);
      T visitAssign(CalcParser.AssignContext ctx);
      T visitBlank(CalcParser.BlankContext ctx);
      T visitParens(CalcParser.ParensContext ctx);
      T visitMulDiv(CalcParser.MulDivContext ctx);
      T visitAddSub(CalcParser.AddSubContext ctx);
      T visitId(CalcParser.IdContext ctx);
      T visitInt(CalcParser.IntContext ctx);
    }
    

    CalcVisitor有9个回调方法,从文法上看,有多少个文法,就应该有多少个回调方法。在Calc.g4中,除了第一个文法(prog:stat+;)外,其他的文法都起了别名,如printExpr,assign等。所以这些文法对应的回调方法都是以别名作为后缀的,然后前面加上visit。其实这9个方法,分别经过了AST的9个非叶子节点后(如果有的话),被分别调用。

    例如,现在测试这个表达式(将表达式放置expr.calc文件中):1+3 * 4 - 12 /5;    

    grun Calc prog  -gui < /MyStudio/java/java_knowledge/antlr/Calc/expr.calc
    

    执行上面的命令,会显示如下图的AST。

    要计算上述表达式,就需要遍历这棵AST。例如,当遍历到prog节点时,就会调用visitProg方法,通过该方法的参数可以获取prog节点的直接子节点的信息(就是左右两个stat节点)。当遇到减法表达式时,就会调用visitAddSub方法,以此类推。

    现在看一下EvalVisitor类的实现。该类的实现原理是当直接计算两个值时,如3 * 5、4 - 1,就分别由visitMulDivhe visitAddSub方法计算,并通过返回值返回计算结果。如果遇到变量(Calc支持变量),需要首先将变量放到一个Map中,然后在获取该变量时,会从Map读取。Map相当于一个符号表。

    import java.util.HashMap;
    import java.util.Map;
    
    
    public class EvalVisitor extends CalcBaseVisitor<Integer> {
    /** "memory" for our calculator; variable/value pairs go here */
    Map<String, Integer> memory = new HashMap<String, Integer>();
        boolean error = false;
    
    
    /** ID '=' expr NEWLINE */
    // 初始化变量的操作(赋值操作)
        @Override
    public Integer visitAssign(CalcParser.AssignContext ctx) {
    String id = ctx.ID().getText();  // id is left-hand side of '='
            int value = visit(ctx.expr());   // compute value of expression on right
            memory.put(id, value);           // store it in our memory
    return value;
        }
    
    
    /** expr NEWLINE */
    // 输出表达式的计算结果
        @Override
    public Integer visitPrintExpr(CalcParser.PrintExprContext ctx) {
    Integer value = visit(ctx.expr()); // evaluate the expr child
    System.out.println(value);         // print the result
    return 0;                          // return dummy value
        }
    
    
    /** INT */
    // 将字符串形式的整数转换为整数类型
        @Override
    public Integer visitInt(CalcParser.IntContext ctx) {
    return Integer.valueOf(ctx.INT().getText());
        }
    
    
    /** ID */
        @Override
    public Integer visitId(CalcParser.IdContext ctx) {
    String id = ctx.ID().getText();
    // 从Map中获取变量的值 
    if ( memory.containsKey(id) ) {
    return memory.get(id);
            } else {
    // 引用了不存在的变量,输出错误信息 
    System.err.println(String.format("变量<%s> 不存在!",id));
                error = true;
    
    
            }
    return 0;
        }
    
    
    /** expr op=('*'|'/') expr */
    // 计算乘法和除法
        @Override
    public Integer visitMulDiv(CalcParser.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() == CalcParser.MUL ) return left * right;
    return left / right; // must be DIV
        }
    
    
    // 计算加法和减法
    /** expr op=('+'|'-') expr */
        @Override
    public Integer visitAddSub(CalcParser.AddSubContext 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() == CalcParser.ADD ) return left + right;
    return left - right; // must be SUB
        }
    
    
    /** '(' expr ')' */
    // 处理括号表达式
        @Override
    public Integer visitParens(CalcParser.ParensContext ctx) {
    return visit(ctx.expr()); // return child expr's value
        }
    }
    

    最后看一下主程序(MarvelCalc)的源代码。

    import org.antlr.v4.runtime.ANTLRInputStream;
    import org.antlr.v4.runtime.CharStream;
    import org.antlr.v4.runtime.CharStreams;
    import org.antlr.v4.runtime.CommonTokenStream;
    import org.antlr.v4.runtime.tree.ParseTree;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    
    public class MarvelCalc {
        public static void main(String[] args) throws Exception  {
            // 从文件读取源代码 
            String inputFile = null;
            if ( args.length>0 ) {
                inputFile = args[0];
            } else {
                System.out.println("语法格式:MarvelCalc inputfile");
                return;
            }
            InputStream is = System.in;
            if ( inputFile!=null ) is = new FileInputStream(inputFile);
    
    
    
    
            CharStream input = CharStreams.fromStream(is);
    
    
            CalcLexer lexer = new CalcLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            CalcParser parser = new CalcParser(tokens);
            ParseTree tree = parser.prog(); // 分析源代码
    
    
            EvalVisitor eval = new EvalVisitor();
    
    
    
    
            eval.visit(tree);
        }
    }
    

    在expr.calc文件中输入下面的内容:

    1+3 * 4 - 12 /6;
    x = 40;
    y = 13;
    x * y + 20 - 42/6;
    z = 12;
    x + 5 * z - y;
    

    并使用下面的命令行执行计算器程序,或在IDE中将expr.calc作为参数允许MarvelCalc。

    java MarvelCalc expr.calc
    

    会得到下面的结果:

    11
    533
    87
    

    我们可以看到,在expr.calc文件中,有3个可以计算的表达式,其中最后两个表达式使用了变量,而输出结果就是这3个表达式的计算结果。从Calc.g4中也可以看出。语句一共有如下3种:

    (1) 输出表达式(包括运算、id和常量)

    (2)赋值表达式(创建变量)

    (3)空行

    从EvalVisitor类的实现可以看出,只有输出表达式才会输出结果,其他的表达式只是在内部计算,生成内部结果,如向Map中存储变量和值。

    OK,到现在为止,我们已经创建了一个非常实用的计算器程序,不过这个程序仍然很简单,在后面的文章中,将会不断利用新学到的知识完成更复杂的编译器程序,直到可以实现Marvel语言为止。

    展开全文
  • 2019年11月中国大陆编程语言排行榜

    万次阅读 多人点赞 2019-11-02 21:45:28
    针对招聘信息,提取编程语言关键字,并统计如下: 编程语言比例 rank pl_ percentage 1 java 33.62% 2 cpp 16.42% 3 c_sharp 12.82% 4 javascript 12.31% 5 python 7.93% 6 go 7.25% 7 p...
  • 很多培训机构宣称py是人工智能必备的编程语言,打着速成的旗号来引诱学者学习python。事实却并不是这样的,万丈高台平地起,不论你想从事怎样的编程工作,都是从最基本的编程技巧开始的;Python并不适合所有人,如果...
  • 编程语言比例 排名 编程语言 最低工资 工资中位数 最低工资 最高工资 人头 人头百分比 1 rust 20713 17500 5042 46250 480 0.14% 2 typescript 18503 22500 6000 30000 1821 0.52% 3 lua 18150 17500 ...
  • 操作系统的硬件控制功能,通常是通过一些小的函数集合体的形式来提供的。这些函数及调用函数的行为统称为系统调用(system call),也就是应用对操作系统(system)的功能进行调用(call)的意思。...
  • 2020年2月中国编程语言排行榜

    万次阅读 多人点赞 2020-02-03 21:13:59
    编程语言比例 排名 编程语言 最低工资 工资中位数 最低工资 最高工资 人头 人头百分比 1 rust 21433 20000 5266 45000 369 0.11% 2 typescript 18727 22500 6500 30000 1841 0.57% 3 go 18292 16000 ...
  • 22种编程语言新年快乐

    万次阅读 多人点赞 2020-01-24 18:46:11
    请允许我用22种编程语言,祝大家新年快乐 C语言:printf(“祝大家新年快乐”); C++ : cout<<“祝大家新年快乐”; OC: NSLog(@“祝大家新年快乐”) QBasic : Print “祝大家新年快乐” Asp : Response....
  • 热门编程语言间的差异

    万次阅读 2020-10-12 00:18:13
    热门编程语言对比 1. C 用途 操作系统开发 软件开发 硬件设计开发 优点 C 语言高度可移动,在不同平台上运行几乎不需要做修改; C 语言基于变量、宏命令、函数和架构,几乎可以嵌入所有线代微型处理器; 几乎所有...
  • 2019年12月中国编程语言排行榜

    万次阅读 多人点赞 2019-12-07 19:22:04
    针对招聘信息,提取编程语言关键字,并统计如下: 编程语言比例 rank pl_ percentage 1 java 33.60% 2 c/c++ 16.58% 3 c# 12.59% 4 javascript 12.21% 5 python 7.96% 6 go 7.19% 7 php ...
  • TIOBE 8 月编程语言:C、Java 差距拉大,R 语言盛行

    万次阅读 多人点赞 2020-08-03 12:43:26
    编程语言社区 TIOBE 最新发布了 8 月编程语言排行榜。 相比上个月,本月 TIOBE 指数整个体变化并不大。C 语言依然保持强劲地增长势头,与第二名 Java 之间差距逐月增大,从上个月相差 1.35% 的份额逐步增长到 2.55% ...
  • TIOBE 1 月编程语言:Python 摘得 2020 年度编程语言

    千次阅读 多人点赞 2021-01-04 15:57:01
    【CSDN 编者按】恭喜 Python 荣获 2020 年度编程语言称号,同时,Python 也是自 TIOBE 榜单发布以来,首款四次获得该奖项的编程语言。 整理 | 苏宓 出品 | CSDN(ID:CSDNnews) 编程语言社区 TIOBE 最新发布了 1 ...
  • atitit 编程语言课程 v1 t55.docx 1. 编程语言概念(what 5 1.1. 自然语言与编程语言的关系 5 1.2. 开发中常用的编程语言 5 1.3. 编程语言代际 5 1.4. 编程语言能力模型金字塔 6 2. 学习编程语言的用途 7 ...
  • 常见编程语言

    万次阅读 2019-03-16 16:04:39
    编程语言排行榜 TIOBE排行榜是根据互联网上有经验的程序员、课程和第三方厂商的数量,并使用搜索引擎(如Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube统计出排名数据,只是反映某个编程语言的热门程度,并...
  • 编程语言种类

    万次阅读 2018-09-13 20:42:49
    编程语言种类 编程语言有很多种,常用的有C语言、C++、Java、C#、Python、PHP、JavaScript、Go语言、Objective-C、Swift、汇编语言等,每种语言都有自己擅长的方面,例如: 编程语言 主要用途 ...
  • 5 种编程语言可能注定失败!

    万次阅读 多人点赞 2019-07-31 17:01:30
    作者 |Nick Kolakowski编译 | 屠敏出品 | CSDN(ID:CSDNnews)并非所有编程语言都能长盛不衰。事实上,随着新一代开发者使用的语言与框架不...
  • 编程语言总结

    千次阅读 多人点赞 2018-12-12 21:22:58
    学了C/C++,并了解了一点Python,对编程语言之间的共性有一点点自己的认识,总结如下: 1、编程语言的结构 语言本身是一种实现沟通烦人工具,从这个角度考虑,编程语言本身是一个实现人与计算机交流,从而控制...
  • 2019年9月中国编程语言排行榜

    万次阅读 多人点赞 2019-09-05 08:30:59
    针对招聘信息,提取编程语言关键字,并统计如下: 编程语言比例 排名 编程语言 百分比 1 java 33.71% 2 cpp 15.95% 3 c_sharp 12.72% 4 javascript 12.70% 5 python 7.63% 6 go 7.37% 7 php ...
  • 编程语言社区 TIOBE 最新发布了 11 月编程语言排行榜。 本月的排行榜出现了自 TIOBE 榜单发布以来,近二十年从未见过的变化:前两名的位置首次出现了一个除 C 和 Java 以外的语言。C 依然位列第一,Java 跌至第三,...
  • TIOBE 9 月编程语言:C++ 突起、Java 流行度下降

    万次阅读 多人点赞 2020-09-07 18:00:00
    整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)编程语言社区 TIOBE 最新发布了 9 月编程语言排行榜。自从今年5月被C语言超越后,Java就一直位列第2,久久无法突破,...
  • 2020年5月中国编程语言排行榜

    万次阅读 多人点赞 2020-05-01 13:10:18
    编程语言比例 排名 编程语言 平均工资 工资中位数 最低工资 最高工资 人头 人头百分比 1 scala 20165 18000 7000 45000 3309 0.71% 2 rust 19364 17500 5398 43687 483 0.10% 3 python 18622 17500 6500...
  • 常用编程语言

    千次阅读 2019-03-26 12:16:47
    编程语言 一.编程语言概念: 是用来定义计算机程序的形式语言。它是一种被标准化的交流技巧,用来向计算机发出指令。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所...
  • 今天看到新闻: 心头一震,看起来很牛逼啊,毕竟前几天美帝宣布要对中国AI软件进行限制 这是要还美帝一巴掌的节奏啊,顿时来了兴趣,赶紧下载一个尝尝鲜 网上很多类似的新闻,都已经这么普及了,生态链也不错...
  • 编程语言变化

    千次阅读 2019-12-12 12:13:11
    了解编程语言变化趋势, 初步了解市场情况, 提供初步思考决策依据. 2.学习/操作 1.编程语言排行榜 https://hellogithub.com/report/tiobe/?url=/periodical/category/PHP%20%E9%A1%B9%E7%9B%AE/ ...

空空如也

1 2 3 4 5 ... 20
收藏数 291,846
精华内容 116,738
关键字:

编程语言