Форум программистов, компьютерный форум CyberForum.ru

Пишем свой интерпретатор языка BASIC - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 1509, средняя оценка - 4.80
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
20.06.2009, 20:03     Пишем свой интерпретатор языка BASIC #1
*****************
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
Makefile: как с использованием gcc строить автоматические зависимости от .h файлов?
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
[C/C++] Можно ли выразить порядковый номер элемента массива через индексы?
[C++] Какие флаги указать линкеру для компиляции программы?
Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile?
Странный SIGSEGV, или что зависит от порядка написания интерфейса класса
[C++]Можно ли как-то указать в Makefile,чтобы часть файлов компилировал компилятор C?
Альтернативная версия интерпретатора от Evg на C
Это простая реализация разбора выражений, написанная Evg на C:
Представление выражения в двоичном дереве
*****************
Первое сообщение:
*****************
Задание(Страуструп,из книги,по готовому коду): Введите программу калькулятора и заставьте её работать.Например,при вводе
C++
1
2
r = 2.5
area = pi*r*r
Программа калькулятора выведет:
C++
1
2
2.5
19.635
Получили такой код:
LexicalAnalyzer.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// LexicalAnalyzer.h
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
 
enum Token_value {
    NAME,       NUMBER,      END,
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')'
};
extern Token_value curr_tok;
extern std::map<std::string,double>table;
extern int no_of_errors;
 
Token_value get_token();
 
double expr(bool);
double term (bool);
double prim (bool);
int error(const std::string&);
 
#endif // LEXICALANALYZER_H_INCLUDED

LexicalAnalyzer.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// LexicalAnalyzer.cpp
#include "LexicalAnalyzer.h"
 
 
std::map<std::string,double>table;
Token_value curr_tok=PRINT;
 
double expr (bool get)
{
    double left = term(get);
 
    for (;;)
        switch (curr_tok) {
            case PLUS:
                 left += term(true);
            break;
            case MINUS:
                 left-= term(true);
            break;
            default:
                 return left;
        }
}
 
double term (bool get)
{
    double left = prim (get);
 
    for (;;)
        switch (curr_tok) {
            case MUL:
                 left*=prim(true);
            break;
            case DIV:
                 if (double d = prim (true)) {
                     left /= prim (true);
                     break;
                 }
                 return error("Деление на ноль");
            default:
                 return left;
        }
}
 
double number_value;
std::string string_value;
 
double prim (bool get)
{
    if (get) get_token();
    switch (curr_tok){
        case NUMBER:{
            double& v = number_value;
            get_token();
            return v;
        }
        case NAME:{
            double& v = table[string_value];
            if (get_token()==ASSIGN) v = expr(true);
            return v;
        }
        case MINUS:
            return -prim(true);
        case LP:{
            double e = expr(true);
            if (curr_tok!=RP) return error("Ожидалась )");
            get_token();
            return e;
        }
        default:
            return error("Ожидалось первичное выражение");
    }
}
 
Token_value get_token()
{
    char ch = 0;
 
    do {
        if (!std::cin.get(ch)) return curr_tok = END;
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
             return curr_tok = END;
        case ';':case '\n':
             return curr_tok = PRINT;
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':case '.':
             std::cin.putback(ch);
             std::cin>>number_value;
             return curr_tok=NUMBER;
        default:
             if (isalpha(ch)) {
                 string_value = ch;
                 while (std::cin.get(ch)&&isalnum(ch)) string_value.push_back(ch);
                 std::cin.putback(ch);
                 return curr_tok = NAME;
             }
             error ("Неправильная лексема");
             return curr_tok = PRINT;
    }
}
int no_of_errors=0;
int error (const std::string& s)
{
    no_of_errors++;
    std::cerr<<"Ошибка: "<<s<<'\n';
    return no_of_errors;
}

main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// main.cpp
#include "LexicalAnalyzer.h"
 
 
int main()
{
    table["pi"]=3.1415926535897932385;
    table["e"]=2.7182818284590452354;
    while (std::cin) {
        get_token();
        if (curr_tok==END) break;
        if (curr_tok==PRINT) continue;
        std::cout<<expr(false)<<'\n';
    }
    return no_of_errors;
}

