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>
相关推荐
时光の尘11 分钟前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
以后不吃煲仔饭24 分钟前
Java基础夯实——2.7 线程上下文切换
java·开发语言
进阶的架构师25 分钟前
2024年Java面试题及答案整理(1000+面试题附答案解析)
java·开发语言
前端拾光者29 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
程序猿阿伟31 分钟前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链
Json_181790144801 小时前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
傻啦嘿哟1 小时前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
大数据编程之光1 小时前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
初九之潜龙勿用1 小时前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
风尚云网1 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网