0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

詞法分析-Antlr-1

汽車(chē)電子技術(shù) ? 來(lái)源:程序猿搬磚 ? 作者:壞人 ? 2023-03-03 10:15 ? 次閱讀

在上一節(jié)中我們提到了,我們可以根據(jù)一種叫 有限自動(dòng)機(jī) 的東西將字符串分割成多個(gè)Token。

下面是一段swift代碼,其中實(shí)現(xiàn)了一些基礎(chǔ)Token的解析,原理就是: 有限自動(dòng)機(jī)

//
//  ScriptLexer.swift
//  MyScriptCompiler
//
//  Created by legendry on 2019/8/21.
//  Copyright ? 2019 legendry. All rights reserved.
//
import Foundation
/// 腳本語(yǔ)言詞法分析器
public class ScriptLexer {
    
    public enum ScriptLexer: Error {
        /// 源碼為空
        case SourceCodeEmpty
        /// 語(yǔ)法錯(cuò)誤
        case SyntaxError(reason: String)
    }
    /// 詞法分析器的有限狀態(tài)機(jī)狀態(tài)
    var fsmType = FSMType.Initial
    /// 默認(rèn)token為nil
    var token: ScriptToken? = nil
    var _tmpTokens = [ScriptToken]()
    /// 初始化新的token
    /// 將狀態(tài)機(jī)遷移到新的狀態(tài)
    private func initToken(_ c: UInt8) throws -> FSMType {
        var _fsmType = FSMType.Unknow
        if let _t = token {
            /// 將解析到的token保存起來(lái)
            _tmpTokens.append(_t)
            token = nil
        }
        if c.isLetter {
            /// 當(dāng)我們解析到字符i時(shí),當(dāng)前認(rèn)為他是一個(gè)標(biāo)識(shí)符
            token = ScriptToken(type: .Identifier)
            token?.appendTokenText(c: c)
            if c.char == "i" {
                /// 狀態(tài)機(jī)狀態(tài)切換至標(biāo)識(shí)符i,如果后續(xù)是nt并以空格結(jié)束,則解析成int
                /// 否則解析成標(biāo)識(shí)符
                _fsmType = .Identifier_Int_i
            } else {
                _fsmType = .Identifier
            }
        } else if c.char == "=" {
            token = ScriptToken(type: .EQ)
            token?.appendTokenText(c: c)
            _fsmType = .EQ
        } else if c.isDigit {
            token = ScriptToken(type: .IntLiteral)
            token?.appendTokenText(c: c)
            _fsmType = .IntLiteral
        } else if c.char == ">" {
            token = ScriptToken(type: .GT)
            token?.appendTokenText(c: c)
            _fsmType = .GT
        } else if c.char == "<" {
            token = ScriptToken(type: .LT)
            token?.appendTokenText(c: c)
            _fsmType = .LT
        } else if c.char == "-" {
            token = ScriptToken(type: .Minus)
            token?.appendTokenText(c: c)
            _fsmType = .Minus
        } else if c.char == "*" {
            token = ScriptToken(type: .Star)
            token?.appendTokenText(c: c)
            _fsmType = .Star
        } else if c.char == "+" {
            token = ScriptToken(type: .Plus)
            token?.appendTokenText(c: c)
            _fsmType = .Plus
        } else if c.char == "/" {
            token = ScriptToken(type: .Slash)
            token?.appendTokenText(c: c)
            _fsmType = .Slash
        } else if c.char == "(" {
            token = ScriptToken(type: .LeftBracket)
            token?.appendTokenText(c: c)
            _fsmType = .LeftBracket
        } else if c.char == ")" {
            token = ScriptToken(type: .RightBracket)
            token?.appendTokenText(c: c)
            _fsmType = .RightBracket
        } else {
            if c.isValid {
                _fsmType = .Initial
            } else {
                throw ScriptLexer.SyntaxError(reason: "不支持: \\(c.char)")
            }
        }
        return _fsmType
        
    }
    
    
    /// 解析腳本,生成Token
    /// 利用有限狀態(tài)自動(dòng)機(jī)在不同狀態(tài)之間遷移得到不同的Token
    /// int age = 45
    /// age >= 3
    public func analysis(script: String) throws -> ScriptTokenReader {
        _tmpTokens.removeAll()
        fsmType = FSMType.Initial
        token = nil
        
        guard script.count > 0 else {
            throw ScriptLexer.SourceCodeEmpty
        }
        let charReader = CharReader(script)
        /// 開(kāi)始分析源碼
        while let c = charReader.read() {
            switch fsmType {
            case .Initial:
                self.fsmType = try initToken(c)
            case .Identifier_Int_i:
                /// 第一個(gè)字母是i,如果第二個(gè)字母是n則狀態(tài)機(jī)遷移至Identifier_Int_n
                /// 否則狀態(tài)機(jī)遷移至Identifier
                if c.char == "n" {
                    self.fsmType = .Identifier_Int_n
                    token?.appendTokenText(c: c)
                } else if c.isTail {
                    /// 當(dāng)前token標(biāo)識(shí)完成
                    /// 狀態(tài)機(jī)重置
                    self.fsmType = try initToken(c)
                } else {
                    /// 狀態(tài)機(jī)遷移至標(biāo)識(shí)符狀態(tài),繼續(xù)解析標(biāo)識(shí)符token
                    self.fsmType = .Identifier
                    token?.appendTokenText(c: c)
                }
            case .Identifier_Int_n:
                if c.char == "t" {
                    self.fsmType = .IntLiteral
                    token?.appendTokenText(c: c)
                    /// 這里暫時(shí)將類型切換成Int,如果后面還有字符則表式是一個(gè)標(biāo)識(shí)符,再切換類型
                    token?.type = .Int
                } else if c.isTail {
                    /// 當(dāng)前token標(biāo)識(shí)完成
                    /// 狀態(tài)機(jī)重置
                    self.fsmType = try initToken(c)
                } else {
                    /// 狀態(tài)機(jī)遷移至標(biāo)識(shí)符狀態(tài),繼續(xù)解析標(biāo)識(shí)符token
                    self.fsmType = .Identifier
                    token?.appendTokenText(c: c)
                }
            case .IntLiteral:
                if c.isTail {
                    /// 當(dāng)前token標(biāo)識(shí)完成
                    /// 狀態(tài)機(jī)重置
                    self.fsmType = try initToken(c)
                } else if c.char == "+" || c.char == "-" || c.char == "*" || c.char == "/" /*|| c.char == "(" || c.char == ")"*/ {
                    self.fsmType = try initToken(c)
                } else if c.isLetter {
                    throw ScriptLexer.SyntaxError(reason: "非數(shù)字字面量")
                } else {
                    token?.appendTokenText(c: c)
                }
            case .Identifier:
                if c.isTail {
                    self.fsmType = try initToken(c)
                } else {
                    token?.appendTokenText(c: c)
                }
            case .EQ:
                if c.isTail {
                    token?.type = .Assignment
                    self.fsmType = try initToken(c)
                } else {
                    throw ScriptLexer.SyntaxError(reason: "語(yǔ)法異常")
                }
            case .GE, .LE:
                if c.isTail {
                    self.fsmType = try initToken(c)
                } else {
                    throw ScriptLexer.SyntaxError(reason: "語(yǔ)法異常")
                }
            case .LeftBracket, .RightBracket:
                self.fsmType = try initToken(c)
            case .GT:
                if c.isTail {
                    self.fsmType = try initToken(c)
                } else if c.char == "=" {
                    self.fsmType = .GE
                    token?.type = .GE
                    token?.appendTokenText(c: c)
                } else {
                    throw ScriptLexer.SyntaxError(reason: "語(yǔ)法異常")
                }
            case .LT:
                if c.isTail {
                    self.fsmType = try initToken(c)
                } else if c.char == "=" {
                    self.fsmType = .LE
                    token?.type = .LE
                    token?.appendTokenText(c: c)
                } else {
                    throw ScriptLexer.SyntaxError(reason: "語(yǔ)法異常")
                }
            case .Minus, .Plus, .Star, .Slash:
                if c.isTail || c.isDigit {
                    self.fsmType = try initToken(c)
                } else {
                    throw ScriptLexer.SyntaxError(reason: "語(yǔ)法異常")
                }
            default: break
            }
        }
        if let _t = token {
            /// 將解析到的token保存起來(lái)
            _tmpTokens.append(_t)
            token = nil
        }
        let tokenReader = ScriptTokenReader.init(_tmpTokens)
        return tokenReader
    }
}

