对于antlr4的基础使用,请参考我的前一篇文章《用antlr4来实现<按编译原理的思路设计的一个计算器>中的计算器》。

其实我对于antlr4的理解也仅限于那篇文章的范围,但那些内容对于我们实现一个SNL语言已是足够了。

SNL语言的简介可以百度百科(http://baike.baidu.com/subview/2298006/5909410.htm)中看到。

简单说来,这是一个非常简单的结构化编程语言,它包含了一个编程语言应该有的最基本的东西,同时基本上不包含其它的所谓先进的元素。

首先贴一下文法(我对原始的文法做了一些调整,如:原始的文法中if语句的else部分不是可选的,原始文法中record类型内部只能有基础类型或array类型,等等。但我所做的这些调整都是比较边缘的,以下文法基本上保持了SNL语言的原貌):


grammar Snlc;

@header{package net.yeah.zhouyou.mickey.snl.antlr4;}

program: programHead declarePart programBody? ;

programHead: PROGRAM programName=ID ;
declarePart: typeDecpart? varDecpart? procDecpart? ;

typeDecpart: TYPE typeDecList ;
typeDecList: typeId=ID ':=' typeDef ';' typeDecList? ;

typeDef: basicType
       | structureType
       | ID
       ;
basicType: INTEGER | CHAR ;
structureType: arrayType
             | recordType
             ;
arrayType: ARRAY '[' low=INTC '..' top=INTC ']' OF basicType ;
recordType: RECORD fieldDecList END ;
fieldDecList: typeDef idList ';' fieldDecList? ;
idList: ID (',' idList)? ;

varDecpart: VAR varDecList ;
varDecList: typeDef idList ';' varDecList? ;

procDecpart: procDeclare procDecpart? ;
procDeclare: PROCEDURE procName=ID '(' paramList? ')' ';'
             declarePart programBody ;
paramList: param (';' paramList)? ;
param: VAR? typeDef idList ;

programBody: BEGIN stmList END ;
stmList: stm (';' stmList)? ;
stm: conditionalStm
   | loopStm
   | inputStm
   | outputStm
   | assignment
   | callStm
   | RETURN
   ;
assignment: variable ':=' exp ;
callStm: ID '(' actParamList? ')' ;
conditionalStm: IF condExp THEN stmList (ELSE stmList)? FI ;
loopStm: WHILE condExp DO stmList ENDWH ;
inputStm: READ '(' invar=ID ')' ;
outputStm: WRITE '(' exp ')' ;
actParamList: exp (',' actParamList)? ;
condExp: exp (cmpOp=(EQ | LT) exp)? ;
exp: exp op=(MUL | DIV) exp
   | exp op=(ADD | SUB) exp
   | factor
   ;
factor: '(' exp ')'
      | intVal=INTC
      | variable
      ;
variable: ID variMore? ;
variMore: '[' exp ']'
        | DOT ID variMore?
        ;

//
PROGRAM: 'program' ;
TYPE: 'type';
INTEGER: 'integer' ;
CHAR: 'char' ;
ARRAY: 'array' ;
OF: 'of' ;
RECORD: 'record' ;
BEGIN: 'begin' ;
END: 'end' ;
PROCEDURE: 'procedure' ;
VAR: 'var' ;
IF: 'if' ;
THEN: 'then' ;
ELSE: 'else' ;
WHILE: 'while' ;
DO: 'do';
ENDWH: 'endwh' ;
READ: 'read' ;
WRITE: 'write' ;
RETURN: 'return' ;
FI: 'fi' ;
ID: [a-zA-Z][a-zA-Z0-9]* ;
INTC: [0-9]+ ;
ADD: '+' ;
SUB: '-' ;
MUL: '*' ;
DIV: '/' ;
EQ: '=' ;
LT: '<' ;
DOT: '.' ;
DDOT: '..' ;
AMGT: ':=' ;
SEM: ';' ;
COMMA: ',' ;
LBT: '(' ;
RBT: ')' ;
LSBT: '[' ;
RSBT: ']' ;
WS: [ \t\n\r]+ -> skip;


读懂这个文法就完全清楚了这门语言的语法结构了,大家可以直接用它来开始开发了,对于语义的部分,大家猜都能猜得出个大概。

但是文法对于学习一个语言来说是比较抽象的,虽然大家都能看懂,但过于理论化,所以我写几个例子贴在下面。

例一,以下程序演示了参数传递,调用proc时传入三个参数1,2,3,在proc内部打印这三个参数的值到控制台——如果我们实现的没有问题,则它应该可以正确打印出这三个数字:


program test
    procedure proc(integer a, b; integer c);
    begin
        write(a);
        write(b);
        write(c)
    end
begin
    proc(1, 2, 3)
end


例二,以下程序中创建了一个整数数组,这个数组的下标是从6到8,共三个值(或三个引用),然后分写或读数组的每个元素。

program test
    var array[6..8] of integer a;
begin
    a[6] := 1;
    a[7] := 2;
    a[8] := 3;
   
    write(a[6]);
    write(a[7]);
    write(a[8])
end


例三,以下程序定义了一个过程f,当调用这个过程中,会打印当前的参数,并把参数+1传入下一次的递归调用中。如果没有实现“尾递归优化”,则这个程序的调用可预期的情况是“堆栈溢出”。

program recursion
    procedure f(integer d);
    begin
        write(d);
        f(d + 1)
    end
begin
    f(1)
end


例四,以下程序用来表示引用传参。我们把t做为参数传到ref过程中,过程中对参数n做了一些动作,结束这个过程后打印t——如果我们的实现中,把实参t传给形参n是值传递的,则最终会打印出5(因为过程内部无论做了什么都改变不了实参);如果我们的实现中,把实参t传给形参n是引用传递的,则n和t在语义上就是同一个对象,则作用于n的都会在t上有所体现。

在我的实现中,它会打印出3,因为我把参数传递实现为引用传递,但把 := 实现为值传递。


program test
    var integer t;
    procedure ref(integer n);
        var integer i;
    begin
        i := n;
        n := 3;
        i := 1
    end
begin
    t := 5;
    ref(t);
    write(t)
end


例五,以下程序中的factorial过程用于计算n的阶乘,因为我实现的是引用传参,所以我们可以用参数从过程中返回结果。如果我们不设计成引用传参,则需要使用全局变量来传递数据了。

program test
    var integer res;
    procedure factorial(integer n, r);
    begin
        r := 1;
        factorial_recursion(n, r)
    end
    procedure factorial_recursion(integer n, r);
    begin
        if 1 < n then
            r := r * n;
            factorial_recursion(n - 1, r)
        fi
    end
begin
    factorial(10, res);
    write(res)
end


例六,以下程序用来测试类型定义的功能(其实是一个挺无聊的测试用例)

program test
    type
        int := integer;
        array1to20 := array[1..20] of char;
        recordtype1 := record
            int a, b;
            array1to20 c;
        end;
        recordtype2 := record
            recordtype1 v;
            char c;
        end;
    var recordtype2 v;
        array1to20 v2;
begin
    v.v.a := 1;
    write(v.v.a);
   
    v2[1] := 65;
    write(v2[1]);
   
    v.v.c[1] := 68;
    write(v.v.c[1]);

v.c := 66;
    write(v.c)
end


例七,以下程序实现了一个冒泡排序(这是教材上给出的例子):

program bubble
    var integer i, j, num;
        array [1..20] of integer a;

procedure q(integer num);
        var integer i, j, k;
            integer t;
    begin
        i := 1;
        while i < num do
            j := num - i + 1;
            k := 1;
            while k < j do
                if a[k + 1] < a[k] then
                    t := a[k];
                    a[k] := a[k + 1];
                    a[k + 1] := t
                else
                    t := 0
                fi;
                k := k + 1
            endwh;
            i:= i + 1
        endwh
    end
begin
    read(num);
    i := 1;
    while i < (num + 1) do
        read(j);
        a[i] := j;
        i := i + 1
    endwh;
    q(num);
    i := 1;
    while i < (num + 1) do
        write(a[i]);
        i := i + 1
    endwh
end.


经过以上的介绍,大家应该对SNL这个语言比较理解了,下面我选择这个解释器最重要的部分介绍一下开发思路。

一、堆栈

………………

二、上下文(符号表)

………………

三、引用

………………

四、类型

………………

使用antlr4及java实现snl语言的解释器的更多相关文章

  1. 对SNL语言的解释器实现尾递归优化

    对于SNL语言解释器的内容可以参考我的前一篇文章<使用antlr4及java实现snl语言的解释器>.此文只讲一下"尾递归优化"是如何实现的--"尾递归优化& ...

  2. 分享:写了一个 java 调用 C语言 开发的动态库的范例

    分享:写了一个 java 调用 C语言 开发的动态库的范例 cfunction.h   代码#pragma once#ifdef __cplusplusextern "C" {#e ...

  3. atitit.java解析sql语言解析器解释器的实现

    atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...

  4. 比较java与c语言中数字转换成字符的不同

    java java中将数字转换成字符非常方便,只要用一个"+"然后在跟一个空格行了.比如,你输入一个122 ,就会变成"122 ". import java.u ...

  5. JAVA调用C语言写的SO文件

    JAVA调用C语言写的SO文件 因为工作需要写一份SO文件,作为手机硬件IC读卡和APK交互的桥梁,也就是中间件,看了网上有说到JNI接口技术实现,这里转载了一个实例 // 用JNI实现 // 实例: ...

  6. Java替代C语言的可能性

        前不久CSDN刊登了一篇<C语言已经死了>的文章,引起了一些争论.事实上那篇文章是从Ed Burnette的博客上转载来的,原文题目是“Die, C, die!”,直译过来应该是& ...

  7. TOP100summit 2017 七牛云许式伟:不用JAVA和C语言,我为什么坚持Go语言

    本文编辑:Cynthia 2009年,谷歌发布第二款开源编程语言,Go语言.8年过去了,很多初创公司早期使用Go进行开发,包括云计算巨头Docker.而Go语言在中国的普及程度还比不上JAVA和C语言 ...

  8. atitit.js javascript 调用c# java php后台语言api html5交互的原理与总结p97

    atitit.js javascript 调用c# java php后台语言api html5交互的原理与总结p97 1. 实现html5化界面的要解决的策略1 1.1. Js交互1 1.2. 动态参 ...

  9. 3、Java调用C语言(JNA法)

    这个方法挺方便的……(改写“二.Java调用C语言(JNative法)“的例子) 一.访问https://github.com/twall/jna ,下载jna-4.1.0.jar(版本不同名字不同) ...

随机推荐

  1. 【转】基于Linux下的TCP编程

    http://blog.csdn.net/tigerjibo/article/details/6775534 一.Linux下TCP编程框架 TCP网络编程的流程包含服务器和客户端两种模式.服务器模式 ...

  2. Codeforces 479B. Towers 暴力

    纯暴力..... B. Towers time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  3. ubuntu12.04通过Ganglia利用NVML模块进行GPU监控

    1.安装Ganglia,这里安装的是3.1*版本,因为监控GPU的模块只支持3.1*版本系列的 apt-get install ganglia* 2.下载并安装PyNVML和NVML模块,下载地址ht ...

  4. iOS开发——基础篇——assign,copy,retain之间的区别以及weak和strong的区别

    @property (nonatomic, assign) NSString *title; 什么是assign,copy,retain之间的区别? assign: 简单赋值,不更改索引计数(Refe ...

  5. Vijos 1565 多边形 【区间DP】

    描述 zgx给了你一个n边的多边形,这个多边形每个顶点赋予一个值,每条边都被标上运算符号+或*,对于这个多边形有一个游戏,游戏的步骤如下:(1)第一步,删掉一条边:(2)接下来n-1步,每步对剩下的边 ...

  6. H264--2--语法及结构[5]

    名词解释 场和帧 :    视频的一场或一帧可用来产生一个编码图像.在电视中,为减少大面积闪烁现象,把一帧分成两个隔行的场. 片:             每个图象中,若干宏块被排列成片的形式.片分为 ...

  7. Oracle利用游标返回结果集的的例子(C#)...(最爱)

    引用地址:http://www.alixixi.com/program/a/2008050727634.shtml   本例在VS2005+Oracle 92010 + WindowsXp Sp2测试 ...

  8. flash builder 4.6 下载完成后安装不成功

    从网上下载了一下flash builder 4.6 下载完成后安装不成功,说是有一个安装被挂起,不成安装成功结果从注册表中删除了pendingobject,还是不行,没有办法,从网上搜了一下,发现了大 ...

  9. luogu 1726 上白泽惠音

    题目大意: 给一个有向图 求一个最大的强连通分量,输出这个强连通分量里的所有元素 若两个联通分量内点数相同 则输出字典序小的那个 思路: 直接tarjan 对每个连通分量,求一下最小点,然后判断字典序 ...

  10. bzoj 4883 棋盘上的守卫 —— 基环树转化

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4883 首先,注意到每个点可横可竖,但花费一样: 所以考虑行列的交集,那么这个条件可以转化为行 ...