js实现简单计算器词法解析语法解析解释器,带可视化界面

代码

Lexer是词法解析器

Parser是语法解析器

Interpreter 是ast解释器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lexer, Parser, and Interpreter Visualization</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        #input-container {
            margin-bottom: 20px;
        }
        #lexer-output, #parser-output, #interpreter-output {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 20px;
        }
        #lexer-output h2, #parser-output h2, #interpreter-output h2 {
            margin-top: 0;
        }
        #lexer-tokens, #parser-ast {
            list-style-type: none;
            padding: 0;
        }
        #lexer-tokens li {
            margin-bottom: 5px;
        }
        #parser-ast ul {
            padding-left: 20px;
        }
    </style>
</head>
<body>
    <h1>Lexer, Parser, and Interpreter Visualization</h1>
    <div id="input-container">
        <label for="input">Input Expression:</label>
        <input type="text" id="input" placeholder="Enter expression...">
        <button onclick="processInput()">Process</button>
    </div>
    <div id="lexer-output">
        <h2>Lexer Output:</h2>
        <ul id="lexer-tokens"></ul>
    </div>
    <div id="parser-output">
        <h2>Parser Output (Abstract Syntax Tree):</h2>
        <pre id="parser-ast"></pre>
    </div>
    <div id="interpreter-output">
        <h2>Interpreter Output (Result):</h2>
        <div id="interpreter-result"></div>
    </div>

    <script>
        function processInput() {
            const input = document.getElementById('input').value;
            
            // Lexer
            const lexer = new Lexer(input);
            const lexerTokens = [];
            let token;
            while ((token = lexer.nextToken()) !== null) {
                lexerTokens.push(token);
            }
            displayLexerOutput(lexerTokens);

            // Parser
            const parser = new Parser(new Lexer(input));
            const ast = parser.parse();
            displayParserOutput(ast);

            // Interpreter
            const interpreter = new Interpreter();
            const result = interpreter.interpret(ast);
            displayInterpreterOutput(result);
        }

        function displayLexerOutput(tokens) {
            const lexerTokensList = document.getElementById('lexer-tokens');
            lexerTokensList.innerHTML = '';
            tokens.forEach(token => {
                const li = document.createElement('li');
                li.textContent = `${token.type}: ${token.value}`;
                lexerTokensList.appendChild(li);
            });
        }

        function displayParserOutput(ast) {
            const parserAST = document.getElementById('parser-ast');
            parserAST.textContent = JSON.stringify(ast, null, 2);
        }

        function displayInterpreterOutput(result) {
            const interpreterResult = document.getElementById('interpreter-result');
            interpreterResult.textContent = `Result: ${result}`;
        }

        class Lexer {
            constructor(input) {
                this.input = input;
                this.position = 0;
            }

            nextToken() {
                while (this.position < this.input.length && /\s/.test(this.input[this.position])) {
                    this.position++;
                }

                if (this.position >= this.input.length) {
                    return null;
                }

                let currentChar = this.input[this.position];

                if (/\d/.test(currentChar)) {
                    let number = '';
                    while (/\d/.test(currentChar) && this.position < this.input.length) {
                        number += currentChar;
                        this.position++;
                        currentChar = this.input[this.position];
                    }
                    return { type: 'NUMBER', value: number };
                }

                if (currentChar === '+') {
                    this.position++;
                    return { type: 'PLUS', value: '+' };
                }

                if (currentChar === '-') {
                    this.position++;
                    return { type: 'MINUS', value: '-' };
                }

                if (currentChar === '*') {
                    this.position++;
                    return { type: 'STAR', value: '*' };
                }

                if (currentChar === '/') {
                    this.position++;
                    return { type: 'SLASH', value: '/' };
                }

                if (currentChar === '(') {
                    this.position++;
                    return { type: 'LPAREN', value: '(' };
                }

                if (currentChar === ')') {
                    this.position++;
                    return { type: 'RPAREN', value: ')' };
                }

                throw new Error(`Unknown character: ${currentChar}`);
            }
        }

        class Parser {
            constructor(lexer) {
                this.lexer = lexer;
                this.currentToken = this.lexer.nextToken();
            }

            eat(tokenType) {
                if (this.currentToken && this.currentToken.type === tokenType) {
                    this.currentToken = this.lexer.nextToken();
                } else {
                    throw new Error(`Expected token type ${tokenType} but got ${this.currentToken ? this.currentToken.type : 'null'}`);
                }
            }

            factor() {
                let token = this.currentToken;
                if (token.type === 'NUMBER') {
                    this.eat('NUMBER');
                    return { type: 'Literal', value: parseInt(token.value, 10) };
                } else if (token.type === 'LPAREN') {
                    this.eat('LPAREN');
                    let node = this.expr();
                    this.eat('RPAREN');
                    return node;
                } else {
                    throw new Error(`Unexpected token: ${token.type}`);
                }
            }

            term() {
                let node = this.factor();

                while (this.currentToken && (this.currentToken.type === 'STAR' || this.currentToken.type === 'SLASH')) {
                    let token = this.currentToken;
                    if (token.type === 'STAR') {
                        this.eat('STAR');
                    } else if (token.type === 'SLASH') {
                        this.eat('SLASH');
                    }
                    node = { type: 'BinaryExpression', operator: token.value, left: node, right: this.factor() };
                }

                return node;
            }

            expr() {
                let node = this.term();

                while (this.currentToken && (this.currentToken.type === 'PLUS' || this.currentToken.type === 'MINUS')) {
                    let token = this.currentToken;
                    if (token.type === 'PLUS') {
                        this.eat('PLUS');
                    } else if (token.type === 'MINUS') {
                        this.eat('MINUS');
                    }
                    node = { type: 'BinaryExpression', operator: token.value, left: node,
                    right: this.term() };
                }

                return node;
            }

            parse() {
                return this.expr();
            }
        }

        class Interpreter {
            interpret(node) {
                if (node.type === 'Literal') {
                    return node.value;
                } else if (node.type === 'BinaryExpression') {
                    const leftValue = this.interpret(node.left);
                    const rightValue = this.interpret(node.right);
                    switch (node.operator) {
                        case '+':
                            return leftValue + rightValue;
                        case '-':
                            return leftValue - rightValue;
                        case '*':
                            return leftValue * rightValue;
                        case '/':
                            if (rightValue === 0) {
                                throw new Error('Division by zero');
                            }
                            return leftValue / rightValue;
                        default:
                            throw new Error(`Unknown operator: ${node.operator}`);
                    }
                } else {
                    throw new Error(`Unknown node type: ${node.type}`);
                }
            }
        }
    </script>
</body>
</html>
相关推荐
轻口味17 分钟前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami19 分钟前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
古希腊掌管学习的神31 分钟前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
赵钰老师31 分钟前
【R语言遥感技术】“R+遥感”的水环境综合评价方法
开发语言·数据分析·r语言
就爱学编程40 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
wakangda1 小时前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
emoji1111111 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼1 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs