前言
網頁最初只是靜態文件的載體,如今已發展為最廣泛使用的跨平台運行環境之一。許多軟體專案也因此提供將程式碼轉譯為 JavaScript 的工具,以便搭上網頁技術的發展潮流。
本文將介紹一個可將 Pascal 程式轉換為 JavaScript 的工具——pas2js。透過這類工具,或許可以延續甚至拓展既有 Pascal 程式的生命週期。
注意事項
雖然 pas2js 目前仍持續開發與維護,但整體而言仍偏向「可用但不成熟」的階段,尚未達到廣泛實務應用的程度。主要原因在於相關文件相當稀少,除了官方文件外,幾乎沒有其他可參考的資源。實務上若遇到問題,往往只能透過閱讀原始碼或有限文件來解決,開發效率較低。
此外,轉換後的 JavaScript 程式碼會在 JavaScript 執行環境(例如瀏覽器或 Node.js)中運行,而非原本的 Pascal 執行環境。因此,仍需對轉換後的程式進行測試,以確認其行為符合預期。
除了本文介紹的 pas2js 之外,網路上也可以找到其他類似的專案。不過,多數並非由 Free Pascal 官方團隊維護,使用時需自行評估其可靠性。
pas2js 的工作原理
pas2js 本質上是一個將 Pascal 轉譯為 JavaScript 的工具。它會掃描輸入的 Pascal 原始碼,並轉換為等效的 JavaScript 程式碼。產生的 JavaScript 程式可在瀏覽器或 Node.js 環境中執行。
需要注意的是,pas2js 的輸入為 Pascal 原始碼,無法直接處理編譯後的二進位檔案。此外,pas2js 專案也提供了以原始碼形式存在的小型執行環境(runtime)與部分函式庫(例如 jQuery),供轉譯後程式使用。
安裝 pas2js
在 Windows 上安裝
至 pas2js 官方下載點下載 Windows 版本,解壓縮後放置於任意目錄,例如 C:\ProgramData。
以 C:\ProgramData 為例,將 C:\ProgramData\pas2js-windows-1.4.8\bin 加入 PATH 環境變數後,即可在命令列中直接使用 pas2js。
在 macOS 上安裝
至 pas2js 官方下載點下載 macOS 版本,解壓縮後放置於任意目錄,例如 /opt 或 $HOME/opt。
筆者實測發現,macOS 版本的 pas2js 有時無法正確讀取其執行檔所在位置的設定檔。解決方式是將 path/to/pas2js-macos-1.4.8/bin/pas2js.cfg 複製至 $HOME/.pas2js.cfg(注意檔名前的 .)。
可參考以下範例來設置:
#
# Minimal config file for pas2js compiler
#
# -d is the same as #DEFINE
# -u is the same as #UNDEF
#
# Write always a nice logo ;)
-l
# Display Warnings, Notes and Hints
-vwnh
# If you don't want so much verbosity use
#-vw
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/chartjs
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/dataabstract
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/fcl-base
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/fcl-db
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/fpcunit
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/jspdf
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/nodejs
-Fu/Users/user/opt/pas2js-macos-1.4.18/packages/rtl
#IFDEF nodejs
-Jirtl.js
#ENDIF
# Put all generated JavaScript into one file
-Jc
# end.
經測試,直接在 pas2js.cfg 中使用絕對路徑會較為簡單。由於此設定檔僅供個人環境使用,通常不需考慮跨平台問題。
在 GNU/Linux 上安裝
至 pas2js 官方下載點下載 GNU/Linux 版本,解壓縮後放置於任意目錄,例如 /opt 或 $HOME/opt。
以 $HOME/opt 為例,將 $HOME/opt/pas2js-linux-1.4.8/bin 加入 PATH,即可在命令列中使用 pas2js。
撰寫第一隻程式
Hello World
用編輯器新增 hello.pas 檔案,在該檔案輸入以下內容:
program main;
begin
WriteLn('Hello World');
end.
基本上,就是 Pascal 版本的 Hello World 程式。
將 Pascal 程式碼轉為網頁前端程式
用 pas2js 將寫好的 Hello World 程式轉為適用於瀏覽器的 JavaScript 程式碼:
$ pas2js -Jc -Jirtl.js hello.pas
-Jc 表示將生成的 JavaScript 程式碼串接成一個大檔案。-Jirtl.js 表示將 pas2js 附帶的小型運行環境一起包進去。
撰寫一個空 (dummy) 頁面來載入轉譯出來的 hello.js 命令稿:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="hello.js">
<script>
rtl.run();
</script>
</body>
</html>
載入該網頁時,應該可以在開發者主控台 (developer console) 看到 Hello World 字串。
若不想把運行期函式庫 (rtl.js) 包進 JavaScript 命令稿,就要自行將 pas2js 專案所提供的 rtl.js 命令稿載入網頁中,否則轉換的 JavaScript 命令稿會無法運作。 rtl.js 命令稿位於 pas2js-platform-version/package/rtl 目錄中。
將 Pascal 程式碼轉為 Node.js 程式
在預設情形下,pas2js 轉出來的 JavaScript 命令稿適用於瀏覽器。不過,pas2js 也可轉出適用於 Node.js 的 JavaScript 命令稿。將指令修改如下:
$ pas2js -Jc -Jirtl.js -Tnodejs hello.pas
-Tnodejs 代表將目標環境設為 Node.js。
使用 Node.js 提供的直譯器即可執行轉譯出來的命令稿:
$ node hello.js
Hello World
Pascal 和其等效的 JavaScript 程式碼
pas2js 的官方文件展示了 pas2js 將 Pascal 程式碼轉出的等效 JavaScript 程式碼。由於目前 pas2js 的教學甚少,該份文件應該是目前學習 pas2js 最重要的資源。
pas2js 不支援的特性
受限於異質運行環境,pas2js 不支援以下特性(未全部列出):
- 記憶體管理
class的解構子- 指標運算
- 運算子重載
- 泛型程式
object型態variant型態
其他項目請看 pas2js 的官方文件。
在 Pascal 中使用 JavaScript 程式碼
pas2js 轉換出來的命令稿會在 JavaScript 運行環境跑,和現有的 JavaScript 程式碼能夠互動是相當重要的。著眼於這個議題,pas2js 加入了一些原本在 Pascal 中沒有的語法。
在主程式中塞入 JavaScript 程式碼
由於 pas2js 的目的就是將 Pascal 程式碼轉 JavaScript 程式碼,但必要時仍然可以用 asm 區塊在 Pascal 程式碼中直接插入 JavaScript 程式碼。參考以下的實例:
program main; (* 1 *)
var (* 2 *)
s : string; (* 3 *)
procedure Print(s: string); (* 4 *)
begin (* 5 *)
WriteLn(s); (* 6 *)
end; (* 7 *)
begin (* 8 *)
s := 'Hello World'; (* 9 *)
(* Raw JavaScript code. *) (* 10 *)
asm /* 11 */
console.log('Print from JavaScript: ' + $mod.s); /* 12 */
$mod.Print('Print from Pascal: ' + $mod.s); /* 13 */
end; (* 14 *)
end. (* 15 *)
本程式的關鍵之處在第 11 行至第 14 行。在這段程式碼中,我們使用 asm 區塊置入 JavaScript 程式碼。
asm 區塊的工作原理是 pas2js 會自動忽略 asm 區塊內的程式碼,因此區塊內的內容會原封不動地輸出。我們可以在其中撰寫任意 JavaScript 程式碼。
在這個例子中,變數 s 未在 Pascal 程式碼中直接使用。預設情形下,pas2js 為了效能考量,會移除未使用的宣告。為避免變數被移除,需要關閉最佳化:
$ pas2js -Jc -Jirtl.js -O- -Tnodejs -omain.js source.pas
其中,-O- 表示停用程式碼最佳化。
用 JavaScript 實作函式和程序
既然 pas2js 允許在主程式中插入 JavaScript,也可以用 JavaScript 來實作 Pascal 的函式與程序。方式是在宣告後加上 assembler 修飾詞:
program main; (* 1 *)
var (* 2 *)
s : string; (* 3 *)
function Greet(const s: string): string; assembler; (* 4 *)
asm /* 5 */
return 'Hello ' + s; /* 6 */
end; (* 7 *)
procedure Print(const s: string); assembler; (* 8 *)
asm /* 9 */
console.log(s); /* 10 */
end; (* 11 *)
begin (* 12 *)
s := 'Hello World'; (* 13 *)
Print(Greet('World')); (* 14 *)
end. (* 15 *)
在第 4 行至第 7 行間,我們以 JavaScript 實作函式;第 8 行至第 11 行則實作程序。
同樣地,由於使用了變數,需要關閉最佳化:
$ pas2js -Jc -Jirtl.js -O- -Tnodejs -omain.js source.pas
使用現有的 JavaScript 函式
我們也可以直接使用 JavaScript 的函式:
program main; (* 1 *)
uses (* 2 *)
SysUtils; (* 3 *)
function Abs(n: real): real; external name 'Math.abs'; (* 4 *)
procedure Log(const s: string); external name 'console.log'; (* 5 *)
begin (* 6 *)
Log(FloatToStr(Abs(-3))); (* 7 *)
end. (* 8 *)
此處的 Abs 對應 JavaScript 的 Math.abs,Log 則對應 console.log。
在函式中使用不定參數
JavaScript 可透過 arguments 物件實作不定參數函式,pas2js 亦支援此功能:
program main; (* 1 *)
uses JS; (* 2 *)
function Sum(): real; varargs; (* 3 *)
var (* 4 *)
i : integer; (* 5 *)
begin (* 6 *)
result := 0.0; (* 7 *)
for i := 0 to JSArguments.length-1 do (* 8 *)
result := result + real(JSArguments[i]); (* 9 *)
end; (* 10 *)
begin (* 11 *)
WriteLn(Sum(1, 2, 3, 4, 5)); (* 12 *)
end. (* 13 *)
使用 JavaScript 的變數或常數
program main; (* 1 *)
const (* 2 *)
E: Double; external name 'Math.E'; (* 3 *)
PI: Double; external name 'Math.PI'; (* 4 *)
begin (* 5 *)
WriteLn(E); (* 6 *)
WriteLn(PI); (* 7 *)
end. (* 8 *)
使用 JavaScript 類別
program main; (* 1 *)
{$modeswitch externalclass} (* 2 *)
type (* 3 *)
TJSDate = class external name 'Date' (* 4 *)
private (* 5 *)
function getFullYear(): NativeInt; (* 6 *)
procedure setFullYear(const value: NativeInt); (* 7 *)
public (* 8 *)
constructor New(); (* 9 *)
constructor New(const ms: NativeInt); (* 10 *)
class function Now: NativeInt; (* 11 *)
property Year: NativeInt read getFullYear write setFullYear; (* 12 *)
end; (* 13 *)
var (* 14 *)
d: TJSDate; (* 15 *)
begin (* 16 *)
d := TJSDate.New; (* 17 *)
WriteLn(d.Year); (* 18 *)
d.Year := d.Year + 1; (* 19 *)
WriteLn(d.Year); (* 20 *)
end. (* 21 *)
結語
本文介紹了如何使用 pas2js 將 Pascal 程式轉譯為 JavaScript。若要熟練運用 pas2js,仍需同時具備 Pascal 與 JavaScript 的基礎,實務上不一定會比直接撰寫原生 JavaScript 更簡單。
不過,pas2js 的價值在於讓既有 Pascal 程式能夠移植至網頁平台,進而延續其生命週期。從這個角度來看,這或許才是 pas2js 最具意義的用途。
補充
距離本文撰寫時間已有一段時間(2020 → 2026),從現在的角度來看,pas2js 的定位與使用情境也可以有一些補充說明。
工具成熟度與社群現況
截至 2026 年,pas2js 仍然維持在「特定用途可用,但不屬於主流工具」的位置。其開發與維護並未中斷,但整體生態系與文件資源仍然相對有限。實務上,開發者在使用時,依然需要具備自行閱讀原始碼與解決問題的能力。
相較之下,JavaScript/TypeScript 生態系已更加成熟,各類框架(如前端 UI 框架、後端 Node.js 架構)也已形成穩定的開發模式。因此,若是從零開始開發新專案,直接使用 JavaScript 或 TypeScript 通常會是更高效的選擇。
pas2js 的實際適用場景
從實務角度來看,pas2js 較適合以下幾種情境:
- 既有 Pascal 專案的延伸
- 跨平台需求(以 Pascal 為主體)
- 教育或技術研究用途
相對地,若是以下情境,則較不建議使用 pas2js:
- 從零開始開發大型前端應用
- 高度依賴現代前端框架(React、Vue、Angular 等)
- 團隊中缺乏 Pascal 經驗
與現代工具鏈的落差
現代前端開發流程通常會搭配:
- 模組打包工具(Webpack、Vite 等)
- 型別系統(TypeScript)
- 套件管理(npm / pnpm / yarn)
- UI 框架與元件系統
pas2js 雖然可以產生 JavaScript,但與上述工具鏈的整合程度有限,實務上通常需要額外調整。
替代方案的思考
若目標是讓非 JavaScript 語言在瀏覽器執行,可以考慮:
- WebAssembly(Wasm)
- 直接改寫為 TypeScript
- 將 Pascal 程式保留於後端
總結
從 2026 年的角度來看,pas2js 並不是用來取代 JavaScript 的工具,而是一個讓 Pascal 程式進入網頁世界的橋接方案。
若你的目標是延續既有資產,pas2js 仍有價值;但若是建立現代前端應用,建議優先考慮主流技術堆疊。