我的第一个编译器之词法分析器

用Java写java的编译器和jvm

为什么用java,java的结构最便于理解,其丰富的设计模式能使编译器的结构十分鲜明

一个编译器的前端模型

源代码–词法分析器-(词法单元)-语法分析器-(语法分析树)-中间代码生成器–三地址代码

再加上一个符号表连接所有的结构

文法定义

一个上下文无关方法由四个元素组成

1.一个终结符号集合,也就是“词法单元”终结符号是该文法定义的语言的基本符号的集合

2.一个非终结符号集合,“语法变量”每个非终结符号表示一个终结符号串的集合

3.一个产生式集合,其中每个产生式包括 一个称为产生式左头或左部的非终结符号一个箭头,和一个称为产生式体或右部的由终结符号及非终结符号组成的序列。

4.指定一个非终结符号的开始符号

词法单元由两部分组成:名字和属性值。

语法分析树

1.根结点的标号为文法的开始符号

2.每个叶子结点的标号为一个终结符号或ε

3.每个内部结点的标号为一个非终结符号

4.如果非终结符号A是某个内部结点的符号,并且它的子结点的标号从左至右分别为X1,x2,x3..,那么必然存在产生式A→X1,X2..Xn,其中X1,X2,..Xn既可以是终结符号,也可以是非终结符号,作为一种特殊情况 ,如果A→ε是一个产生式,那么一个标号为A的结点可以只有 一个标号为ε的子结点。

那么基本概念就说完了,直接上代码,因为代码比较直观

词法分析

先说一个Code类,为什么有它,我是为了更好的进行测试。同时以后也可以直接把代码赋值给它,只要可以做到一个一个的读入字符就可以,同学们可以另想办法。

public class Code {
    public static String content="int a=9;";
    private static int index=0;
    public static char read(){
        return content.charAt(index++);
    }

}

剔除空白和注释

for (;; peek = (char) Code.read()) {
            if (peek == ‘ ‘ || peek == ‘\t‘)
                continue;
            else if (peek == ‘\n‘)
                line++;
            else
                break;
        }

预读

例如,我们需要在1后面预读一个字符来区分1和10,t预读一个来区分 t和true,当然像*这种是不需要预读的

常量

if (Character.isDigit(peek)) {
            int v = 0;
            do {
                v = 10 * v + Character.digit(peek, 10);
                peek = (char) Code.read();
            } while (Character.isDigit(peek));
            return new Num(v);
        }

我们在输入 1+2+3时就会转成序列

<num,1><+><num,2><+><num,3>

+就是终结符号,没有属性,所以它的元组就是<+>

识别关键字和标识符

for do if

如果 输入

a=a+b

终结符号 就是id=id+id

  <id,"a"><+><id,"a"><+><id,"b">

关键字是不能用于标识符的,所以我们要识别关键字和标识符

这就用到了一个符号表,我们用一个hashtable来存储关键字

private Hashtable words = new Hashtable();

    @SuppressWarnings({ "unchecked" })
    private void reserve(Word t) {
        words.put(t.lexeme, t);
    }

    public Lexer() {
        reserve(new Word(Tag.TRUE, "true"));
        reserve(new Word(Tag.FALSE, "false"));
    }
    if (Character.isLetter(peek)) {
            StringBuffer b = new StringBuffer();
            do {
                b.append(peek);
                peek = (char) Code.read();
            } while (Character.isLetterOrDigit(peek));
            String s = b.toString();
            Word w = (Word) words.get(s);
            if (w != null)
                return w;
            w = new Word(Tag.ID, s);
            words.put(s, w);
            return w;
        }

词法分析器

package com.bigbear.lexer;

public class Tag {
    public final static int NUM=256,ID=257,TRUE=258,FALSE=259;

}
package com.bigbear.lexer;

public class Token {
    public final int tag;
    public Token(int tag) {
        this.tag = tag;
    }
}
package com.bigbear.lexer;

public class Num extends Token {
    public final int value;
    public Num(int v) {
        // TODO Auto-generated constructor stub
        super(Tag.NUM);
        this.value=v;
    }

}
package com.bigbear.lexer;

public class Word extends Token {
    public final String lexeme;
    public Word(int t,String s) {
        super(t);
        this.lexeme=new String(s);
    }
}
package com.bigbear.lexer;

import java.io.IOException;
import java.util.Hashtable;

import com.bigbear.main.Code;

/**
 * @author winney 词法分析器
 *
 */
public class Lexer {
    public int line = 1;
    private char peek = ‘ ‘;
    @SuppressWarnings("rawtypes")
    private Hashtable words = new Hashtable();

    @SuppressWarnings({ "unchecked" })
    private void reserve(Word t) {
        words.put(t.lexeme, t);
    }

    public Lexer() {
        reserve(new Word(Tag.TRUE, "true"));
        reserve(new Word(Tag.FALSE, "false"));
    }

    @SuppressWarnings("unchecked")
    public Token scan() throws IOException {
        for (;; peek = (char) Code.read()) {
            if (peek == ‘ ‘ || peek == ‘\t‘)
                continue;
            else if (peek == ‘\n‘)
                line++;
            else
                break;
        }
        if (Character.isDigit(peek)) {
            int v = 0;
            do {
                v = 10 * v + Character.digit(peek, 10);
                peek = (char) Code.read();
            } while (Character.isDigit(peek));
            return new Num(v);
        }
        if (Character.isLetter(peek)) {
            StringBuffer b = new StringBuffer();
            do {
                b.append(peek);
                peek = (char) Code.read();
            } while (Character.isLetterOrDigit(peek));
            String s = b.toString();
            Word w = (Word) words.get(s);
            if (w != null)
                return w;
            w = new Word(Tag.ID, s);
            words.put(s, w);
            return w;
        }
        Token t = new Token(peek);
        peek = ‘ ‘;
        return t;
    }

