С Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 1509, средняя оценка - 4.80
#pragma
Временно недоступен
954 / 225 / 6
Регистрация: 12.04.2009
Сообщений: 921
#1

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

20.06.2009, 20:03. Просмотров 193942. Ответов 464
Метки нет (Все метки)

*****************
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
Makefile: как с использованием gcc строить автоматические зависимости от .h файлов?
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
Можно ли выразить порядковый номер элемента массива через индексы?
[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;
30
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
20.06.2009, 20:03
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Пишем свой интерпретатор языка BASIC (C++):

Пишем свой чекер - C++
Я хочу написать свой чекер, но не знаю с чего начать? Кто знает основные принцип работы чекеров прошу объясните.

пишем свой троян с нуля - C++
Всем привет)))соглашусь, что изобретаю велосипед, но хочется сделать все своими ручками не прибегая к open source и т.п. для повышения...

Пишем свой класс, спецификатор доступа protected - C++
Всем привет! Из книги Р. Лафоре относительно спецификатора доступа protected: Далее пишется следующее: Возникает вопросы:...

Не удается откомпилировать интерпретатор М-языка - C++
Задача: взять интерпретатор М-языка на сайте http://cmcmsu.no-ip.info/2course/model.lang.parser.sample.htm и переработать его, добавив в...

Интерпретатор небольшого языка программирования на С++ - C++
Здравствуйте, уважаемые форумчане! Я тут где-то год назад прочитал тему Evg и #pragma о создании интерпретатора, меня эта тема очень...

Написать Интерпретатор Программного Языка(собственного) - C++
Здраствуйте! Кто знает C++ помогите пожалуйста с реализацией данного задания!!! Пожалуйста, очень надо. сроки поджимают. Есть...

464
Evg
Эксперт CАвтор FAQ
18381 / 6429 / 441
Регистрация: 30.03.2009
Сообщений: 17,845
Записей в блоге: 28
16.09.2009, 16:33 #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", что неверно
1
#pragma
Временно недоступен
954 / 225 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 17:29  [ТС] #122
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 12 минут
Ну и вообще я так с ходу не совсем пойму, как GOTO внутри IF'а отработает. По логике вещей при окончании списка мы должны вернуться из процедуры interp_Run
Только вот как? GOTO же требует,чтобы выполнялся оператор за меткой,так вроде логично что это уже на плечах программиста .Как можно вернуться из инструкции,если управление передаётся опять на IF?Что-то не пойму .


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

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

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

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

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

в общем надо по ним читать мануал
1
Evg
Эксперт CАвтор FAQ
18381 / 6429 / 441
Регистрация: 30.03.2009
Сообщений: 17,845
Записей в блоге: 28
17.09.2009, 18:36 #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'
1
Evg
Эксперт CАвтор FAQ
18381 / 6429 / 441
Регистрация: 30.03.2009
Сообщений: 17,845
Записей в блоге: 28
18.09.2009, 14:42 #135
revision 21 на оригинальном исходнике ломается "file: source.bas line: 23 error: Unexpected symbol". Там в конце строки символ '\r'. Его надо уметь пропускать
0
18.09.2009, 14:42
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
18.09.2009, 14:42
Привет! Вот еще темы с ответами:

Написать интерпретатор программного языка -помощь - C++
Здраствуйте! Ребят, кто хорошо разбирается в C++ помогите пожалуйста с реализацией данного задания или хотя бы подтолкните к решению,...

Интерпретатор/компилятор ассемблер-подобного языка - C++
Привет! Чую, что изобрёл велисипед, даже скорее велопарк, но всё же, поделюсь: Некоторое время назад начал изучать кресты, в целом...

Интерпретатор музыки стандарта BASIC PLAY на С++ - C++
У кого нибудь есть функция или класс, который сможет воспроизводить в С++ напрямую музыкальные строки, записанные в стандарте оператора...

Задание: разработать "Интерпретатор языка". С чего начать? - C++
Здравствуйте, вручили темку на курсовик, ну точнее как вручили, не успел взять то, что хотел - пришлось брать то, что осталось. Плоховато...


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

Или воспользуйтесь поиском по форуму:
135
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.