python2igcse
Version:
Convert Python code to IGCSE Pseudocode format
536 lines • 16.5 kB
JavaScript
"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