    public static void main(String[] args) {
        try {
            Lexer lx = new Lexer();
            for (int i = 0; i <5; i++) {
                System.out.println(lx.scan().tag);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
时间: 2024-10-16 15:21:09

我的第一个编译器之词法分析器的相关文章

我的第一个编译器之符号表

符号表 符号表是一种供编译器用于保存有关源程序构造的各种信息的数据结构,这些信息在编译器的分析阶段被逐步收集并放入符号表. 如我们输入 {int x;char y;{bool y; x; y; }x; y;} 期望生成: {{x:int; y:bool;} x:int; y:char;} 内层块的x来源于外部. 为每个作用域设置一个符号表 package com.bigbear.main; public class Code { public static String content = "{

操作系统思考 第一章 编译

第一章 编译 作者:Allen B. Downey 原文:Chapter 1 Compilation 译者:飞龙 协议:CC BY-NC-SA 4.0 1.1 编译语言和解释语言 人们通常把编程语言描述为编译语言或者解释语言.前者的意思是程序被翻译成机器语言,之后由硬件执行:而后者的意思是程序被软件解释器读取并执行.例如,C被认为是编译语言,而Python被认为是解释语言.但是二者之间的界限并不总是那么明显. 首先,许多语言既可以编译执行也可以解释执行.例如,存在C的解释器,和Python的编译

编译技术图示(第一章 编译概述)

编译技术图示(第一章 编译概述) 源程序——>机器代码 分析(前端):分成小部分,找出小部分属性,包括:词法分析.语法分析.语义分析.中间代码生成 合成(后端),包括:中间代码优化.目标代码生成.目标代码优化 1.词法分析:识别.删除单词符号.词法检查 输入:源代码,输出:二元式<单词类别,单词属性> 2.语法分析 输出:抽象语法树(AST),从下向上看 3.语义分析 有限,大部分都是类型(运算)检查 4.中间代码生成 eg:x=a+b —> t1=a+b x=t1 为什么分为两个

自己动手实现简单编译器之(二)词法分析器和有穷自动机

一:实验指导书 对下述单词表定义的语言设计编制一个词法分析器.单词符号及种别表和词法分析器功能及基本要求如下: (1)单词符号及种别表 单词符号 种别编码 单词值 main 1   int 2   float 3   double 4   char 5   if 6   else 7   do 8   while 9   l(l|d)* 10 内部字符串 ( +|-|ε ) dd*(.dd* | ε)( e ( +|-|ε ) dd*|ε) 20 二进制数值表示 = 21   + 22   -

编译器之词法分析

最近我们在做一个有关SNL语言的编译器,下面写了一下大概流程 词法分析器是编译过程的第一阶段,功能是 1.对以字符串形式输入的源程序(这里是把源程序从文件读出,也可以在控制台输入)按顺序进行扫描,根据SNL语言的词法规则识别具有独立意义的单词(符号)序列,  如保留字(由语言系统自身定义的,通常是由字母组成的字符串),     标识符(一般是由字母开头,字母.数字,或其它符号的任意组合构成的,这里SNL是由字母开头,后面加字母或数字组成的,分析起来较简单),     常量(包括整数常数. 实数常

编译器之神vim大改造

相信码农都听过两大神器,一是emacs,一是vim.大家都知道e党和v党老是吵架.其实,这并没有什么好吵的,只是每个人都有自己适用的东西.作者接触了vim,也接触过emacs.对于作者,觉得vim更加适用. 下面,给大家介绍一下作者自己的vim配置.github地址:https://github.com/tenghui0425/VimScripts 首先给大家看一下大图.这个是vimrc配置的图目录情况. 配置主要是以下几块. 基本配置,放在.vimrc下,比如一下全局的map.最后加载一个基本

大三开学第一天--编译原理和人工智能的初步入门

开学的第一天,学的都是入门,所以知识比较少,但是我还是会整理出来.因为是开学第一天,事情比较忙,知识没有得到很好的消化和了解,所以今天只作初步了解,过几天会找时间重新整理,具体化. 编译原理第一课: 编译器:编译器是一种语言处理器,可以将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”.通俗的讲,就是我们目前所使用的高级语言,如C++,java,都是易于人们理解和编写的.但是对于机器来说,只能看懂机器语言(即汇编语言,属于低级语言).所以,人们用高级语言写出来的代码,首先要经

C++编译器之Eclipse安装配置

小伙伴们,你们造吗?除了CodeBlocks和VS.Clion等等开发工具外,我们开发Java常用的Eclipse也可以来开发C++!!下面老九君就给大家简单的讲解下如何使用Eclipse的C++集成版来搭建标准的C/C++开发环境,以及打印出“Hello world!”. 首先,我们需要3种“原材料”: 1.Eclipsefor C++,即C++集成版的Eclispe安装包: 2.JRE安装包: 3.MinGW安装包: 是不是看着挺烦人的,居然要下载安装这么多的东西!没关系,老九君已经在群文件

用C语言写一个Helloworld_实现第一步编译运行

编写第一个hello world 创建helloworld.c // 程序头文件 #include <stdio.h> // 主入口函数 int main(int arc, char* argv[]) { printf("Hello World!\n"); return 0; } 编译自己的第一个程序 Mac os clang -g -o hellword helloword.c -g: 是输出调试信息 -o: 是输出可执行程序 hellword: 最终生成的文件名称 he