Анализатор-то работает,но конечное значение не вычисляется.Более того,если вводим
C++
1
a = 3 + 6
,то получаем "a", равное первому элементу в выражении,то есть 3.В чём логическая ошибка данной программы?С этими каскадными вызовами она слегка запутана.Уверен,что кто-то уже делал это задание.

Добавлено через 2 часа 5 минут 30 секунд
Пришлось решать влоб с дебаггером.У Страуструпа опечатка (или намеренная ошибка,что более вероятно ) Вот в этом куске кода в функции get_token():
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
Нехватает смены значения curr_tok,что и приводит к ошибочной работе.
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return curr_tok=Token_value(ch);
Теперь всё пашет,всем спасибо,вопрос можно считать закрытым,но есть вопрос поважнее: В функциях prim и term возвращается int при ошибке,но ведь они имеют тип double,как вообще это работает?Происходит неявное преобразование типа,так?Мне интересно,почему Страуструп прибег к такому способу,это распространённая практика?

Добавлено через 16 минут 19 секунд
И ещё опечатка была
C++
1
2
3
                 if (double d = prim (true)) {
                     left /= d;// было left /= prim (true)
                     break;
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
16.09.2009, 16:33     Пишем свой интерпретатор языка BASIC #121
Насколько я понял, в момент интерпретации IF'а для одной из его веток ты вызываешь interp_Run.
Доходишь до метки. Далее делаешь переход на метку. Опять попадаешь в interp_stmtIF. В итоге получается бесконечная рекурсия

Добавлено через 12 минут
Ну и вообще я так с ходу не совсем пойму, как GOTO внутри IF'а отработает. По логике вещей при окончании списка мы должны вернуться из процедуры interp_Run

Код
   LET B1 = 5
   IF B1 THEN GOTO 2
   PRINT B1+1
2:
   PRINT B1+2
Для такого теста у тебя печатается "67", что неверно
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 17:29  [ТС]     Пишем свой интерпретатор языка BASIC #122
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 12 минут
Ну и вообще я так с ходу не совсем пойму, как GOTO внутри IF'а отработает. По логике вещей при окончании списка мы должны вернуться из процедуры interp_Run
Только вот как? GOTO же требует,чтобы выполнялся оператор за меткой,так вроде логично что это уже на плечах программиста .Как можно вернуться из инструкции,если управление передаётся опять на IF?Что-то не пойму .


А,я понял что ты имел ввиду походу выход там есть просто в interp_Run когда вызывается из IF -а подставляется ЛОКАЛЬНЫЙ список statement-ов,а statement из THEN всего один(только GOTO в нашем случае),и после этого происходит возврат из IF.Ну вроде так
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
16.09.2009, 17:32     Пишем свой интерпретатор языка BASIC #123
Цитата Сообщение от #pragma Посмотреть сообщение
Только вот как? GOTO же требует,чтобы выполнялся оператор за меткой,так вроде логично что это уже на плечах программиста .Как можно вернуться из инструкции,если управление передаётся опять на IF?Что-то не пойму .
Я просто не могу чётко сформулировать свою мысль, т.к. в деталях не знаю, как у тебя работает. Попробуй для начала вышеобозначенные ошибки исправить, может самому станет понятно
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 19:28  [ТС]     Пишем свой интерпретатор языка BASIC #124
Я кажись понял я ту ошибку исправил,но и правда с GOTO ошибка есть.Происходит это потому,что GOTO меняет значение счётчика,но это работает только для списка инструкций,по которому идёт интерпретация.Поэтому с IF получается,что происходит попытка проинтерпретировать инструкцию в списке,которой там нет(так как у THEN только одна инструкция,а именно GOTO).Отсюда и ошибка.
Я вот думаю,как можно это поправить "малой кровью" =) Может,сделать чтобы все интерпретирующие функции возвращали порядковые номера в глобальном списке,по умолчанию свои,а если идёт передача управления(как у GOTO),то номер инструкции в списке,которая должна интерпретироваться следующей..
В-общем у меня ощущение,что в целом направление правильное(отдельные списки для WHILE и IF,проверка синтаксиса в них),но я не уверен.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
16.09.2009, 20:14     Пишем свой интерпретатор языка BASIC #125
Тут беда не только в том, что нет номера в списке, а ещё и в том, то у тебя возникает рекурсия. Т.е. после GOTO ты по сути дела начинаешь по новой интерпретировать самый верхний список операторов (поскольку метка в начале программы). Но этот список ты уже интерпретируешь внутри второй активации RunList - т.е. когда ты попадаешь в операцию присваивания и в этом месте смотришь стек интерпретатора, то получается, что с каждым попадением в эту точку стек у тебя растёт. Либо я как-то не так представляю, как у тебя работает, но на первый взгляд получается именно так
Nikita2009
 Аватар для Nikita2009
