const TokenType = { NUMBER: "NUMBER", IDENT: "IDENT", LET: "LET", PRINT: "PRINT", EQUAL: "EQUAL", PLUS: "PLUS", MINUS: "MINUS", STAR: "STAR", SLASH: "SLASH", SEMICOLON: "SEMICOLON", LPAREN: "LPAREN", RPAREN: "RPAREN", EOF: "EOF", }; const keywords = { let: TokenType.LET, print: TokenType.PRINT, }; class Token { constructor(type, lexeme, literal, position) { this.type = type; this.lexeme = lexeme; this.literal = literal; this.position = position; } } class Lexer { constructor(source) { this.source = source; this.tokens = []; this.start = 0; this.current = 0; this.line = 1; this.column = 1; } scanTokens() { while (!this.isAtEnd()) { this.start = this.current; this.scanToken(); } this.tokens.push(new Token(TokenType.EOF, "", null, { line: this.line, column: this.column })); return this.tokens; } isAtEnd() { return this.current >= this.source.length; } scanToken() { const c = this.advance(); if (this.isDigit(c)) { this.number(); return; } if (this.isAlpha(c)) { this.identifier(); return; } switch (c) { case "+": this.addToken(TokenType.PLUS); break; case "-": this.addToken(TokenType.MINUS); break; case "*": this.addToken(TokenType.STAR); break; case "/": if (this.match("/")) { while (!this.isAtEnd() && this.peek() !== "\n") { this.advance(); } } else { this.addToken(TokenType.SLASH); } break; case "=": this.addToken(TokenType.EQUAL); break; case ";": this.addToken(TokenType.SEMICOLON); break; case "(": this.addToken(TokenType.LPAREN); break; case ")": this.addToken(TokenType.RPAREN); break; case " ": case "\r": case "\t": break; case "\n": this.line += 1; this.column = 1; break; default: throw new Error("无法识别的字符: '" + c + "' 在第 " + this.line + " 行"); } } advance() { const ch = this.source[this.current]; this.current += 1; this.column += 1; return ch; } addToken(type, literal = null) { const text = this.source.slice(this.start, this.current); this.tokens.push(new Token(type, text, literal, { line: this.line, column: this.column })); } match(expected) { if (this.isAtEnd()) return false; if (this.source[this.current] !== expected) return false; this.current += 1; this.column += 1; return true; } peek() { if (this.isAtEnd()) return "\0"; return this.source[this.current]; } isDigit(c) { return c >= "0" && c <= "9"; } isAlpha(c) { return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_"; } isAlphaNumeric(c) { return this.isAlpha(c) || this.isDigit(c); } number() { while (this.isDigit(this.peek())) { this.advance(); } if (this.peek() === "." && this.isDigit(this.peekNext())) { this.advance(); while (this.isDigit(this.peek())) { this.advance(); } } const text = this.source.slice(this.start, this.current); const value = Number(text); this.addToken(TokenType.NUMBER, value); } peekNext() { if (this.current + 1 >= this.source.length) return "\0"; return this.source[this.current + 1]; } identifier() { while (this.isAlphaNumeric(this.peek())) { this.advance(); } const text = this.source.slice(this.start, this.current); const type = keywords[text] || TokenType.IDENT; this.addToken(type); } } class Parser { constructor(tokens) { this.tokens = tokens; this.current = 0; } parseProgram() { const statements = []; while (!this.isAtEnd()) { statements.push(this.statement()); } return { type: "Program", statements }; } statement() { if (this.match(TokenType.LET)) return this.letStatement(); if (this.match(TokenType.PRINT)) return this.printStatement(); const expr = this.expression(); this.consume(TokenType.SEMICOLON, "期望在表达式后面出现分号"); return { type: "ExprStmt", expression: expr }; } letStatement() { const nameToken = this.consume(TokenType.IDENT, "let 后面需要变量名"); this.consume(TokenType.EQUAL, "变量声明需要等号"); const initializer = this.expression(); this.consume(TokenType.SEMICOLON, "变量声明后需要分号"); return { type: "LetStmt", name: nameToken.lexeme, initializer }; } printStatement() { const value = this.expression(); this.consume(TokenType.SEMICOLON, "print 后需要分号"); return { type: "PrintStmt", expression: value }; } expression() { return this.term(); } term() { let expr = this.factor(); while (this.match(TokenType.PLUS) || this.match(TokenType.MINUS)) { const operator = this.previous(); const right = this.factor(); expr = { type: "Binary", left: expr, operator: operator.type, right }; } return expr; } factor() { let expr = this.unary(); while (this.match(TokenType.STAR) || this.match(TokenType.SLASH)) { const operator = this.previous(); const right = this.unary(); expr = { type: "Binary", left: expr, operator: operator.type, right }; } return expr; } unary() { if (this.match(TokenType.MINUS)) { const operator = this.previous(); const right = this.unary(); return { type: "Unary", operator: operator.type, right }; } return this.primary(); } primary() { if (this.match(TokenType.NUMBER)) { return { type: "Literal", value: this.previous().literal }; } if (this.match(TokenType.IDENT)) { return { type: "Variable", name: this.previous().lexeme }; } if (this.match(TokenType.LPAREN)) { const expr = this.expression(); this.consume(TokenType.RPAREN, "缺少右括号"); return expr; } throw new Error("无法解析的表达式起始符号"); } match(type) { if (this.check(type)) { this.advance(); return true; } return false; } consume(type, message) { if (this.check(type)) return this.advance(); throw new Error(message); } check(type) { if (this.isAtEnd()) return false; return this.peek().type === type; } advance() { if (!this.isAtEnd()) this.current += 1; return this.previous(); } isAtEnd() { return this.peek().type === TokenType.EOF; } peek() { return this.tokens[this.current]; } previous() { return this.tokens[this.current - 1]; } } class Interpreter { constructor() { this.globals = Object.create(null); } execute(program, outputCallback = console.log) { for (const stmt of program.statements) { this.executeStatement(stmt, outputCallback); } } executeStatement(stmt, outputCallback) { switch (stmt.type) { case "LetStmt": this.globals[stmt.name] = this.evaluate(stmt.initializer); break; case "PrintStmt": const value = this.evaluate(stmt.expression); outputCallback(value); break; case "ExprStmt": this.evaluate(stmt.expression); break; default: throw new Error("未知语句类型: " + stmt.type); } } evaluate(expr) { switch (expr.type) { case "Literal": return expr.value; case "Variable": if (Object.prototype.hasOwnProperty.call(this.globals, expr.name)) { return this.globals[expr.name]; } throw new Error("未定义的变量: " + expr.name); case "Unary": const right = this.evaluate(expr.right); if (expr.operator === TokenType.MINUS) { this.ensureNumber(right); return -right; } throw new Error("未知一元运算符"); case "Binary": const left = this.evaluate(expr.left); const r = this.evaluate(expr.right); if (expr.operator === TokenType.PLUS) { this.ensureNumber(left); this.ensureNumber(r); return left + r; } if (expr.operator === TokenType.MINUS) { this.ensureNumber(left); this.ensureNumber(r); return left - r; } if (expr.operator === TokenType.STAR) { this.ensureNumber(left); this.ensureNumber(r); return left * r; } if (expr.operator === TokenType.SLASH) { this.ensureNumber(left); this.ensureNumber(r); return left / r; } throw new Error("未知二元运算符"); default: throw new Error("未知表达式类型: " + expr.type); } } ensureNumber(value) { if (typeof value !== "number" || Number.isNaN(value)) { throw new Error("期望数字类型"); } } } function run(source, outputCallback = console.log) { const lexer = new Lexer(source); const tokens = lexer.scanTokens(); const parser = new Parser(tokens); const program = parser.parseProgram(); const interpreter = new Interpreter(); interpreter.execute(program, outputCallback); } if (typeof module !== "undefined" && module.exports) { module.exports = { run }; } else { // 浏览器环境 window.MiniLang = { run }; }