8

スタックベースのプログラミング言語を実装することで、コンピュータープログラミングの知識を広げることに興味があります。pushint 1値1の整数をスタックの一番上にプッシュし、「」のようなラベルを介してフロー制御を行う「」のような関数を使用することを意図しているため、どこから始めればよいかについてアドバイスを求めていますL01: jump L01:

これまでのところ、自分の言語をどのように動作させたいか(リンクしたいのですが、IDEOneがブロックされています)のC#実装を作成しましたが、非常に面倒で最適化が必要です。入力をXMLに変換してから、解析します。私の目標は低水準言語(おそらくC / C ++)に移行することですが、私の問題は、さまざまなデータ型を保持でき、サイズが固定されていないスタックを実装することです。

最終的には、配列と関数も実装したいと思います。さらに、私はより良いレクサーが必要だと思います。そのような単純な言語には、構文解析ツリーが良いアイデアになるのではないかと思います。

アドバイスや批評は大歓迎です。私はまだプログラミングにかなり慣れていないことを考慮してください(私は最近AP CompSci Iを完了しました)。また、オープンソースのスタックベースの言語へのリンクも歓迎します。

これが私が試して解釈/コンパイルしたい基本的なプログラムです(ここで[this is a comment]):

[Hello World!]
pushchar    '\n'
pushstring  "Hello World!"
print
[Count to 5 and then count down!]
pushint     1
setlocal    0
L01:
pushchar    '\n'
getlocal    0
print           [print x + '\n']
getlocal    0
increment
setlocal    0   [x = x + 1]
pushint     5
getlocal    0
lessthan        [x < 5]
iftrue      L01
L02:
pushchar    '\n'
getlocal    0
print           [print x + '\n']
getlocal    0
decrement
setlocal    0   [x = x - 1]
pushint     0
getlocal    0
greaterthan     [x > 0]
iftrue      L02

期待される出力は次のようになります。

Hello World!
1
2
3
4
5
4
3
2
1
4

2 に答える 2

16

Factorなどのスタックベースの言語の構文は次のとおりです。

2 3 + 5 - print

これは、次のCスタイルのコードと同等です。

print(2 + 3 - 5);

スタックベースの言語を使用する利点は、実装が簡単なことです。さらに、ほとんどのスタックベースの言語が使用するように、言語が逆ポーランド記法を使用する場合、言語のフロントエンドに必要なのはレクサーだけです。トークンのストリームをデコードする方法は1つしかないため、トークンを構文解析して構文ツリーにする必要はありません。

作成しようとしているのは、スタックベースのプログラミング言語ではなく、スタックベースの仮想マシンです。アプリケーション仮想マシンは、スタックベースまたはレジスタベースのいずれかになります。たとえば、Java仮想マシンはスタックベースです。Javaバイトコード(作成しているもの-仮想マシンのバイトコード)を実行します。ただし、このバイトコードにコンパイルされるプログラミング言語(Java、Erlang、Groovyなど)はスタックベースではありません。

作成しようとしているのは、スタックベースの仮想マシンのアセンブリレベル言語のようなものです。そうは言っても、そうするのはかなり簡単です-スタックベースの仮想マシンは、そのレジスタベースの仮想マシンを実装する方が簡単です。繰り返しますが、必要なのはflexなどのレクサーだけです。レクサーと呼ばれるライブラリを使用したJavaScriptの小さな例を次に示します。

var program = "[print(2 + 3)]";
program += "\n push 2";
program += "\n push 3";
program += "\n add";
program += "\n print";

lexer.setInput(program);

var token;
var stack = [];
var push = false;

while (token = lexer.lex()) {
    switch (token) {
    case "NUMBER":
        if (push) stack.push(lexer.yytext);
        else alert("Unexpected number.");
        break;
    case "ADD":
        if (push) alert("Expected number.");
        else stack.push(stack.pop() + stack.pop());
        break;
    case "PRINT":
        if (push) alert("Expected number.");
        else alert(stack.pop());
        break;
    }

    push = token === "PUSH";
}
<script src="https://rawgit.com/aaditmshah/lexer/master/lexer.js"></script>
<script>
var lexer = new Lexer;

lexer.addRule(/\s+/, function () {
    // matched whitespace - discard it
});

lexer.addRule(/\[.*\]/, function () {
    // matched a comment - discard it
});

lexer.addRule(/\d+/, function (lexeme) {
    this.yytext = parseInt(lexeme);
    return "NUMBER";
});

lexer.addRule(/push/, function () {
    return "PUSH";
});

lexer.addRule(/add/, function () {
    return "ADD";
});

lexer.addRule(/print/, function () {
    return "PRINT";
});
</script>

とても簡単です。プログラムをいじって、必要に応じて変更することができます。幸運を祈ります。

于 2012-12-13T02:36:20.283 に答える
2

「MetaII」に関する論文は本当に啓発的だと思います。プッシュダウンスタックコンパイラマシンとそのコンパイラを定義する方法を、短いが気が遠くなるような10ページで示しています。この回答を参照してください:https ://stackoverflow.com/a/1005680/120163 これを理解すると、プッシュダウンスタックインタープリターの作成は永遠に簡単になります。

于 2012-12-13T04:05:25.500 に答える