103 / 102 / 6
Регистрация: 12.09.2009
Сообщений: 453
16.09.2009, 21:07     Пишем свой интерпретатор языка BASIC #126
Цитата Сообщение от #pragma Посмотреть сообщение
Задание(Страуструп,из книги,по готовому коду)
А что за книга???
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 22:37  [ТС]     Пишем свой интерпретатор языка BASIC #127
Книга называется "Язык программирования С++" Б.Страуструп.Специальное издание.
Но по сути,исходники программы,получившейся в итоге,довольно далеки от книжной версии.
Nikita2009
 Аватар для Nikita2009
103 / 102 / 6
Регистрация: 12.09.2009
Сообщений: 453
16.09.2009, 22:46     Пишем свой интерпретатор языка BASIC #128
На сколько далеки???
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 22:55  [ТС]     Пишем свой интерпретатор языка BASIC #129
Ну возьми книгу и сравни с исходниками последней версии тут http://basin.svn.sourceforge.net/
По сути-это совешенно другая программа,и общего между ними - только то,что способ рекурсивного спуска при разборе выражений - тот же.
Nikita2009
 Аватар для Nikita2009
103 / 102 / 6
Регистрация: 12.09.2009
Сообщений: 453
16.09.2009, 23:12     Пишем свой интерпретатор языка BASIC #130
Ага, спасибо...

Добавлено через 15 минут
А о такой книге слышал что нибудь ??

Харви Дейтл, Пол Дейтл "Как програмировать на C++"

Говорят что хорошая...
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
17.09.2009, 00:40  [ТС]     Пишем свой интерпретатор языка BASIC #131
Цитата Сообщение от Nikita2009 Посмотреть сообщение
Ага, спасибо...
Добавлено через 15 минут
А о такой книге слышал что нибудь ??
Харви Дейтл, Пол Дейтл "Как програмировать на C++"
Говорят что хорошая...
Я слышал плохое ...
Цитата Сообщение от Evg Посмотреть сообщение
Тут беда не только в том, что нет номера в списке, а ещё и в том, то у тебя возникает рекурсия. Т.е. после GOTO ты по сути дела начинаешь по новой интерпретировать самый верхний список операторов (поскольку метка в начале программы). Но этот список ты уже интерпретируешь внутри второй активации RunList - т.е. когда ты попадаешь в операцию присваивания и в этом месте смотришь стек интерпретатора, то получается, что с каждым попадением в эту точку стек у тебя растёт. Либо я как-то не так представляю, как у тебя работает, но на первый взгляд получается именно так
Я попытаюсь понять как это исправить ... Но в общем я вижу это не как рекурсию,но склонен доверять тебе,постараюсь понять..
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
17.09.2009, 08:10     Пишем свой интерпретатор языка BASIC #132
Цитата Сообщение от #pragma Посмотреть сообщение
Я попытаюсь понять как это исправить ... Но в общем я вижу это не как рекурсию,но склонен доверять тебе,постараюсь понять..
Давай тогда добейся того, чтобы для начала эти примеры заработали, а там видно будет. Просто это одно из моих предположений, которое я надумал, пока тебе объяснял принцип построения промежуточного представления
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
17.09.2009, 13:03     Пишем свой интерпретатор языка BASIC #133
Цитата Сообщение от #pragma Посмотреть сообщение
Как мне это сделать вообще с правами,сначала все файлы удалить из репов,а потом опять закачать с новыми правами?В репозиторий руками же не залезешь.
по svn help печатаются команды

propdel (pdel, pd)
propedit (pedit, pe)
propget (pget, pg)
proplist (plist, pl)
propset (pset, ps)