如果要快速落地一個(gè)DSL原型Demo,全部都自己去寫(xiě)似乎有點(diǎn)慢。所以我們需要借助于工具來(lái)幫我們解析Token, 這個(gè)工具叫做: Antlr https://www.antlr.org/

這個(gè)工具支持很多語(yǔ)言,C++,Swift,Java....,常用的編碼語(yǔ)言都可以。你可以選擇一個(gè)合適你的語(yǔ)言來(lái)實(shí)現(xiàn)DSL了。

下載并配置好Antlr,Antlr本身是Java實(shí)現(xiàn)的,所以你的環(huán)境要運(yùn)行Antlr需要有Java運(yùn)行時(shí)環(huán)境。

圖片

配置好Antlr之后,我們就可以借助Antlr來(lái)實(shí)現(xiàn)我們的詞法分析了。Antlr通過(guò)解析規(guī)則文件來(lái)分析我們要分割的Token的規(guī)則,規(guī)則則是用正則表達(dá)式來(lái)書(shū)寫(xiě)。

lexer grammar FlexDSLLexer;
//關(guān)鍵字
If: 'if';
FOR: 'for';
WHILE: 'while';
IN: 'in';
/// 基礎(chǔ)數(shù)據(jù)類型
Int: 'int';
Double: 'double';
Float: 'float';
True: 'true';
False: 'false';
//字面量
IntLiteral: [0-9]+;
DoubleLiteral: [0-9] . [0-9]*;
StringLiteral: '"' .*? '"'; //字符串字面量
//操作符
AssignmentOP: '=';
RelationalOP: '>' | '>=' | '<' | '<=';
Star: '*';
Plus: '+';
Sharp: '#';
SemiColon: ';';
Dot: '.';
Comm: ',';
LeftBracket: '[';
RightBracket: ']';
LeftBrace: '{';
RightBrace: '}';
LeftParen: '(';
RightParen: ')';
//標(biāo)識(shí)符
Id: [a-zA-Z_] ([a-zA-Z_] | [0-9])*;
//空白字符,拋棄
Whitespace: [ \\t]+ -> skip;
Newline: ( '\\r' '\\n'? | '\\n') -> skip;

