1

私は作っている言語のために、Pythonに似たインタプリタを作っています。今私はこれは小さな仕事ではないと私はそれが非常にうまく動作するとは思っていないか、多くのことを理解していますが、私はいくつかの基本的な機能(変数、関数、ループ、if文など...)インタプリタを構築する:ASTを設計する

私は現在、通訳者がファイルを受け取り、それをトークンのリストに分割し、これらのトークンをASTに変える準備ができています。私は理解していると信じている再帰的降下パーサーでこれを行うつもりですが、ここに問題があります。さんがそう

2 * 3 = 6 

はその後、さらには私が知っている

1 + 6 = 7 

後に行われる最初に行われるBIDMASに乗算を使用しているので、私は、以下の入力これは希望

1 + 2 * 3 

出力7を持っているとしましょう私は単純な文法を持っているので、この順序をどうやって得るのですか?これをASTとしてどのように保存するのか分かりません。答えのために物事を単純化するために、これはあなたが受け取るだろう唯一の入力で、文法が

program = add 
add = mul {"+" mul} 
mul = NUM {"*" NUM} 

ことができると仮定することができますだから基本的に、どのようにASTを格納するデータ構造体(複数可)を作るのですか?

P.S. >https://en.wikipedia.org/wiki/Shunting-yard_algorithm

psudocodeがあまりにもそこにある - 私は「分流ヤード」アルゴリズムを使用したいC.

+0

あなたはAho SetiとUllmanからコンパイラの "ドラゴン"の本を買うべきです...そしてバイソンと呼ばれるツールは、あなたが "手で"それをするのに費やした時間のほんの一部であなたの意図を達成するのを助けます – Leo

+0

残念なことに私はこのプロジェクトの目的は "手で"すべてを行うことですので、バイソンを使用することはできません。とにかくありがとう –

答えて

4

免責条項:この表現は主観的です照らすことを意味しました。

基本的に、ASTは、それぞれのASTノードが「左」と「右」の両方のポインタを保持する「C」構造のバイナリツリーのように構築されます。 ASTの他の要素は、通常、文脈に敏感です。たとえば、変数宣言と関数定義または関数内の式などです。あなたが引用たとえば、ラフ木はこれを反映します:だから

+ 
/ \ 
1  * 
     /\ 
    2 3 

を1 +(2 * 3)あなたのASTの構築物は同様であろうとするために、上記のノードを代入して:

    ----------------- 
       | type: ADDOP | 
       | left: struct* | 
       | right: struct*| 
       ----------------- 
      /     \ 
      /     \ 
(ADDOP left points to)   (ADDOP right points to) 
------------------------  -------------------------- 
| type: NUMLITERAL  |  | type: MULTOP   | 
| value: 1    |  | left: struct*   | 
| left: struct* (null) |  | right: struct*   | 
| right: struct*(null) |  -------------------------- 
------------------------   /    \ 
            /    \ 

        (MULTOP left points to)   (MULTOP right points to) 
        ------------------------  -------------------------- 
        | type: NUMLITERAL  |  | type: NUMLITERAL  | 
        | value: 2    |  | value: 3    | 
        | left: struct* (null) |  | left: struct* (null) | 
        | right: struct*(null) |  | right: struct* (null) | 
        ------------------------  -------------------------- 

I "C"について十分に知っていて、ノードをmallocにして、左/右ポインタを割り当てると仮定します。

残りのアクティビティは、式の評価と結果の生成、またはコンパイル結果に合わせた適切な中間コード/マシンコードの発行のために、ツリーのポストオーダートラバーサルを行うことです。どちらの選択肢でも、あなたの大量の思考と計画をもたらすことができます。

BTW:前述のように、ASTノードは通常、表現したい抽象化のレベルに基づいて属性を持つことになります。また、典型的なコンパイラは、異なる理由で複数のASTを活用する可能性があることにも注意してください。うん、もっと読んだり、勉強したり。

注:これはASTのデータ構造を示していますが、@mikeb答えは、このような構造のノードに文字列 "1 + 2 * 3"を取得する方法については固いです。

1

でこれをやっています。

FTA:コンピュータサイエンスの

、操車場アルゴリズムは中置記法で指定された数式を解析するための方法です。逆ポーランド記法(RPN)または抽象構文木(AST)として知られている接尾辞表記文字列を生成するために使用できます。このアルゴリズムは、Edsger Dijkstraによって考案され、その操作が鉄道シャントヤードの操作に似ているため、「シャントヤード」アルゴリズムと呼ばれています。 Dijkstraは、Mathematisch CentrumレポートMR 34/61のShunting Yard Algorithmについて最初に説明しました。

RPNの評価と同様に、シャントヤードアルゴリズムはスタックベースです。中立式は、「3 + 4」や「3 + 4 *(2-1)」など、ほとんどの人が慣れている数式表記の形式です。変換には、2つのテキスト変数(文字列)、入力と出力があります。まだ出力キューに追加されていない演算子を保持するスタックもあります。変換するには、プログラムは各シンボルを順番に読み込み、そのシンボルに基づいて何かを実行します。上記の例の結果は、 "3 4 +"または "3 4 2 1 - * +"となります。

shunting-yardアルゴリズムは後ほど演算子優先解析に一般化されています。

