プログラミング
POP-2000、共通語としてのPOP-2方言
Pop-2000, a lingua-franca POP-2 dialect (hitogata.neocities.org)
要約
この記事では、1970年代のプログラミング言語POP-2の新しい方言であるPOP-2000について紹介しています。POP-2000は、Uxnなどの小さなシステムや「タイニーコンパイラ」に適した、シンプルで実装しやすい「共通語」となることを目指しています。言語設計の目標として、実装の容易さ、使いやすさ、最小限の構文と機能、低レベル制御、そして小さなシステムでのホスト可能性が挙げられています。
全文翻訳
POP-2000、共通語としてのPOP-2方言 2026年8月24日執筆 Uxnで有名なDevineと私は、1970年代の言語で、最初のLisp方言と同じサークルで使われていたPOP-2について多く話してきました。Devineは、Uxnシステムをブートストラップするのを助けるために、高水準言語でUxnの新しいアセンブラを書くことを検討していました。一方、私は個人的に「タイニーコンパイラ」に適した言語にずっと興味があり、これらの小さなシステムすべて間の共通語として適しているものとしてPOP-2を見つけました。
タイニーコンパイラ(または、なぜ私が連結型プログラミングをそれほど気にしないのか)
少しクレイジーなタイトルだとわかっています。誤解しないでください、私は連結型プログラミングパラダイムが大好きです。その新しい関数/構造を並置と合成によって形成する方法は本当にエレガントです。しかし、私がokのために書いた論文で明確にしようとしたのは、それらの機能のために連結型言語に特に惹かれているわけではないということです。むしろ、連結型言語が好きなのは、それらが解釈器/コンパイラを書くのが非常に簡単であり、(しばしば、常にではないが)ある程度の低レベル制御を可能にするからです。少なくとも、CやOdinのような私が遊ぶのが好きな他の言語と比較してです。元々コンピュータプログラミングに触れる前にコンラング(たくさんのコンラング)を作っていた人間からすると、ソフトウェアを作るというよりも言語開発側の方を重視しています。具体的には、プログラミング言語のアイデアに取り組むとき、通常は以下の目標を念頭に置いていました。
与えられたシステムで解釈器/コンパイラを実装するのが可能な限り容易であること。理想的には、解析やコード生成のために外部ツール/ライブラリをほとんど使用しないこと。
かなり「使いやすい」こと(非常に曖昧で主観的ですが、実質的には「エソラングのように見えない」こと、「見たときにわかる」という説明に基づいています)。
最小限の構文。
最小限の機能セット。基本的に、コンパイラ/解釈器をブートストラップするために必要な最小限のものだけ(これは言語が動作し始めたらすぐに目標となるべきです)。
ある程度の低レベル制御/「システムプログラミング」のサポート。少なくとも、その言語でオペレーティングシステムを書くことが可能である程度。
小さなシステムでホスト可能であること(通常、私の最小期待値はokのレベル、つまり約16MiBのメモリに余裕があることですが、Uxnや6502のようなものならさらに良いでしょう)。
連結型言語、そして一般的に小さな/低レベルのスタックベースVMをターゲットとする言語は、これらの制約によく適合します。連結というパラダイムが本質的に優れているから連結型言語が好きなのではなく、連結がプログラミング言語を作成するという実装問題を喜ばしく小さくしてくれるからです。この利点は、これらのタイプのシステムを支持する古典的な議論(つまり、パーマコンピューティングなど)に自然に広がります。
POP-2について
私が先に概説した言語設計の目標と、元のPOP-2論文で概説された目標を比較すると、それらは基本的に同一です。DevineのPOP-2の記事は、Uxnをターゲットに書かれたPOP-2方言の良い概要を示していますが、要約すると、最小限のPOP-2実装は、vars x y z;のように複数の変数を一度に宣言すること、function myfunc a b;(関数の本体とそれに続くend)のように関数を宣言すること、基本的なif/elseif/else制御フロー、=>によるI/O、およびラベルとgotoによるループをサポートします。POP-2の興味深い点は、値が基盤となるスタックを操作することです。Devine自身の記事から:
function sum x y;
x + y;
end
sum(5, 6) * 2;
これは次のように簡単に書くことができます。sumへの引数はスタックにプッシュされるだけで、sumによって返される値もスタックにプッシュされるからです。そして、+のような演算子に遭遇すると、式の次の要素を先読みし、それをスタックにプッシュし、その要素と次の要素をスタックからポップして加算します。
function sum x y;
x;
+ y;
end
5;
6;
sum();
* 2;
この本質的にスタックベースの性質により、基本的なメモリ管理、異なるシステム用のさまざまな幅の値のサポート、およびより普遍的なI/Oシステムを追加するだけで、多数の小さなシステムで実行できる、便利で初心者向けの言語になります。それらのシステムの既存言語の代替としてではなく、学習しやすく多数の小さなシステムで実行できる言語で初心者が始められるようにする社会的演習として、また、これらの小さなシステムの作成者である私たち自身がコンパイラを実装するのに十分簡単な言語としてです。
POP-2000、控えめな提案
POP-2000(またはPOP2K、まだ名前に完全に固まっていません)は、小さなシステムで望むシステムレベルのサポートを追加することを目標としたPOP-2の標準化された方言であり、同時に非常に明確に定義されています(POP-2論文の参照セクションを詳しく見ると、文法は定義されていますが、理解するのは非常に困難です)。私は過去数日間、Devineと(「絶えずDevineをしつこくからかう」というよりは)連結型Discordサーバーで、どの機能がサポートされるべきか、または追加されるべきかについて、文法、構文などについて話し合いながら、ほぼノンストップで取り組んできました。すぐに詳細をオンラインで公開する予定です。これもまた、1週間前は単なる「もしも」でした。私はすでにEBNF表記で書かれた言語の文法をほぼ完成させました。すぐに「標準」を含むGitリポジトリを作成します(ICEとの悪い無視と関係を考えると、GitHubで行わないことを検討しています)。それが作成され次第、この記事に追加します。それまでの間、作業中のものについて書こうと思いました。また、C言語で言語コンパイラの参照実装のようなものを書きたいとも思っています。当初はokをターゲットにする予定なので、すぐに私たちの小さなVMが高水準言語を持つことになります!
POP-2000 EBNF文法(作業中)
今のところ、私が持っているEBNF表記の完全な文法は次のとおりです(将来変更される可能性があります):
program = { element } ;
element = function | vars | imperative ;
function = "function" , identifier , [ params ] , ";" , [ function_body ] , "end" ;
const = "const"
vars = "vars" , var , { var } , ";" ;
var = identifier , [ ":" , ( integer | string ) ] ;
imperative = if | ( statement_sequence , ";" ) ;
if = "if" , expression , "then" , [ body ] , { "elseif" , expression , "then" , [ body ] } , [ "else" , [ body ] ] , "close" ;
body = { imperative | vars } , [ statement_sequence ] ;
statement_sequence = statement , { ";" , statement } ;
statement = goto | labeled_statement | expression_list ;
(* "return" は関数本体内でのみ許可されるべきです。パーサーがこれを強制する必要があります *)
goto = "goto" , identifier | "return" ;
labeled_statement = label , { imperative } ;
label = identifier , ":" ;
expression_list = expression , { "," , expression } ;
expression = io ;
io = "=>" , primary | assign ;
assign = "->" , primary | apply ;
apply = "<>" , primary | shift ;
shift = ( "<<" | ">>" ) , primary | comparison ;
comparison = ( "<" | ">" | "=" | "!" ) , primary | term ;
term = ( "+" | "-" ) , primary | factor ;
factor = [ "*" | "/" | "%" ] , primary ;
primary = call | reference | quotation | grouping | index | identifier | literal ;
call = identifier , "(" , [ expression_list ] , ")" ;
reference = "#" , identifier ;
quotation = "@" , identifier ;
grouping = "(" , expression , ")" ;
index = "{" , expression , "}" ;
identifier = ( letter | "_" ) , { letter | digit | "_" } ;
literal = integer | hexadecimal | string ;
integer = digit , { digit } ;
hexadecimal = "0x" , hex_digit , { hex_digit } ;
string = "\"" , { string_char } , "\"" ;
string_char = ? any printable character except for double-quotes and backslash ? | "\\\"" (* C-style escape for double quotes, with \" *) | "\\" (* C-style escape for backslash *) ;
letter = "A" ... "Z" |