UNPKG

python2igcse

Version:

Convert Python code to IGCSE Pseudocode format

536 lines 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TextEmitter = void 0; const base_emitter_1 = require("./base-emitter"); /** * プレーンテキスト形式でIGCSE Pseudocodeを出力するエミッター */ class TextEmitter extends base_emitter_1.BaseEmitter { constructor(options = {}) { super({ ...options, format: 'plain' }); this.nodesProcessed = 0; } /** * IRをプレーンテキストに変換 */ emit(ir) { this.startEmitting(); this.resetContext(); this.nodesProcessed = 0; this.debug('Starting text emission...'); try { this.emitNode(ir); const result = this.createEmitResult(); result.stats.nodesProcessed = this.nodesProcessed; this.debug(`Text emission completed. Lines: ${result.stats.linesGenerated}`); return result; } catch (error) { this.addError(`Emit failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'output_error'); return this.createEmitResult(); } } /** * IRノードの処理 */ emitNode(node) { this.nodesProcessed++; this.context.currentNode = node; this.debug(`Emitting node: ${node.kind} - "${node.text}"`); switch (node.kind) { case 'statement': this.emitStatement(node); break; case 'assign': this.emitAssign(node); break; case 'element_assign': this.emitAssign(node); break; case 'attribute_assign': this.emitAssign(node); break; case 'output': this.emitOutput(node); break; case 'input': this.emitInput(node); break; case 'comment': this.emitComment(node.text); break; case 'compound': this.emitCompound(node); break; case 'if': this.emitIf(node); break; case 'else': this.emitElse(node); break; case 'elseif': this.emitElseIf(node); break; case 'endif': this.emitEndif(node); break; case 'for': this.emitFor(node); break; case 'while': this.emitWhile(node); break; case 'endwhile': this.emitEndwhile(node); break; case 'repeat': this.emitRepeat(node); break; case 'until': this.emitUntil(node); break; case 'procedure': this.emitProcedure(node); break; case 'function': this.emitFunction(node); break; case 'return': this.emitReturn(node); break; case 'array': this.emitArray(node); break; case 'type': this.emitType(node); break; case 'class': this.emitClass(node); break; case 'case': this.emitCase(node); break; case 'expression': this.emitExpression(node); break; case 'block': this.emitBlock(node); break; default: // For unknown node types, output text as is if (node.text) { this.emitLine(this.formatText(node.text)); } this.emitChildren(node); break; } } /** * 代入文の出力 */ emitAssign(node) { const text = this.formatText(node.text); this.emitLine(text); this.emitChildren(node); } /** * ブロックの出力 */ emitBlock(node) { // ブロックは子ノードをそのまま出力 this.emitChildren(node); } /** * 出力文の出力 */ emitOutput(node) { // IRArgumentの情報を使用して適切にフォーマット if (node.meta?.arguments) { const formattedArgs = node.meta.arguments .map((arg) => { if (arg.type === 'literal') { // Output string literals as is (no formatting) return arg.value; } else { // 変数や式は通常通り整形 return this.formatText(arg.value); } }) .join(', '); // Safely add spaces on emitter side const outputText = `OUTPUT ${formattedArgs}`; this.emitLine(outputText); } else { // 従来の処理(後方互換性) const text = this.formatText(node.text); this.emitLine(text); } this.emitChildren(node); } /** * 入力文の出力 */ emitInput(node) { const text = this.formatText(node.text); this.emitLine(text); this.emitChildren(node); } /** * IF文の出力 * 構造化されたconsequent/alternateフィールドを使用 */ emitIf(node) { const text = this.formatText(node.text); this.emitLine(text); // THEN側(consequent)の出力 if (node.meta?.consequent) { this.increaseIndent(); for (const stmt of node.meta.consequent) { this.emitNode(stmt); } this.decreaseIndent(); // ELSE側(alternate)の出力 if (node.meta?.alternate && node.meta.alternate.length > 0) { for (const altStmt of node.meta.alternate) { // Don't adjust indent for ELSE IF statements if (altStmt.text.startsWith('ELSE IF')) { this.emitNode(altStmt); } else { // 通常のELSE文の場合はインデントを調整 this.emitNode(altStmt); } } } } else { // 従来の子ノード処理(後方互換性) this.increaseIndent(); this.emitChildren(node); this.decreaseIndent(); } // Output ENDIF (don't output for ELSE IF statements) if (!node.text.startsWith('ELSE IF')) { this.emitLine('ENDIF'); } } /** * ELSE文の出力 * 構造化されたconsequentフィールドを使用 */ emitElse(node) { // ELSE is at same level as IF, so temporarily reduce current indent this.decreaseIndent(); const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // ELSE文の本体(consequent)の出力 if (node.meta?.consequent) { for (const stmt of node.meta.consequent) { this.emitNode(stmt); } } else { // 従来の子ノード処理(後方互換性) this.emitChildren(node); } } /** * ELSE IF文の出力 */ emitElseIf(node) { // ELSE IF is at same level as IF, so temporarily reduce current indent this.decreaseIndent(); const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); this.emitChildren(node); } /** * ENDIF文の出力 */ emitEndif(node) { const text = this.formatText(node.text); this.emitLine(text); } /** * FOR文の出力 */ emitFor(node) { const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // Process child nodes, but output NEXT at original indent level for (const child of node.children) { if (child.kind === 'statement' && child.text.trim().startsWith('NEXT')) { this.decreaseIndent(); this.emitNode(child); this.increaseIndent(); } else { this.emitNode(child); } } this.decreaseIndent(); } /** * WHILE文の出力 */ emitWhile(node) { const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // Process child nodes, but output ENDWHILE at original indent level for (const child of node.children) { if (child.kind === 'endwhile') { this.decreaseIndent(); this.emitNode(child); this.increaseIndent(); } else { this.emitNode(child); } } this.decreaseIndent(); } /** * ENDWHILE文の出力 */ emitEndwhile(node) { const text = this.formatText(node.text); this.emitLine(text); this.emitChildren(node); } /** * REPEAT文の出力 */ emitRepeat(node) { const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // Process child nodes, but output UNTIL at original indent level for (const child of node.children) { if (child.kind === 'until') { this.decreaseIndent(); this.emitNode(child); this.increaseIndent(); } else { this.emitNode(child); } } this.decreaseIndent(); } /** * UNTIL文の出力 */ emitUntil(node) { const text = this.formatText(node.text); this.emitLine(text); this.emitChildren(node); } /** * プロシージャの出力 */ emitProcedure(node) { if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // Process child nodes, but output ENDPROCEDURE/ENDFUNCTION at original indent level for (const child of node.children) { if (child.kind === 'statement' && (child.text.trim() === 'ENDPROCEDURE' || child.text.trim() === 'ENDFUNCTION')) { this.decreaseIndent(); this.emitNode(child); return; // Exit after outputting ENDPROCEDURE } else { this.emitNode(child); } } this.decreaseIndent(); if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } } /** * FUNCTION文の出力 */ emitFunction(node) { if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); // Process child nodes, but output ENDPROCEDURE/ENDFUNCTION at original indent level for (const child of node.children) { if (child.kind === 'statement' && (child.text.trim() === 'ENDPROCEDURE' || child.text.trim() === 'ENDFUNCTION')) { this.decreaseIndent(); this.emitNode(child); return; // Exit after outputting ENDFUNCTION } else { this.emitNode(child); } } this.decreaseIndent(); if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } } /** * RETURN文の出力 */ emitReturn(node) { // Output RETURN statement directly without quotes (don't use formatText) this.emitLine(node.text); this.emitChildren(node); } /** * 配列宣言の出力 */ emitArray(node) { const text = this.formatText(node.text); this.emitLine(text); this.emitChildren(node); } /** * TYPE定義の出力 */ emitType(node) { if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); this.emitChildren(node); this.decreaseIndent(); // ENDTYPEを元のインデントレベルで出力 this.emitLine('ENDTYPE'); if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } } /** * CLASS定義の出力 */ emitClass(node) { if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); this.emitChildren(node); this.decreaseIndent(); if (this.context.formatter.insertBlankLines) { this.emitBlankLine(); } } /** * CASE文の出力 */ emitCase(node) { const text = this.formatText(node.text); this.emitLine(text); this.increaseIndent(); this.emitChildren(node); this.decreaseIndent(); } /** * 式の出力 */ emitExpression(node) { const text = this.formatText(node.text); // 長い式の場合は折り返し if (this.context.formatter.wrapLongLines && this.options.maxLineLength) { const wrappedLines = this.wrapLongLine(text, this.options.maxLineLength); if (wrappedLines.length > 1) { for (let i = 0; i < wrappedLines.length; i++) { if (i === 0) { this.emitLine(wrappedLines[i]); } else { this.emitLine(' ' + wrappedLines[i]); // 継続行のインデント } } } else { this.emitLine(text); } } else { this.emitLine(text); } this.emitChildren(node); } /** * 文の出力 */ emitStatement(node) { if (node.text.trim()) { // RETURN文の場合は引用符で囲まずに直接出力 if (node.text.includes('RETURN')) { this.emitLine(node.text); } else { const text = this.formatText(node.text); this.emitLine(text); } } else { // Process child nodes only when text is empty this.emitChildren(node); } } /** * 複合文の出力 */ emitCompound(node) { // 複合文は子ノードをそのまま順次出力 for (const child of node.children) { this.emitNode(child); } } /** * ヘッダーコメントの追加 */ addHeader(title, author, date) { if (!this.options.includeComments) return; this.emitComment('// =========================================='); this.emitComment(`// ${title}`); if (author) { this.emitComment(`// Author: ${author}`); } if (date) { this.emitComment(`// Date: ${date}`); } else { this.emitComment(`// Date: ${new Date().toLocaleDateString()}`); } this.emitComment('// Generated by python2igcse'); this.emitComment('// =========================================='); this.emitBlankLine(); } /** * フッターコメントの追加 */ addFooter() { if (!this.options.includeComments) return; this.emitBlankLine(); this.emitComment('// =========================================='); this.emitComment('// End of program'); this.emitComment('// =========================================='); } } exports.TextEmitter = TextEmitter; //# sourceMappingURL=text-emitter.js.map