コード、それはこれがそれを格納する方法(とあなたがCを好きではない場合 - http://rosettacode.org/wiki/Parsing/Shunting-yard_algorithmからお好きなところをお選びください)ではないことが指摘されて以来:

#include <sys/types.h> 
#include <regex.h> 
#include <stdio.h> 

typedef struct { 
    const char *s; 
    int len, prec, assoc; 
} str_tok_t; 

typedef struct { 
    const char * str; 
    int assoc, prec; 
    regex_t re; 
} pat_t; 

enum assoc { A_NONE, A_L, A_R }; 
pat_t pat_eos = {"", A_NONE, 0}; 

pat_t pat_ops[] = { 
    {"^\\)", A_NONE, -1}, 
    {"^\\*\\*", A_R, 3}, 
    {"^\\^", A_R, 3}, 
    {"^\\*", A_L, 2}, 
    {"^/",  A_L, 2}, 
    {"^\\+", A_L, 1}, 
    {"^-",  A_L, 1}, 
    {0} 
}; 

pat_t pat_arg[] = { 
    {"^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"}, 
    {"^[a-zA-Z_][a-zA-Z_0-9]*"}, 
    {"^\\(", A_L, -1}, 
    {0} 
}; 

str_tok_t stack[256]; /* assume these are big enough */ 
str_tok_t queue[256]; 
int l_queue, l_stack; 
#define qpush(x) queue[l_queue++] = x 
#define spush(x) stack[l_stack++] = x 
#define spop() stack[--l_stack] 

void display(const char *s) 
{ 
    int i; 
    printf("\033[1;1H\033[JText | %s", s); 
    printf("\nStack| "); 
    for (i = 0; i < l_stack; i++) 
     printf("%.*s ", stack[i].len, stack[i].s); // uses C99 format strings 
    printf("\nQueue| "); 
    for (i = 0; i < l_queue; i++) 
     printf("%.*s ", queue[i].len, queue[i].s); 
    puts("\n\n<press enter>"); 
    getchar(); 
} 

int prec_booster; 

#define fail(s1, s2) {fprintf(stderr, "[Error %s] %s\n", s1, s2); return 0;} 

int init(void) 
{ 
    int i; 
    pat_t *p; 

    for (i = 0, p = pat_ops; p[i].str; i++) 
     if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED)) 
      fail("comp", p[i].str); 

    for (i = 0, p = pat_arg; p[i].str; i++) 
     if (regcomp(&(p[i].re), p[i].str, REG_NEWLINE|REG_EXTENDED)) 
      fail("comp", p[i].str); 

    return 1; 
} 

pat_t* match(const char *s, pat_t *p, str_tok_t * t, const char **e) 
{ 
    int i; 
    regmatch_t m; 

    while (*s == ' ') s++; 
    *e = s; 

    if (!*s) return &pat_eos; 

    for (i = 0; p[i].str; i++) { 
     if (regexec(&(p[i].re), s, 1, &m, REG_NOTEOL)) 
      continue; 
     t->s = s; 
     *e = s + (t->len = m.rm_eo - m.rm_so); 
     return p + i; 
    } 
    return 0; 
} 

int parse(const char *s) { 
    pat_t *p; 
    str_tok_t *t, tok; 

    prec_booster = l_queue = 0; 
    display(s); 
    while (*s) { 
     p = match(s, pat_arg, &tok, &s); 
     if (!p || p == &pat_eos) fail("parse arg", s); 

     /* Odd logic here. Don't actually stack the parens: don't need to. */ 
     if (p->prec == -1) { 
      prec_booster += 100; 
      continue; 
     } 
     qpush(tok); 
     display(s); 

re_op:  p = match(s, pat_ops, &tok, &s); 
     if (!p) fail("parse op", s); 

     tok.assoc = p->assoc; 
     tok.prec = p->prec; 

     if (p->prec > 0) 
      tok.prec = p->prec + prec_booster; 
     else if (p->prec == -1) { 
      if (prec_booster < 100) 
       fail("unmatched)", s); 
      tok.prec = prec_booster; 
     } 

     while (l_stack) { 
      t = stack + l_stack - 1; 
      if (!(t->prec == tok.prec && t->assoc == A_L) 
        && t->prec <= tok.prec) 
       break; 
      qpush(spop()); 
      display(s); 
     } 

     if (p->prec == -1) { 
      prec_booster -= 100; 
      goto re_op; 
     } 

     if (!p->prec) { 
      display(s); 
      if (prec_booster) 
       fail("unmatched (", s); 
      return 1; 
     } 

     spush(tok); 
     display(s); 
    } 

    return 1; 
} 

int main() 
{ 
    int i; 
    const char *tests[] = { 
     "3 + 4 * 2/(1 - 5)^2^3", /* RC mandated: OK */ 
     "123",     /* OK */ 
     "3+4 * 2/(1 - 5)^2^3.14", /* OK */ 
     "(((((((1+2+3**(4 + 5))))))",  /* bad parens */ 
     "a^(b + c/d * .1e5)!",   /* unknown op */ 
     "(1**2)**3",    /* OK */ 
     0 
    }; 

    if (!init()) return 1; 
    for (i = 0; tests[i]; i++) { 
     printf("Testing string `%s' <enter>\n", tests[i]); 
     getchar(); 

     printf("string `%s': %s\n\n", tests[i], 
      parse(tests[i]) ? "Ok" : "Error"); 
    } 

    return 0; 
} 
+4

これは、ASTを格納する方法ではなく、 "解決する"方法です –

関連する問題