в общем надо по ним читать мануал
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
17.09.2009, 18:36     Пишем свой интерпретатор языка BASIC #134
У меня gcc-4.2.1
Ревизия 21 ломается на компиляции (линковке)

Код
syntax.o: In function `syntax_IdentRValue()':
syntax.cpp:(.text+0x309f): undefined reference to `defined_arrays'
syntax.cpp:(.text+0x30aa): undefined reference to `FindNameOverVector(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Array*, std::allocator<Array*> >&)'
syntax.o: In function `syntax_StmtLET(std::vector<Statement*, std::allocator<Statement*> >*)':
syntax.cpp:(.text+0x387c): undefined reference to `defined_arrays'
syntax.cpp:(.text+0x3887): undefined reference to `FindNameOverVector(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Array*, std::allocator<Array*> >&)'
syntax.o: In function `syntax_stmtDIM(std::vector<Statement*, std::allocator<Statement*> >*)':
syntax.cpp:(.text+0x3f0d): undefined reference to `defined_arrays'
syntax.cpp:(.text+0x3f18): undefined reference to `FindNameOverVector(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Array*, std::allocator<Array*> >&)'
syntax.cpp:(.text+0x4159): undefined reference to `Array::Array(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Types)'
syntax.cpp:(.text+0x42e7): undefined reference to `defined_arrays'
interp.o: In function `interp_stmtDIM(Statement const*)':
interp.cpp:(.text+0x33c0): undefined reference to `defined_arrays'
interp.cpp:(.text+0x33cb): undefined reference to `FindNameOverVector(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<Array*, std::allocator<Array*> >&)'
interp.cpp:(.text+0x344e): undefined reference to `defined_arrays'
interp.cpp:(.text+0x3461): undefined reference to `Array::SetDimensions(std::vector<unsigned long, std::allocator<unsigned long> >*)'
interp.cpp:(.text+0x346f): undefined reference to `defined_arrays'
interp.cpp:(.text+0x347e): undefined reference to `Array::ArrZeroInit()'
interp.cpp:(.text+0x348c): undefined reference to `defined_arrays'
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
18.09.2009, 14:42     Пишем свой интерпретатор языка BASIC #135
revision 21 на оригинальном исходнике ломается "file: source.bas line: 23 error: Unexpected symbol". Там в конце строки символ '\r'. Его надо уметь пропускать
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
18.09.2009, 16:54  [ТС]     Пишем свой интерпретатор языка BASIC #136
Я первый раз слышу про такой символ.Что за символ и как пропускать?
UPD.А всё я прочитал надо просто в парсере добавить его.Просто интересно почему у тебя ломается,а у меня работало ...
Насчёт GOTO я подумал,что всё таки лучше будет наверное работать по схеме,когда при интерпретации функция возвращает указатель на следующую инструкцию,я вот только не знаю как это сделать с IF THEN ELSE,в случае если есть ветка ELSE и нужно в конце ветки THEN указать,какой оператор следующий,как это определить на этапе синтаксиса.Что-то у меня каша в голове постоянно
Или может завести какую-то глобальную переменную,показывающую номер текущей инструкции,чтобы проверялась каждый раз при интерпретации,и если есть GOTO,то в эту переменную пишется номер нужной инструкции в списке?Я сегодня точно посмотрю,как точно должна отрабатывать схема
PureBasic
1
2
3
4
5
6
7
8
9
10
LET A = 8
IF A THEN
 PRINT A
 1:
  PRINT "AAAA"
ELSE
 PRINT "DFDFD"
 GOTO 1
 PRINT "HHHH"
END IF
(Как только будет доступ к компу с виндой) И уже потом надо решить,что там перелопачивать и как.

Добавлено через 19 минут
Как вообще туда попал этот символ '\r',вот что интересно..
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
18.09.2009, 17:10     Пишем свой интерпретатор языка BASIC #137
Ну просто у всех нормальных людей энетр кодируется одним символом "\n". А в DOS/Windows - двумя "\n\r" (или в обратном порядке, точно не помню). При открытии файла в текстовом режиме fopen (<name>, "r") эта пара символов на уровне системной библиотеки возвращается как один символ "\n", при записи в файл (и печати), соотвественно, наоборот. При окрытии файла в бинарном виде (т.е. в fopen подаётся "rb" вместо "r") такого читерства не делается. Как это в Си++ поддерживается - хз. Как этот символ попал в файл - скорее всего что-то правил под виндами

> Насчёт GOTO я подумал,что всё таки лучше будет наверное работать по схеме,когда при интерпретации функция возвращает указатель на следующую инструкцию

Я так и делал - см. процедуру interp_InterpStatementBRANCH и место её вызова

> я вот только не знаю как это сделать с IF THEN ELSE

А потому я с этой помойкой и не заморачивался. Ввёл три операции управления: метка, условный переход и безусловный переход. И все языковые операторы управления (IF, WHILE, FOR) реализовал через эти три операции промежуточного представления - см. syntax_StatementIF. Если имеешь хоть какое-то понятие о том, как работает процессор на уровне системы команд или зоть как-то писал программы на ассемблере, то поймёшь, что здесь точно такая же логика - вся программа выстроена в виде цепочки операций, некоторые из которых являются операциями перехода (условного или безусловного)
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
18.09.2009, 17:29  [ТС]     Пишем свой интерпретатор языка BASIC #138
Как этот символ правильно пропускать и,может,есть ещё какие-то поводные камни в этом направлении?Всякие там управляющие символы,последовательности и прочее?Что-то у меня не выходит,поскольку в потоке нет перевода строки,как то так получается,что слово PRINT в этом исходнике
PureBasic
1
2
3
4
5
6
   ' Считаем сумму чисел от 1 до 100
   LET COUNT=0
   LET SUM=0
   PRINT SUM
 
   PRINT COUNT
(\r как раз перед последним PRINT-ом) читается ещё на этапе печати первого PRINT-а,следующего до него,хота в парсере я пробовал добавить строку
C++
1
       parser_ResSign[  '\r'  ] = parser_TokenType(TOKEN_EOL);
Но это не помогло ..
И в том то и дело,что я под виндами вообще не сижу .. вот и не знаю,как и когда \r туда мог попасть

Добавлено через 15 минут
Я нашел как поправить.. но вопрос про всякие левые символы в силе.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
18.09.2009, 17:45     Пишем свой интерпретатор языка BASIC #139
Мой интерпретатор метки пока не поддерживает, но они неявно появляются в if'е. Грубо говоря, когда ты в структурном виде пишешь программу

PureBasic
1
2
3
4
5
6
7
IF A==B THEN
  A=1
  B=2
ELSE
  A=100
  B=200
ENDIF
то на представлении в виде цепочки получается аналог вот такой штуки:

C
1
2
3
4
5
6
7
8
9
if (a==b) then goto L1; else goto L2; // операция условного перехода, параметры - условное выражение и две метки
L1: // операция метка
a=1;
b=2;
goto L3; // операция безусловного перехода
L2:
a=100;
b=200;
L3:
Т.е. при интерпретации такого представления всё делается в "единой плоскости" (т.е. стек вызова при интерпретации кажой операции имеет одну и ту же глубину). При этом ты имеешь один-единственный список операций. И моя версия интерпретатора в процессеработы печатает именно такое промежуточное представление (и вообще можно визуально понять, что в нём находится)

Т.е. если для моей версии написать исходник

PureBasic
1
2
3
4
5
6
7
8
LET A = 5
IF A < 6 THEN
  LET A = 1
  LET B = 2
ELSE
  LET A = 100
  LET B = 200
END_IF
то напечатается вот такое промежуточное представление

Код
s1. LET        A = 5
s5. CBRANCH    (A < 6) -> t=s2(#then), f=s3(#else)   <-- переход по true на операцию s2, по false на s3

s2. LABEL      #then        <--- аналог метки L1
s6. LET        A = 1
s7. LET        B = 2
s8. BRANCH     -> s4(#finish)  <---- безусловный переход на операцию s4

s3. LABEL      #else        <--- аналог метки L2
s9. LET        A = 100
s10. LET       B = 200

s4. LABEL      #finish        <--- аналог метки L3
А для твоего исходника

PureBasic
1
2
3
4
5
6
7
8
9
10
LET A = 8
IF A THEN
 PRINT A
 1:
  PRINT "AAAA"
ELSE
 PRINT "DFDFD"
 GOTO 1
 PRINT "HHHH"
END IF
построилось бы представление, аналогом которого является код

C
1
2
3
4
5
6
7
8
9
10
11
if (a) then goto L1; else goto L3;
L1: // метка начала альтернативы then
print a;
L2: // языковая метка 1:
print "AAAA";
goto L4;
L3: // метка начала альтернативы else
printf "DFDFD";
goto L2;
print "HHHH";
L4:
тут я тебе пояснил, как сделано у меня, чтобы было понятно. Мне такое решение показалось наиболее простым, но опять-таки - я НЕ навязываю его тебе. Делай так, как тебе более понятно. Но вопросы как-то более подробно задавай. Твой интерпретатор уже по коду довольно большой и я не могу уже в деталях понимать его работу, не копаясь глубоко в исходнике. А потому нераскрытый вопрос понимаю с трудом и могу понять неправильно

Добавлено через 6 минут
Цитата Сообщение от #pragma Посмотреть сообщение
И в том то и дело,что я под виндами вообще не сижу .. вот и не знаю,как и когда \r туда мог попасть
Теоретически я мог под виндами что-то подправить в своём исходнике и выложить на форум, а ты уже скопировал оттуда

Цитата Сообщение от #pragma Посмотреть сообщение
Я нашел как поправить.. но вопрос про всякие левые символы в силе.
Сложно сказать. Я просто на все неизвестные символы выругиваюсь и всё. У меня есть вызов isspace, который проверяет на всякие пробельные символы, туда входит табуляция и возможно ещё что-то, что меня мало заботит, т.к. интефейс для того и писали, чтобы не разбираться в этом (\r туда возможно тоже попал). Файл я открываю как "rb", а потому с энтером он схлопнуться не должен
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
22.09.2009, 22:48     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

Пишем свой чекер C++
Не удается откомпилировать интерпретатор М-языка C++
Пишем свой класс, спецификатор доступа protected C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16821 / 5242 / 318
Регистрация: 30.03.2009
Сообщений: 14,118
Записей в блоге: 26
22.09.2009, 22:48     Пишем свой интерпретатор языка BASIC #140
Появилось немного времени, сделал у себя поддержку меток и GOTO. Писал для своей реализации хитрожопый тест. Попробовал на твоей версии (с адаптацией под твой синтаксис) - заработало. Можешь тест добавить к себе в набор (не пропадать же ему)

PureBasic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
' Переход назад
    LET A=1
10: IF A<3 THEN LET A=A+1: GOTO 10
    PRINT A: PRINT ' expected 3
 
' Переход вперёд
    LET A=1
    WHILE A<10
      IF A>3 THEN GOTO 20
      LET A=A+1
    WEND
20: PRINT A: PRINT ' expected 4
 
' Несколько переходов вперёд на одну метку (30)
    LET A=1
    LET B=0
    WHILE A<10
      LET A=A+1
      IF A<3 THEN GOTO 30
      IF A>8 THEN GOTO 30
      GOTO 40
30:   LET B=B+A
40: WEND
    PRINT B: PRINT ' expected 21
Правда у тебя есть косячок. Если сделать переход на метку, но метку не определять, то у тебя падает в кору.

И твоя и моя версия страдают недостатком выдачи диагностики - временами без поллитры не разберёшь, что оно хочет. Современные умные компиляторы позволяют более точно жиагностировать ошибку - они указывают не только номер файла и строки, но ещё и номер позиции в строке. Понятное дело, что сейчас это вообще не критично, но это ещё одна вещь, которая нужна для хорошего программного продукта - нормальный диалог с пользователем. Просто для порядка имей в виду

Я так понимаю, что тестовый пример в репозитории отражает все поддерживаемые конструкции?

Добавлено через 2 минуты
Смотрю ты концепцию условных переходов переделал. Ты просто содрал или всё-таки разобрался, чем плох старый вариант?

Добавлено через 11 минут
В общем больше не получилось подобрать примеры, чтобы интерпретатор ломался или работал неправильно. В исходниках копаться уже совсем тяжело, так что пока прими поздравления: то, что уже есть, с виду работает так как надо
Yandex
Объявления
22.09.2009, 22:48     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

Текущее время: 23:36. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru