Подтвердить что ты не робот

Как я могу написать переводчика в C?

Мне бы понравились некоторые рекомендации или советы, возможно, электронная книга или две. Я не собираюсь писать компилятор, просто ищу учебник, над которым я мог бы следовать и модифицировать, когда я иду. Спасибо за понимание!

BTW: Это должно быть C.

Было бы полезно получить больше ответов.

4b9b3361

Ответ 1

Отличный способ начать писать переводчик - написать простой машинный симулятор. Здесь простой язык, на который вы можете написать интерпретатор для:

Язык имеет стек и 6 команд:

push <num> # вставьте номер в стек

pop # выскочит первый номер в стеке

add # вытащите верхние 2 элемента в стеке и вставьте их сумму в стек. (помните, что вы можете добавить отрицательные числа, так что вы тоже вычитаете). Вы также можете получить умножение, создавая цикл, используя некоторые другие инструкции с этим.

ifeq <address> # рассмотрите верхнюю часть стека, если он равен 0, продолжите, else, перейдите к <address>, где <address> - номер строки

jump <address> # перейти к номеру строки

print # напечатать значение в верхней части стека

dup # нажмите копию того, что находится в верхней части стека, в стек.

После того, как вы написали программу, которая может принимать эти инструкции и выполнять их, вы по существу создали очень простую стеквую виртуальную машину на основе стека. Поскольку это язык с очень низким уровнем, вам не нужно понимать, что такое AST, как анализировать грамматику в AST и переводить ее на машинный код и т.д. Это слишком сложно для учебного проекта. Начните с этого, и как только вы создали эту маленькую виртуальную машину, вы можете начать думать о том, как вы можете перевести некоторые общие конструкции в эту машину. например вы можете подумать о том, как вы можете перевести инструкцию C if/else или while на этот язык.

Edit:

Из комментариев ниже, похоже, вам нужно немного больше опыта с C, прежде чем вы сможете решить эту задачу.

Я бы предложил сначала изучить следующие темы:

  • scanf, printf, putchar, getchar - основные функции C IO
  • struct - основная структура данных в C
  • malloc - как выделить память, а также разницу между памятью стека и памятью кучи
  • связанные списки - и как реализовать стек, а затем, возможно, двоичное дерево (вам нужно будет сначала понимайте структуры и malloc)

Тогда будет полезно узнать немного больше о библиотеке string.h  - strcmp, strdup - пара полезных функций строки, которые будут полезны.

Короче говоря, C имеет гораздо более высокую кривую обучения по сравнению с python, просто потому, что это язык более низкого уровня, и вы должны управлять своей собственной памятью, поэтому хорошо сначала изучить некоторые основные вещи о C, прежде чем пытаться написать интерпретатор, даже если вы уже знаете, как записать его в python.

Ответ 2

Единственное различие между интерпретатором и компилятором заключается в том, что вместо генерации кода из AST вы выполняете его в VM. Как только вы это понимаете, почти любая книга компилятора, даже Red Dragon Book (первое издание, а не второе!), Достаточно.

Ответ 3

Я вижу, что это немного поздний ответ, однако, поскольку этот поток появился на втором месте в списке результатов, когда я сделал поиск для написания интерпретатора, и никто не упомянул ничего конкретного, я приведу следующий пример

Отказ от ответственности: это просто какой-то простой код, который я написал в спешке, чтобы получить основание для объяснения ниже и, следовательно, не совершенен, но он компилируется и запускается и, кажется, дает ожидаемые ответы.

Прочитайте следующий C-код снизу вверх:

#include <stdio.h>
#include <stdlib.h>

double expression(void);

double vars[26]; // variables

char get(void) { char c = getchar(); return c; } // get one byte
char peek(void) { char c = getchar(); ungetc(c, stdin); return c; } // peek at next byte
double number(void) { double d; scanf("%lf", &d); return d; } // read one double

void expect(char c) { // expect char c from stream
    char d = get();
    if (c != d) {
        fprintf(stderr, "Error: Expected %c but got %c.\n", c, d);
    }
}

double factor(void) { // read a factor
    double f;
    char c = peek();
    if (c == '(') { // an expression inside parantesis?
        expect('(');
        f = expression();
        expect(')');
    } else if (c >= 'A' && c <= 'Z') { // a variable ?
        expect(c);
        f = vars[c - 'A'];
    } else { // or, a number?
        f = number();
    }
    return f;
}

double term(void) { // read a term
    double t = factor();
    while (peek() == '*' || peek() == '/') { // * or / more factors
        char c = get();
        if (c == '*') {
            t = t * factor();
        } else {
            t = t / factor();
        }
    }
    return t;
}

double expression(void) { // read an expression
    double e = term();
    while (peek() == '+' || peek() == '-') { // + or - more terms
        char c = get();
        if (c == '+') {
            e = e + term();
        } else {
            e = e - term();
        }
    }
    return e;
}

double statement(void) { // read a statement
    double ret;
    char c = peek();
    if (c >= 'A' && c <= 'Z') { // variable ?
        expect(c);
        if (peek() == '=') { // assignment ?
            expect('=');
            double val = expression();
            vars[c - 'A'] = val;
            ret = val;
        } else {
            ungetc(c, stdin);
            ret = expression();
        }
    } else {
        ret = expression();
    }
    expect('\n');
    return ret;
}

int main(void) {
    printf("> "); fflush(stdout);

    for (;;) {
        double v = statement();
        printf(" = %lf\n> ", v); fflush(stdout);
    }
    return EXIT_SUCCESS;
}

Это простой рекурсивный десантный парсер для базовых математических выражений, поддерживающих одну буквенную переменную. Запуск и ввод некоторых инструкций дают следующие результаты:

> (1+2)*3
 = 9.000000
> A=1
 = 1.000000
> B=2
 = 2.000000
> C=3
 = 3.000000
> (A+B)*C
 = 9.000000

Вы можете изменить get(), peek() и число() для чтения из файла или списка строк кода. Также вы должны сделать функцию для чтения идентификаторов (в основном слов). Затем вы расширяете функцию statement(), чтобы иметь возможность изменять, какую строку она запускает дальше, чтобы разветвляться. В последнем случае вы добавляете операции ветвления, которые вы хотите использовать в функции оператора, например

if "condition" then 
    "statements" 
else 
    "statements" 
endif. 

while "condition" do
    "statements"
endwhile

function fac(x)
   if x = 0 then
      return 1
   else
      return x*fac(x-1) 
   endif
endfunction

Очевидно, вы можете решить, что синтаксис будет таким, каким вам нравится. Вам нужно подумать о способах определения функций и о том, как обрабатывать аргументы/переменные параметра, локальные переменные и глобальные переменные. Если предпочтительные массивы и структуры данных. Ссылки/указатели. Ввод, вывод? Для обработки рекурсивных вызовов функций вам, вероятно, потребуется использовать стек.

По-моему, было бы проще сделать все это с С++ и STL. Где, например, одна std:: map может использоваться для хранения локальных переменных, а другая карта может использоваться для глобальных...

Конечно, можно написать компилятор, который строит абстрактное синтаксическое дерево из кода. Затем перемещает это дерево, чтобы получить либо машинный код, либо какой-то байтовый код, который выполняется на виртуальной машине (например, Java и .Net). Это дает лучшую производительность, чем наивный анализ, строит линию и выполняет их, но, на мой взгляд, НЕ записывает переводчика. Это пишет как компилятор, так и его целевую виртуальную машину.

Если кто-то хочет научиться писать переводчика, ему следует попробовать сделать простой базовый простой и практичный рабочий интерпретатор.