以上的規(guī)則文件內(nèi)容指定了我們要從字符串中解析出來(lái)的Token,每一個(gè)Token都有一個(gè)名字,后面對(duì)應(yīng)的則是這個(gè)Token的規(guī)則。把這個(gè)文件保存到FlexDSLLexer.g4

然后通過(guò)命令來(lái)編譯

antlr4 FlexDSLLexer.g4

編譯完成得到如下文件

圖片然后使用

javac *.java

編譯完成之后,通過(guò)運(yùn)行g(shù)run命令來(lái)解析并輸出對(duì)應(yīng)的Token(s)

grun FlexDSLLexer tokens -tokens Hello.play

其中Hello.play內(nèi)容如下

int name = "人\\n字";
true
false

最后得到如下輸出,不僅成功的解析出來(lái)了我們指定的Token,還把對(duì)應(yīng)的行列都輸出了。這樣當(dāng)我們?cè)诮馕龀鲥e(cuò)時(shí)也可以報(bào)具體的錯(cuò)誤信息了。圖片到此,我們的詞法解析就完成了,接下來(lái)我們將進(jìn)行語(yǔ)法分析。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • SWIFT
    +關(guān)注

    關(guān)注

    0

    文章

    92

    瀏覽量

    23775
  • 原理
    +關(guān)注

    關(guān)注

    4

    文章

    550

    瀏覽量

    44853
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68229
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    #硬聲創(chuàng)作季 4.2.1 詞法分析器的結(jié)構(gòu)

    編譯原理詞法分析
    Mr_haohao
    發(fā)布于 :2022年09月01日 06:50:51

    #硬聲創(chuàng)作季 4.1.1 詞法分析概述

    編譯原理詞法分析
    Mr_haohao
    發(fā)布于 :2022年09月01日 06:51:30

    #硬聲創(chuàng)作季 6.3.1 詞法分析程序自動(dòng)生成——LEX

    編譯原理詞法分析
    Mr_haohao
    發(fā)布于 :2022年09月01日 06:56:34

    關(guān)于antlr詞法分析器的使用

    剛剛接觸antlr詞法分析器只略看了些基本理論知識(shí),關(guān)于做實(shí)例就完全不懂了,我想知道他需要什么樣的環(huán)境和軟件,以及軟件的下載地址.多謝各位了
    發(fā)表于 11-12 16:29

    Hanlp分詞之CRF中文詞法分析詳解

    ,對(duì)應(yīng)CRFNERecognizer。CRF詞法分析器訓(xùn)練了1至3個(gè)模型后,可以構(gòu)造CRF詞法分析器: /*** 構(gòu)造CRF
    發(fā)表于 02-18 15:28

    postgreSQL命令的詞法分析和語(yǔ)法分析

    PostgreSQL查詢SQL的語(yǔ)法分析1)——詞法分析
    發(fā)表于 05-16 16:33

    詞法作用域和閉包

    #hello,JS:14閉包(詞法作用域)
    發(fā)表于 05-20 15:35

    手寫(xiě)SQL編譯器詞法分析

    精讀《手寫(xiě) SQL 編譯器 - 詞法分析
    發(fā)表于 05-26 16:27

    C語(yǔ)言詞法分析器的代碼

    C語(yǔ)言詞法分析器的代碼#include #include #include #include <
    發(fā)表于 10-10 15:32 ?85次下載

    借助Lex和Yacc進(jìn)行詞法語(yǔ)法分析

    實(shí)驗(yàn)?zāi)康模?1.通過(guò)對(duì)實(shí)驗(yàn)型程序設(shè)計(jì)語(yǔ)言C1的定義,掌握程序設(shè)計(jì)語(yǔ)言的基本語(yǔ)法和語(yǔ)義; 2.使用Lex及Yacc實(shí)現(xiàn)詞法分析和語(yǔ)法分析;
    發(fā)表于 04-18 23:04 ?30次下載

    基于ANTLR的試卷識(shí)別和導(dǎo)入系統(tǒng)

    為了解決在線考試系統(tǒng)中手工錄入試題效率低下的問(wèn)題,提出了一種基于ANTLR的自動(dòng)化解決方案。該方案建立一個(gè)試卷識(shí)別器,把試卷內(nèi)容作為源代碼,通過(guò)詞法、語(yǔ)法和語(yǔ)義分析來(lái)進(jìn)
    發(fā)表于 04-27 10:54 ?0次下載
    基于<b class='flag-5'>ANTLR</b>的試卷識(shí)別和導(dǎo)入系統(tǒng)

    語(yǔ)言與編譯器設(shè)計(jì)課程之詞法分析程序源程序

    本文檔的主要內(nèi)容詳細(xì)介紹的是語(yǔ)言與編譯器設(shè)計(jì)課程之詞法分析程序源程序。
    發(fā)表于 07-01 08:00 ?0次下載
    語(yǔ)言與編譯器設(shè)計(jì)課程之<b class='flag-5'>詞法</b><b class='flag-5'>分析</b>程序源程序

    語(yǔ)法分析-Antlr

    上一節(jié),我們通過(guò)Antlr快速的落地實(shí)現(xiàn)了Token的解析,這一節(jié)我們還是基于Antlr來(lái)實(shí)現(xiàn)語(yǔ)法的解析。
    的頭像 發(fā)表于 03-03 10:14 ?621次閱讀
    語(yǔ)法<b class='flag-5'>分析</b>-<b class='flag-5'>Antlr</b>

    手寫(xiě)詞法分析

    在開(kāi)始手寫(xiě)詞法分析器之前呢,我們得先準(zhǔn)備好一些零件,規(guī)劃好將要使用哪些函數(shù),如果函數(shù)沒(méi)有現(xiàn)成的,那還得自己寫(xiě)。
    的頭像 發(fā)表于 05-23 11:20 ?692次閱讀
    手寫(xiě)<b class='flag-5'>詞法</b><b class='flag-5'>分析</b>器

    自頂向下的語(yǔ)法分析器—采用遞歸下降方法

    在之前已經(jīng)通過(guò)手寫(xiě)的方式實(shí)現(xiàn)了一個(gè)詞法分析器,現(xiàn)在,我將利用之前手寫(xiě)的詞法分析器,使用遞歸下降的方式,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)法分析器。
    的頭像 發(fā)表于 05-23 11:24 ?1949次閱讀
    自頂向下的語(yǔ)法<b class='flag-5'>分析</b>器—采用遞歸下降方法