zoukankan      html  css  js  c++  java
  • 编译原理 #01# 简易词法分析器(JavaScript实现)

    // 实验存档

    输入示例

    main()
    {
    int a, b;
    a = 10;
    b = a + 20; 
    }

    效果图

     全部代码

    编辑一份.html文件,将代码拷入,作为网页打开即可使用。

    <!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title>Lexical_Analysis</title>
            <link href="https://fonts.googleapis.com/css?family=Noto+Serif+SC" rel="stylesheet">
            <style>
                main {
                    /*对子元素开启弹性布局*/
                    display: flex;
                    /*弹性元素在必要的时候换行*/
                    flex-wrap: wrap;
                    /*将弹性元素居中*/
                    justify-content: center;
                }
                
                textarea,
                button {
                    font-family: 'Noto Serif SC', STFangSong, serif;
                    font-size: 17px;
                }
            </style>
        </head>
    
        <body>
            <main>
                <textarea name="input" rows="20" cols="40"></textarea>
                <textarea name="output" rows="20" cols="40"></textarea>
                <button name="compile">Lexical Analysis</button>
            </main>s
    
            <script>
                let inputBox = document.querySelector("textarea[name=input]");
                let outputBox = document.querySelector("textarea[name=output]");
                let btnCompile = document.querySelector("button[name=compile]");
                btnCompile.addEventListener("click", event => {
                    let inputCode = inputBox.value;
                    outputBox.value = JSON.stringify(Lexical_Analysis(inputCode));
                });
                /*
                 * 规则:
                识别保留字:if、int、for、while、do、return、break、continue;
                单词种别码为1。
                其他的都识别为标识符;单词种别码为2。
                常数为无符号整形数;单词种别码为3。
                运算符包括:+、-、*、/、=、>、<、>=、<=、!= ;单词种别码为4。
                分隔符包括:,、;、{、}、(、); 单词种别码为5。
                 */
                const reservedWords = ['if', 'int', 'for', 'while', 'do', 'return', 'break', 'continue'];
                const operators = ['+', '-', '*', '/', '=', '<', '>', '!', '>=', '<=', '!='];
                const separators = [',', ';', '{', '}', '(', ')'];
    
                function Lexical_Analysis(str) {
                    /**
                     * current用于标识当前字符位置,
                     * str[cur]即为当前字符
                     */
                    let cur = 0;
                    /**
                     * tokens存储词法分析的最终结果
                     */
                    let tokens = [];
    
                    while(cur < str.length) {
    
                        if(/s/.test(str[cur])) { // 跳过空格
                            cur++;
                        } else if(/[a-z]/i.test(str[cur])) { // 读单词
                            debugger;
                            let word = "" + str[cur++];
                            // 测试下一位字符,如果不是字母直接进入下一次循环(此时cur已经右移)
                            // 如果是则继续读字母,并将cur向右移动
                            while(cur < str.length && /[a-z]/i.test(str[cur])) {
                                // cur < str.length防止越界
                                word += str[cur++];
                            }
                            if(reservedWords.includes(word)) {
                                tokens.push({
                                    type: 1,
                                    value: word,
                                }); // 存储保留字(关键字)
                            } else {
                                tokens.push({
                                    type: 2,
                                    value: word,
                                }); // 存储普通单词                            
                            }
                        } else if(separators.includes(str[cur])) {
                            tokens.push({
                                type: 5,
                                value: str[cur++],
                            }); // 存储分隔符并将cur向右移动                                            
                        } else if(operators.includes(str[cur])) {
                            let operator = "" + str[cur++];
                            if(['>', '<', '!'].includes(operator)) {
                                // 如果下一个字符是=就添加到operator并再次向右移动cur
                                if(str[cur] = '=') {
                                    operator += str[cur++];
                                }
                            }
                            tokens.push({
                                type: 4,
                                value: operator,
                            }); // 存储运算符                        
                        } else if(/[0-9]/.test(str[cur])) {
                            let val = "" + str[cur++];
                            // cur < str.length防止越界
                            while(cur < str.length && /[0-9]/.test(str[cur])) {
                                val += str[cur++];
                            }
                            tokens.push({
                                type: 3,
                                value: val,
                            }); // 存储整数数字    
                        } else {
                            return "包含非法字符:" + str[cur];
                        }
    
                    }
                    return tokens;
                }
            </script>
        </body>
    </html>

    附件,龙书2.6节练习:

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8">
        <title></title>
        <link href="https://fonts.googleapis.com/css?family=Noto+Serif+SC" rel="stylesheet">
        <style>
            main {
                /*对子元素开启弹性布局*/
                display: flex;
                /*弹性元素在必要的时候换行*/
                flex-wrap: wrap;
                /*将弹性元素居中*/
                justify-content: center;
            }
    
            textarea,
            button {
                font-family: 'Noto Serif SC', STFangSong, serif;
                font-size: 17px;
            }
        </style>
    </head>
    
    <body>
        <main>
            <textarea name="input" rows="20" cols="40"></textarea>
            <textarea name="output" rows="20" cols="40"></textarea>
            <button name="execute">Execute</button>
        </main>
    
        <script>
            let inputBox = document.querySelector("textarea[name=input]");
            let outputBox = document.querySelector("textarea[name=output]");
            let btnExecute = document.querySelector("button[name=execute]");
    
            btnExecute.addEventListener("click", event => {
                let tokens = tokenizer(inputBox.value);
                console.log(tokens);
            });
    
            function tokenizer(input) {
                let s = input;
                let cur = 0;
                let peek = ' ';
                let line = 1;
                let words = new Map();
    
                let readChar = () => s[cur++];
                let undo = () => cur--;
                let scan = () => { // 每次scan返回一个Token
                    // 略过空格,上次设置的peek值并不会被清空
                    for (;; peek = readChar()) {
                        if (peek == undefined) {
                            return null; // 读完了
                        } else if (peek == ' ' || peek == '	') {
                            continue; // 略过空格和Tab
                        } else if (peek == '
    ') {
                            line++; // 记录当前行
                        } else {
                            break;
                        }
                    }
    
                    // 略过注释
                    if ('/' == peek) {
                        peek = readChar();
                        if ('/' == peek) {
                            // 注释类型1
                            peek = readChar();
                            for (;; peek = readChar()) {
                                if (peek == '
    ') {
                                    break; // 正常退出
                                } else if (peek == undefined) {
                                    return null; // 读完了,正常退出
                                }
                            }
                        } else if ('*' == peek) {
                            peek = readChar();
                            // 注释类型2
                            let lastAsterisk = false;
                            for (;; peek = readChar()) {
                                if (peek == undefined) {
                                    console.log("注释语法错误01");
                                    return null; // 语法错误
                                } else if (peek == '
    ') {
                                    lastAsterisk = false;
                                    line++; // 记录当前行
                                } else if (peek == '*') {
                                    lastAsterisk = true;
                                } else if (lastAsterisk && peek == '/') {
                                    peek = readChar();
                                    break; // 正常退出
                                } else {
                                    lastAsterisk = false;
                                }
                            }
                        } else {
                            // 语法错误
                            console.log("注释语法错误02");
                            return null;
                        }
                    }
    
                    // 略过空格,上次设置的peek值并不会被清空
                    for (;; peek = readChar()) {
                        if (peek == undefined) {
                            return null; // 读完了
                        } else if (peek == ' ' || peek == '	') {
                            continue; // 略过空格和Tab
                        } else if (peek == '
    ') {
                            line++; // 记录当前行
                        } else {
                            break;
                        }
                    }
    
                    if (/[0-9.]/.test(peek)) {
                        let temp = peek;
                        let hasPoint = false;
                        if (peek == '.') hasPoint = true;
                        while (/[0-9.]/.test(peek = readChar())) {
                            if (peek == '.' && hasPoint) {
                                console.log("语法错误3,包含多个小数点");
                                return null;
                            } else if (peek == '.') {
                                hasPoint = true;
                                temp += peek;
                            } else {
                                temp += peek;
                            }
                        }
                        return {
                            tag: 'NUM',
                            value: Number(temp),
                        };
                    }
    
                    if (/[a-zA-z]/.test(peek)) {
                        let temp = peek;
                        while ((peek = readChar()) && /[a-zA-z]/.test(peek)) {
                            // 经测试,null和undefined都能通过/w/以及/[a-zA-z]/,并可以转化为字面字符串
                            temp += peek;
                        }
                        let w = words.get(temp);
                        if (w != undefined) {
                            return w;
                        } else {
                            w = {
                                tag: 'ID',
                                lexeme: temp,
                            };
                            words.set(temp, w);
                            return w;
                        }
                    }
    
                    if (/[><=!]/.test(peek)) {
                        let first = peek;
                        peek = readChar();
                        if (peek == '=') {
                            peek = readChar(); // 避免重复处理
                            return {
                                tag: '逻辑运算符',
                                value: first + '=',
                            };
                        } else if (first != '=') {
                            return {
                                tag: '逻辑运算符',
                                value: first,
                            };
                        } else { // 单个=的情况,回溯
                            undo();
                            peek = first;
                        }
                    }
    
                    let res = {
                        tag: peek,
                    };
                    peek = ' ';
                    return res;
                };
    
                let tokens = [];
                let token;
                while (token = scan()) {
                    tokens.push(token);
                }
                return tokens;
            }
        </script>
    </body>
    
    </html>
    View Code
  • 相关阅读:
    婚姻中媒人存在的客观逻辑——leo鉴书45
    为什么要使用RTP
    OCP-1Z0-053-200题-148题-485
    OCP-1Z0-053-200题-149题-78
    OCP-1Z0-053-200题-150题-236
    OCP-1Z0-053-200题-151题-53
    OCP-1Z0-053-200题-152题-56
    OCP-1Z0-053-200题-153题-211
    OCP-1Z0-053-200题-154题-208
    OCP-1Z0-053-200题-155题-218
  • 原文地址:https://www.cnblogs.com/xkxf/p/10538719.html
Copyright © 2011-2022 走看看