代码
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>